From 459323a931f6d4e23d813ef6414ea8e4d511dd76 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 21 Apr 2010 19:44:19 +0100 Subject: [PATCH 001/260] Make the detection cone in attachments face in the direction of the avatar, not the relative rotation of the attachment towards the avatar. --- .../Shared/Api/Implementation/Plugins/SensorRepeat.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index 2296379da7..4d7ead6553 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -302,6 +302,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins float dz; Quaternion q = SensePoint.RotationOffset; + if (SensePoint.ParentGroup.RootPart.IsAttachment) + { + // In attachments, the sensor cone always orients with the + // avatar rotation. This may include a nonzero elevation if + // in mouselook. + + ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.RootPart.AttachedAvatar); + q = avatar.Rotation; + } LSL_Types.Quaternion r = new LSL_Types.Quaternion(q.X, q.Y, q.Z, q.W); LSL_Types.Vector3 forward_dir = (new LSL_Types.Vector3(1, 0, 0) * r); double mag_fwd = LSL_Types.Vector3.Mag(forward_dir); From c9da66728a6f6bc1304b3f0ec70bd06c32d064c2 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 22 Apr 2010 07:25:32 -0700 Subject: [PATCH 002/260] Renamed OpenSim.Server.exe to Robust.exe and corresponding .ini's and config. --- bin/{OpenSim.Server.HG.ini.example => Robust.HG.ini.example} | 0 bin/{OpenSim.Server.exe.config => Robust.exe.config} | 0 bin/{OpenSim.Server.ini.example => Robust.ini.example} | 0 prebuild.xml | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename bin/{OpenSim.Server.HG.ini.example => Robust.HG.ini.example} (100%) rename bin/{OpenSim.Server.exe.config => Robust.exe.config} (100%) rename bin/{OpenSim.Server.ini.example => Robust.ini.example} (100%) diff --git a/bin/OpenSim.Server.HG.ini.example b/bin/Robust.HG.ini.example similarity index 100% rename from bin/OpenSim.Server.HG.ini.example rename to bin/Robust.HG.ini.example diff --git a/bin/OpenSim.Server.exe.config b/bin/Robust.exe.config similarity index 100% rename from bin/OpenSim.Server.exe.config rename to bin/Robust.exe.config diff --git a/bin/OpenSim.Server.ini.example b/bin/Robust.ini.example similarity index 100% rename from bin/OpenSim.Server.ini.example rename to bin/Robust.ini.example diff --git a/prebuild.xml b/prebuild.xml index 5cc742c45a..47f0347d3d 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1299,7 +1299,7 @@ - + ../../bin/ From e33209fe5bf2a520d9fcb7fb309d66c8d0000aab Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 22 Apr 2010 08:47:47 -0700 Subject: [PATCH 003/260] Sanitized parsing of floats (x, y, z location) for Culture. --- OpenSim/Services/LLLoginService/LLLoginService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 7b25274b47..c333b5cec0 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -405,9 +405,9 @@ namespace OpenSim.Services.LLLoginService } else { - position = new Vector3(float.Parse(uriMatch.Groups["x"].Value), - float.Parse(uriMatch.Groups["y"].Value), - float.Parse(uriMatch.Groups["z"].Value)); + position = new Vector3(float.Parse(uriMatch.Groups["x"].Value, Culture.NumberFormatInfo), + float.Parse(uriMatch.Groups["y"].Value, Culture.NumberFormatInfo), + float.Parse(uriMatch.Groups["z"].Value, Culture.NumberFormatInfo)); string regionName = uriMatch.Groups["region"].ToString(); if (regionName != null) From ebcc9874d4d285ab2b45d62cc4fe89e9830111b7 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Apr 2010 17:45:03 +0100 Subject: [PATCH 004/260] Insert a ROLLBACK command on migration step failure. This ensures that updating the Migrations table will not occur in a partial transaction, which would be auto-rolled-back later. --- OpenSim/Data/Migration.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 4622e23ded..68e25ef0aa 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -146,6 +146,8 @@ namespace OpenSim.Data { m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", cmd.CommandText); m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); + cmd.CommandText = "ROLLBACK;"; + cmd.ExecuteNonQuery(); } if (version == 0) From 67990ea7e4d768b6574249082224978c22da0af4 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 22 Apr 2010 18:55:31 -0700 Subject: [PATCH 005/260] * Better error logging for failed SimianGrid web service calls --- OpenSim/Framework/WebUtil.cs | 11 ++++++++--- .../SimianGrid/SimianAssetServiceConnector.cs | 13 +++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 2843e202be..94862a6372 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -146,18 +146,23 @@ namespace OpenSim.Framework { using (Stream responseStream = response.GetResponseStream()) { + string responseStr = null; + try { - string responseStr = responseStream.GetStreamString(); + responseStr = responseStream.GetStreamString(); OSD responseOSD = OSDParser.Deserialize(responseStr); if (responseOSD.Type == OSDType.Map) return (OSDMap)responseOSD; else errorMessage = "Response format was invalid."; } - catch + catch (Exception ex) { - errorMessage = "Failed to parse the response."; + if (!String.IsNullOrEmpty(responseStr)) + errorMessage = "Failed to parse the response:\n" + responseStr; + else + errorMessage = "Failed to retrieve the response: " + ex.Message; } } } diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs index 79e49a11b9..3fdee9c8b4 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs @@ -303,9 +303,11 @@ namespace OpenSim.Services.Connectors.SimianGrid HttpWebResponse response = MultipartForm.Post(request, postParameters); using (Stream responseStream = response.GetResponseStream()) { + string responseStr = null; + try { - string responseStr = responseStream.GetStreamString(); + responseStr = responseStream.GetStreamString(); OSD responseOSD = OSDParser.Deserialize(responseStr); if (responseOSD.Type == OSDType.Map) { @@ -317,12 +319,15 @@ namespace OpenSim.Services.Connectors.SimianGrid } else { - errorMessage = "Response format was invalid."; + errorMessage = "Response format was invalid:\n" + responseStr; } } - catch + catch (Exception ex) { - errorMessage = "Failed to parse the response."; + if (!String.IsNullOrEmpty(responseStr)) + errorMessage = "Failed to parse the response:\n" + responseStr; + else + errorMessage = "Failed to retrieve the response: " + ex.Message; } } } From a23bebdc0f106c19de09e54d88eebc6b086fdaab Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 17:17:15 +0100 Subject: [PATCH 006/260] Duplicate OpenSim.Data.SQLite into OpenSim.Data.SQLiteNG. SQLiteNG will shortly be changed to work under mono 2.6 and above --- .../Data/SQLiteNG/Properties/AssemblyInfo.cs | 65 + .../SQLiteNG/Resources/001_AssetStore.sql | 12 + .../Data/SQLiteNG/Resources/001_AuthStore.sql | 18 + .../Data/SQLiteNG/Resources/001_Avatar.sql | 9 + .../SQLiteNG/Resources/001_FriendsStore.sql | 10 + .../SQLiteNG/Resources/001_InventoryStore.sql | 32 + .../SQLiteNG/Resources/001_RegionStore.sql | 144 ++ .../SQLiteNG/Resources/001_UserAccount.sql | 17 + .../Data/SQLiteNG/Resources/001_UserStore.sql | 39 + .../SQLiteNG/Resources/002_AssetStore.sql | 10 + .../Data/SQLiteNG/Resources/002_AuthStore.sql | 5 + .../SQLiteNG/Resources/002_FriendsStore.sql | 5 + .../SQLiteNG/Resources/002_InventoryStore.sql | 8 + .../SQLiteNG/Resources/002_RegionStore.sql | 10 + .../SQLiteNG/Resources/002_UserAccount.sql | 5 + .../Data/SQLiteNG/Resources/002_UserStore.sql | 5 + .../SQLiteNG/Resources/003_AssetStore.sql | 1 + .../SQLiteNG/Resources/003_InventoryStore.sql | 5 + .../SQLiteNG/Resources/003_RegionStore.sql | 5 + .../Data/SQLiteNG/Resources/003_UserStore.sql | 6 + .../SQLiteNG/Resources/004_AssetStore.sql | 7 + .../SQLiteNG/Resources/004_InventoryStore.sql | 36 + .../SQLiteNG/Resources/004_RegionStore.sql | 38 + .../Data/SQLiteNG/Resources/004_UserStore.sql | 6 + .../SQLiteNG/Resources/005_RegionStore.sql | 5 + .../Data/SQLiteNG/Resources/005_UserStore.sql | 5 + .../SQLiteNG/Resources/006_RegionStore.sql | 102 + .../Data/SQLiteNG/Resources/006_UserStore.sql | 20 + .../SQLiteNG/Resources/007_RegionStore.sql | 8 + .../Data/SQLiteNG/Resources/007_UserStore.sql | 7 + .../SQLiteNG/Resources/008_RegionStore.sql | 6 + .../Data/SQLiteNG/Resources/008_UserStore.sql | 5 + .../SQLiteNG/Resources/009_RegionStore.sql | 8 + .../Data/SQLiteNG/Resources/009_UserStore.sql | 11 + .../SQLiteNG/Resources/010_RegionStore.sql | 5 + .../Data/SQLiteNG/Resources/010_UserStore.sql | 37 + .../SQLiteNG/Resources/011_RegionStore.sql | 28 + .../SQLiteNG/Resources/012_RegionStore.sql | 5 + .../SQLiteNG/Resources/013_RegionStore.sql | 6 + .../SQLiteNG/Resources/014_RegionStore.sql | 8 + .../SQLiteNG/Resources/015_RegionStore.sql | 6 + .../SQLiteNG/Resources/016_RegionStore.sql | 5 + .../SQLiteNG/Resources/017_RegionStore.sql | 8 + .../SQLiteNG/Resources/018_RegionStore.sql | 79 + .../Resources/OpenSim.Data.SQLite.addin.xml | 20 + OpenSim/Data/SQLiteNG/SQLiteAssetData.cs | 343 +++ .../Data/SQLiteNG/SQLiteAuthenticationData.cs | 262 ++ OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs | 74 + OpenSim/Data/SQLiteNG/SQLiteEstateData.cs | 387 +++ OpenSim/Data/SQLiteNG/SQLiteFramework.cs | 91 + OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs | 70 + .../SQLiteNG/SQLiteGenericTableHandler.cs | 268 ++ OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs | 898 +++++++ OpenSim/Data/SQLiteNG/SQLiteRegionData.cs | 2264 +++++++++++++++++ .../Data/SQLiteNG/SQLiteUserAccountData.cs | 81 + OpenSim/Data/SQLiteNG/SQLiteUtils.cs | 307 +++ OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs | 155 ++ .../Data/SQLiteNG/Tests/SQLiteAssetTest.cs | 64 + .../Data/SQLiteNG/Tests/SQLiteEstateTest.cs | 65 + .../SQLiteNG/Tests/SQLiteInventoryTest.cs | 66 + .../Data/SQLiteNG/Tests/SQLiteRegionTest.cs | 64 + prebuild.xml | 37 + 62 files changed, 6378 insertions(+) create mode 100644 OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_AssetStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_AuthStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_Avatar.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_FriendsStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_InventoryStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_UserAccount.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/001_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_AssetStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_AuthStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_FriendsStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_InventoryStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_UserAccount.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/002_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/003_AssetStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/003_InventoryStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/003_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/003_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/004_AssetStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/004_InventoryStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/004_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/004_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/005_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/005_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/006_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/006_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/007_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/007_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/008_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/008_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/009_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/009_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/010_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/010_UserStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/011_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/012_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/013_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/014_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/015_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/016_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/017_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/018_RegionStore.sql create mode 100644 OpenSim/Data/SQLiteNG/Resources/OpenSim.Data.SQLite.addin.xml create mode 100644 OpenSim/Data/SQLiteNG/SQLiteAssetData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteEstateData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteFramework.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteRegionData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteUtils.cs create mode 100644 OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs create mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs create mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs create mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs create mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs diff --git a/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs b/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d45ab5092a --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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 OpenSimulator 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("OpenSim.Data.SQLite")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data.SQLite")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("6113d5ce-4547-49f4-9236-0dcc503457b1")] + +// 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 Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.6.5.*")] +[assembly : AssemblyFileVersion("0.6.5.0")] diff --git a/OpenSim/Data/SQLiteNG/Resources/001_AssetStore.sql b/OpenSim/Data/SQLiteNG/Resources/001_AssetStore.sql new file mode 100644 index 0000000000..2e026cad19 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_AssetStore.sql @@ -0,0 +1,12 @@ +BEGIN TRANSACTION; +CREATE TABLE assets( + UUID varchar(255) primary key, + Name varchar(255), + Description varchar(255), + Type integer, + InvType integer, + Local integer, + Temporary integer, + Data blob); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/001_AuthStore.sql b/OpenSim/Data/SQLiteNG/Resources/001_AuthStore.sql new file mode 100644 index 0000000000..468567dcc2 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_AuthStore.sql @@ -0,0 +1,18 @@ +BEGIN TRANSACTION; + +CREATE TABLE auth ( + UUID char(36) NOT NULL, + passwordHash char(32) NOT NULL default '', + passwordSalt char(32) NOT NULL default '', + webLoginKey varchar(255) NOT NULL default '', + accountType VARCHAR(32) NOT NULL DEFAULT 'UserAccount', + PRIMARY KEY (`UUID`) +); + +CREATE TABLE tokens ( + UUID char(36) NOT NULL, + token varchar(255) NOT NULL, + validity datetime NOT NULL +); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/001_Avatar.sql b/OpenSim/Data/SQLiteNG/Resources/001_Avatar.sql new file mode 100644 index 0000000000..7ec906b48a --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_Avatar.sql @@ -0,0 +1,9 @@ +BEGIN TRANSACTION; + +CREATE TABLE Avatars ( + PrincipalID CHAR(36) NOT NULL, + Name VARCHAR(32) NOT NULL, + Value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY(PrincipalID, Name)); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/001_FriendsStore.sql b/OpenSim/Data/SQLiteNG/Resources/001_FriendsStore.sql new file mode 100644 index 0000000000..f1b9ab9902 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_FriendsStore.sql @@ -0,0 +1,10 @@ +BEGIN TRANSACTION; + +CREATE TABLE `Friends` ( + `PrincipalID` CHAR(36) NOT NULL, + `Friend` VARCHAR(255) NOT NULL, + `Flags` VARCHAR(16) NOT NULL DEFAULT 0, + `Offered` VARCHAR(32) NOT NULL DEFAULT 0, + PRIMARY KEY(`PrincipalID`, `Friend`)); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/001_InventoryStore.sql b/OpenSim/Data/SQLiteNG/Resources/001_InventoryStore.sql new file mode 100644 index 0000000000..554d5c2ec8 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_InventoryStore.sql @@ -0,0 +1,32 @@ +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders( + UUID varchar(255) primary key, + name varchar(255), + agentID varchar(255), + parentID varchar(255), + type integer, + version integer); + +CREATE TABLE inventoryitems( + UUID varchar(255) primary key, + assetID varchar(255), + assetType integer, + invType integer, + parentFolderID varchar(255), + avatarID varchar(255), + creatorsID varchar(255), + inventoryName varchar(255), + inventoryDescription varchar(255), + inventoryNextPermissions integer, + inventoryCurrentPermissions integer, + inventoryBasePermissions integer, + inventoryEveryOnePermissions integer, + salePrice integer default 99, + saleType integer default 0, + creationDate integer default 2000, + groupID varchar(255) default '00000000-0000-0000-0000-000000000000', + groupOwned integer default 0, + flags integer default 0); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/001_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/001_RegionStore.sql new file mode 100644 index 0000000000..39e8180cdc --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_RegionStore.sql @@ -0,0 +1,144 @@ +BEGIN TRANSACTION; + +CREATE TABLE prims( + UUID varchar(255) primary key, + RegionUUID varchar(255), + ParentID integer, + CreationDate integer, + Name varchar(255), + SceneGroupID varchar(255), + Text varchar(255), + Description varchar(255), + SitName varchar(255), + TouchName varchar(255), + CreatorID varchar(255), + OwnerID varchar(255), + GroupID varchar(255), + LastOwnerID varchar(255), + OwnerMask integer, + NextOwnerMask integer, + GroupMask integer, + EveryoneMask integer, + BaseMask integer, + PositionX float, + PositionY float, + PositionZ float, + GroupPositionX float, + GroupPositionY float, + GroupPositionZ float, + VelocityX float, + VelocityY float, + VelocityZ float, + AngularVelocityX float, + AngularVelocityY float, + AngularVelocityZ float, + AccelerationX float, + AccelerationY float, + AccelerationZ float, + RotationX float, + RotationY float, + RotationZ float, + RotationW float, + ObjectFlags integer, + SitTargetOffsetX float NOT NULL default 0, + SitTargetOffsetY float NOT NULL default 0, + SitTargetOffsetZ float NOT NULL default 0, + SitTargetOrientW float NOT NULL default 0, + SitTargetOrientX float NOT NULL default 0, + SitTargetOrientY float NOT NULL default 0, + SitTargetOrientZ float NOT NULL default 0); + +CREATE TABLE primshapes( + UUID varchar(255) primary key, + Shape integer, + ScaleX float, + ScaleY float, + ScaleZ float, + PCode integer, + PathBegin integer, + PathEnd integer, + PathScaleX integer, + PathScaleY integer, + PathShearX integer, + PathShearY integer, + PathSkew integer, + PathCurve integer, + PathRadiusOffset integer, + PathRevolutions integer, + PathTaperX integer, + PathTaperY integer, + PathTwist integer, + PathTwistBegin integer, + ProfileBegin integer, + ProfileEnd integer, + ProfileCurve integer, + ProfileHollow integer, + Texture blob, + ExtraParams blob, + State Integer NOT NULL default 0); + +CREATE TABLE primitems( + itemID varchar(255) primary key, + primID varchar(255), + assetID varchar(255), + parentFolderID varchar(255), + invType integer, + assetType integer, + name varchar(255), + description varchar(255), + creationDate integer, + creatorID varchar(255), + ownerID varchar(255), + lastOwnerID varchar(255), + groupID varchar(255), + nextPermissions string, + currentPermissions string, + basePermissions string, + everyonePermissions string, + groupPermissions string); + +CREATE TABLE terrain( + RegionUUID varchar(255), + Revision integer, + Heightfield blob); + +CREATE TABLE land( + UUID varchar(255) primary key, + RegionUUID varchar(255), + LocalLandID string, + Bitmap blob, + Name varchar(255), + Desc varchar(255), + OwnerUUID varchar(255), + IsGroupOwned string, + Area integer, + AuctionID integer, + Category integer, + ClaimDate integer, + ClaimPrice integer, + GroupUUID varchar(255), + SalePrice integer, + LandStatus integer, + LandFlags string, + LandingType string, + MediaAutoScale string, + MediaTextureUUID varchar(255), + MediaURL varchar(255), + MusicURL varchar(255), + PassHours float, + PassPrice string, + SnapshotUUID varchar(255), + UserLocationX float, + UserLocationY float, + UserLocationZ float, + UserLookAtX float, + UserLookAtY float, + UserLookAtZ float, + AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'); + +CREATE TABLE landaccesslist( + LandUUID varchar(255), + AccessUUID varchar(255), + Flags string); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/001_UserAccount.sql b/OpenSim/Data/SQLiteNG/Resources/001_UserAccount.sql new file mode 100644 index 0000000000..c38d9a762f --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_UserAccount.sql @@ -0,0 +1,17 @@ +BEGIN TRANSACTION; + +-- useraccounts table +CREATE TABLE UserAccounts ( + PrincipalID CHAR(36) primary key, + ScopeID CHAR(36) NOT NULL, + FirstName VARCHAR(64) NOT NULL, + LastName VARCHAR(64) NOT NULL, + Email VARCHAR(64), + ServiceURLs TEXT, + Created INT(11), + UserLevel integer NOT NULL DEFAULT 0, + UserFlags integer NOT NULL DEFAULT 0, + UserTitle varchar(64) NOT NULL DEFAULT '' +); + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/001_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/001_UserStore.sql new file mode 100644 index 0000000000..b584594ced --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/001_UserStore.sql @@ -0,0 +1,39 @@ +BEGIN TRANSACTION; + +-- users table +CREATE TABLE users( + UUID varchar(255) primary key, + username varchar(255), + surname varchar(255), + passwordHash varchar(255), + passwordSalt varchar(255), + homeRegionX integer, + homeRegionY integer, + homeLocationX float, + homeLocationY float, + homeLocationZ float, + homeLookAtX float, + homeLookAtY float, + homeLookAtZ float, + created integer, + lastLogin integer, + rootInventoryFolderID varchar(255), + userInventoryURI varchar(255), + userAssetURI varchar(255), + profileCanDoMask integer, + profileWantDoMask integer, + profileAboutText varchar(255), + profileFirstText varchar(255), + profileImage varchar(255), + profileFirstImage varchar(255), + webLoginKey text default '00000000-0000-0000-0000-000000000000'); +-- friends table +CREATE TABLE userfriends( + ownerID varchar(255), + friendID varchar(255), + friendPerms integer, + ownerPerms integer, + datetimestamp integer); + +COMMIT; + diff --git a/OpenSim/Data/SQLiteNG/Resources/002_AssetStore.sql b/OpenSim/Data/SQLiteNG/Resources/002_AssetStore.sql new file mode 100644 index 0000000000..5339b84dfd --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_AssetStore.sql @@ -0,0 +1,10 @@ +BEGIN TRANSACTION; + +CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; +DROP TABLE assets; +CREATE TABLE assets(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; +DROP TABLE assets_backup; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/002_AuthStore.sql b/OpenSim/Data/SQLiteNG/Resources/002_AuthStore.sql new file mode 100644 index 0000000000..3237b68fd6 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_AuthStore.sql @@ -0,0 +1,5 @@ +BEGIN TRANSACTION; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/002_FriendsStore.sql b/OpenSim/Data/SQLiteNG/Resources/002_FriendsStore.sql new file mode 100644 index 0000000000..6733502224 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_FriendsStore.sql @@ -0,0 +1,5 @@ +BEGIN TRANSACTION; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/002_InventoryStore.sql b/OpenSim/Data/SQLiteNG/Resources/002_InventoryStore.sql new file mode 100644 index 0000000000..01951d6582 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_InventoryStore.sql @@ -0,0 +1,8 @@ +BEGIN TRANSACTION; + +create index inventoryfolders_agentid on inventoryfolders(agentid); +create index inventoryfolders_parentid on inventoryfolders(parentid); +create index inventoryitems_parentfolderid on inventoryitems(parentfolderid); +create index inventoryitems_avatarid on inventoryitems(avatarid); + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/002_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/002_RegionStore.sql new file mode 100644 index 0000000000..c5c7c99455 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_RegionStore.sql @@ -0,0 +1,10 @@ +BEGIN TRANSACTION; + +CREATE TABLE regionban( + regionUUID varchar (255), + bannedUUID varchar (255), + bannedIp varchar (255), + bannedIpHostMask varchar (255) + ); + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/002_UserAccount.sql b/OpenSim/Data/SQLiteNG/Resources/002_UserAccount.sql new file mode 100644 index 0000000000..c7a62932ac --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_UserAccount.sql @@ -0,0 +1,5 @@ +BEGIN TRANSACTION; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, surname AS LastName, '' as Email, '' AS ServiceURLs, created as Created FROM users; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/002_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/002_UserStore.sql new file mode 100644 index 0000000000..48fc680b33 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/002_UserStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE users add homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/003_AssetStore.sql b/OpenSim/Data/SQLiteNG/Resources/003_AssetStore.sql new file mode 100644 index 0000000000..f54f8d98a2 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/003_AssetStore.sql @@ -0,0 +1 @@ +DELETE FROM assets WHERE UUID = 'dc4b9f0bd00845c696a401dd947ac621' diff --git a/OpenSim/Data/SQLiteNG/Resources/003_InventoryStore.sql b/OpenSim/Data/SQLiteNG/Resources/003_InventoryStore.sql new file mode 100644 index 0000000000..4c6da91aab --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/003_InventoryStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/003_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/003_RegionStore.sql new file mode 100644 index 0000000000..4db2f7587d --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/003_RegionStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/003_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/003_UserStore.sql new file mode 100644 index 0000000000..6f890eeec1 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/003_UserStore.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/004_AssetStore.sql b/OpenSim/Data/SQLiteNG/Resources/004_AssetStore.sql new file mode 100644 index 0000000000..39421c4434 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/004_AssetStore.sql @@ -0,0 +1,7 @@ +BEGIN; + +update assets + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/004_InventoryStore.sql b/OpenSim/Data/SQLiteNG/Resources/004_InventoryStore.sql new file mode 100644 index 0000000000..e8f4d46333 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/004_InventoryStore.sql @@ -0,0 +1,36 @@ +BEGIN; + +update inventoryitems + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update inventoryitems + set assetID = substr(assetID, 1, 8) || "-" || substr(assetID, 9, 4) || "-" || substr(assetID, 13, 4) || "-" || substr(assetID, 17, 4) || "-" || substr(assetID, 21, 12) + where assetID not like '%-%'; + +update inventoryitems + set parentFolderID = substr(parentFolderID, 1, 8) || "-" || substr(parentFolderID, 9, 4) || "-" || substr(parentFolderID, 13, 4) || "-" || substr(parentFolderID, 17, 4) || "-" || substr(parentFolderID, 21, 12) + where parentFolderID not like '%-%'; + +update inventoryitems + set avatarID = substr(avatarID, 1, 8) || "-" || substr(avatarID, 9, 4) || "-" || substr(avatarID, 13, 4) || "-" || substr(avatarID, 17, 4) || "-" || substr(avatarID, 21, 12) + where avatarID not like '%-%'; + +update inventoryitems + set creatorsID = substr(creatorsID, 1, 8) || "-" || substr(creatorsID, 9, 4) || "-" || substr(creatorsID, 13, 4) || "-" || substr(creatorsID, 17, 4) || "-" || substr(creatorsID, 21, 12) + where creatorsID not like '%-%'; + + +update inventoryfolders + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update inventoryfolders + set agentID = substr(agentID, 1, 8) || "-" || substr(agentID, 9, 4) || "-" || substr(agentID, 13, 4) || "-" || substr(agentID, 17, 4) || "-" || substr(agentID, 21, 12) + where agentID not like '%-%'; + +update inventoryfolders + set parentID = substr(parentID, 1, 8) || "-" || substr(parentID, 9, 4) || "-" || substr(parentID, 13, 4) || "-" || substr(parentID, 17, 4) || "-" || substr(parentID, 21, 12) + where parentID not like '%-%'; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/004_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/004_RegionStore.sql new file mode 100644 index 0000000000..de328cb47a --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/004_RegionStore.sql @@ -0,0 +1,38 @@ +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36)); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/004_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/004_UserStore.sql new file mode 100644 index 0000000000..03142afa37 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/004_UserStore.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/005_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/005_RegionStore.sql new file mode 100644 index 0000000000..1f6d1bd271 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/005_RegionStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +delete from regionsettings; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/005_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/005_UserStore.sql new file mode 100644 index 0000000000..e45c09a493 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/005_UserStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `attachpoint` int(11) NOT NULL DEFAULT 0, `item` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `asset` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/006_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/006_RegionStore.sql new file mode 100644 index 0000000000..94ed8181cd --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/006_RegionStore.sql @@ -0,0 +1,102 @@ +BEGIN TRANSACTION; + +CREATE TABLE estate_groups ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_managers ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_map ( + RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + EstateID int(11) NOT NULL +); + +CREATE TABLE estate_settings ( + EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + EstateName varchar(64) default NULL, + AbuseEmailToEstateOwner tinyint(4) NOT NULL, + DenyAnonymous tinyint(4) NOT NULL, + ResetHomeOnTeleport tinyint(4) NOT NULL, + FixedSun tinyint(4) NOT NULL, + DenyTransacted tinyint(4) NOT NULL, + BlockDwell tinyint(4) NOT NULL, + DenyIdentified tinyint(4) NOT NULL, + AllowVoice tinyint(4) NOT NULL, + UseGlobalTime tinyint(4) NOT NULL, + PricePerMeter int(11) NOT NULL, + TaxFree tinyint(4) NOT NULL, + AllowDirectTeleport tinyint(4) NOT NULL, + RedirectGridX int(11) NOT NULL, + RedirectGridY int(11) NOT NULL, + ParentEstateID int(10) NOT NULL, + SunPosition double NOT NULL, + EstateSkipScripts tinyint(4) NOT NULL, + BillableFactor float NOT NULL, + PublicAccess tinyint(4) NOT NULL +); +insert into estate_settings (EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); +delete from estate_settings; +CREATE TABLE estate_users ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estateban ( + EstateID int(10) NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL, + bannedNameMask varchar(64) default NULL +); + +drop table regionsettings; +CREATE TABLE regionsettings ( + regionUUID char(36) NOT NULL, + block_terraform int(11) NOT NULL, + block_fly int(11) NOT NULL, + allow_damage int(11) NOT NULL, + restrict_pushing int(11) NOT NULL, + allow_land_resell int(11) NOT NULL, + allow_land_join_divide int(11) NOT NULL, + block_show_in_search int(11) NOT NULL, + agent_limit int(11) NOT NULL, + object_bonus float NOT NULL, + maturity int(11) NOT NULL, + disable_scripts int(11) NOT NULL, + disable_collisions int(11) NOT NULL, + disable_physics int(11) NOT NULL, + terrain_texture_1 char(36) NOT NULL, + terrain_texture_2 char(36) NOT NULL, + terrain_texture_3 char(36) NOT NULL, + terrain_texture_4 char(36) NOT NULL, + elevation_1_nw float NOT NULL, + elevation_2_nw float NOT NULL, + elevation_1_ne float NOT NULL, + elevation_2_ne float NOT NULL, + elevation_1_se float NOT NULL, + elevation_2_se float NOT NULL, + elevation_1_sw float NOT NULL, + elevation_2_sw float NOT NULL, + water_height float NOT NULL, + terrain_raise_limit float NOT NULL, + terrain_lower_limit float NOT NULL, + use_estate_sun int(11) NOT NULL, + fixed_sun int(11) NOT NULL, + sun_position float NOT NULL, + covenant char(36) default NULL, + Sandbox tinyint(4) NOT NULL, + PRIMARY KEY (regionUUID) +); + +CREATE INDEX estate_ban_estate_id on estateban(EstateID); +CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); +CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); +CREATE INDEX estate_map_estate_id on estate_map(EstateID); +CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); +CREATE INDEX estate_users_estate_id on estate_users(EstateID); + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/006_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/006_UserStore.sql new file mode 100644 index 0000000000..f9454c55cf --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/006_UserStore.sql @@ -0,0 +1,20 @@ +BEGIN TRANSACTION; + +-- usersagents table +CREATE TABLE IF NOT EXISTS useragents( + UUID varchar(255) primary key, + agentIP varchar(255), + agentPort integer, + agentOnline boolean, + sessionID varchar(255), + secureSessionID varchar(255), + regionID varchar(255), + loginTime integer, + logoutTime integer, + currentRegion varchar(255), + currentHandle varchar(255), + currentPosX float, + currentPosY float, + currentPosZ float); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/007_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/007_RegionStore.sql new file mode 100644 index 0000000000..1c813a0d40 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/007_RegionStore.sql @@ -0,0 +1,8 @@ +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null default ''; + +alter table estate_settings add column EstateOwner varchar(36) not null default ''; + +commit; + diff --git a/OpenSim/Data/SQLiteNG/Resources/007_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/007_UserStore.sql new file mode 100644 index 0000000000..8b0cd285c7 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/007_UserStore.sql @@ -0,0 +1,7 @@ +BEGIN TRANSACTION; + +ALTER TABLE useragents add currentLookAtX float not null default 128; +ALTER TABLE useragents add currentLookAtY float not null default 128; +ALTER TABLE useragents add currentLookAtZ float not null default 70; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/008_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/008_RegionStore.sql new file mode 100644 index 0000000000..28bfbf59c3 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/008_RegionStore.sql @@ -0,0 +1,6 @@ +begin; + +alter table estate_settings add column DenyMinors tinyint not null default 0; + +commit; + diff --git a/OpenSim/Data/SQLiteNG/Resources/008_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/008_UserStore.sql new file mode 100644 index 0000000000..97da81848c --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/008_UserStore.sql @@ -0,0 +1,5 @@ +BEGIN TRANSACTION; + +ALTER TABLE users add email varchar(250); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/009_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/009_RegionStore.sql new file mode 100644 index 0000000000..1f40548f36 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/009_RegionStore.sql @@ -0,0 +1,8 @@ +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/009_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/009_UserStore.sql new file mode 100644 index 0000000000..8ab03ef897 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/009_UserStore.sql @@ -0,0 +1,11 @@ +BEGIN; + +update users + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update useragents + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/010_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/010_RegionStore.sql new file mode 100644 index 0000000000..b91ccf0a8d --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/010_RegionStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE prims ADD COLUMN ClickAction INTEGER NOT NULL default 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/010_UserStore.sql b/OpenSim/Data/SQLiteNG/Resources/010_UserStore.sql new file mode 100644 index 0000000000..5f956dadfd --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/010_UserStore.sql @@ -0,0 +1,37 @@ +BEGIN TRANSACTION; + +CREATE TABLE IF NOT EXISTS avatarappearance( + Owner varchar(36) NOT NULL primary key, + BodyItem varchar(36) DEFAULT NULL, + BodyAsset varchar(36) DEFAULT NULL, + SkinItem varchar(36) DEFAULT NULL, + SkinAsset varchar(36) DEFAULT NULL, + HairItem varchar(36) DEFAULT NULL, + HairAsset varchar(36) DEFAULT NULL, + EyesItem varchar(36) DEFAULT NULL, + EyesAsset varchar(36) DEFAULT NULL, + ShirtItem varchar(36) DEFAULT NULL, + ShirtAsset varchar(36) DEFAULT NULL, + PantsItem varchar(36) DEFAULT NULL, + PantsAsset varchar(36) DEFAULT NULL, + ShoesItem varchar(36) DEFAULT NULL, + ShoesAsset varchar(36) DEFAULT NULL, + SocksItem varchar(36) DEFAULT NULL, + SocksAsset varchar(36) DEFAULT NULL, + JacketItem varchar(36) DEFAULT NULL, + JacketAsset varchar(36) DEFAULT NULL, + GlovesItem varchar(36) DEFAULT NULL, + GlovesAsset varchar(36) DEFAULT NULL, + UnderShirtItem varchar(36) DEFAULT NULL, + UnderShirtAsset varchar(36) DEFAULT NULL, + UnderPantsItem varchar(36) DEFAULT NULL, + UnderPantsAsset varchar(36) DEFAULT NULL, + SkirtItem varchar(36) DEFAULT NULL, + SkirtAsset varchar(36) DEFAULT NULL, + Texture blob, + VisualParams blob, + Serial int DEFAULT NULL, + AvatarHeight float DEFAULT NULL +); + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/011_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/011_RegionStore.sql new file mode 100644 index 0000000000..42bef89616 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/011_RegionStore.sql @@ -0,0 +1,28 @@ +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton1 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton2 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton3 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton4 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN LoopedSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN TextureAnimation string; +ALTER TABLE prims ADD COLUMN ParticleSystem string; +ALTER TABLE prims ADD COLUMN OmegaX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ForceMouselook string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SalePrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SaleType string NOT NULL default 0; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/012_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/012_RegionStore.sql new file mode 100644 index 0000000000..d952b78bd2 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/012_RegionStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE prims ADD COLUMN Material INTEGER NOT NULL default 3; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/013_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/013_RegionStore.sql new file mode 100644 index 0000000000..11529cd3f1 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/013_RegionStore.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE land ADD COLUMN OtherCleanTime INTEGER NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell INTEGER NOT NULL default 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/014_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/014_RegionStore.sql new file mode 100644 index 0000000000..c59b27e745 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/014_RegionStore.sql @@ -0,0 +1,8 @@ +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + diff --git a/OpenSim/Data/SQLiteNG/Resources/015_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/015_RegionStore.sql new file mode 100644 index 0000000000..c43f356be3 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/015_RegionStore.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float NOT NULL default 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/016_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/016_RegionStore.sql new file mode 100644 index 0000000000..52f160cdbc --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/016_RegionStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE prims ADD COLUMN VolumeDetect INTEGER NOT NULL DEFAULT 0; + +COMMIT; diff --git a/OpenSim/Data/SQLiteNG/Resources/017_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/017_RegionStore.sql new file mode 100644 index 0000000000..6c6b7b5d40 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/017_RegionStore.sql @@ -0,0 +1,8 @@ +BEGIN; +CREATE TEMPORARY TABLE prims_backup(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims_backup SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims; +DROP TABLE prims; +CREATE TABLE prims(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims_backup; +DROP TABLE prims_backup; +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/018_RegionStore.sql b/OpenSim/Data/SQLiteNG/Resources/018_RegionStore.sql new file mode 100644 index 0000000000..6a390c2161 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/018_RegionStore.sql @@ -0,0 +1,79 @@ +BEGIN; + +update terrain + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = substr(LandUUID, 1, 8) || "-" || substr(LandUUID, 9, 4) || "-" || substr(LandUUID, 13, 4) || "-" || substr(LandUUID, 17, 4) || "-" || substr(LandUUID, 21, 12) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = substr(AccessUUID, 1, 8) || "-" || substr(AccessUUID, 9, 4) || "-" || substr(AccessUUID, 13, 4) || "-" || substr(AccessUUID, 17, 4) || "-" || substr(AccessUUID, 21, 12) + where AccessUUID not like '%-%'; + + +update prims + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update prims + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = substr(SceneGroupID, 1, 8) || "-" || substr(SceneGroupID, 9, 4) || "-" || substr(SceneGroupID, 13, 4) || "-" || substr(SceneGroupID, 17, 4) || "-" || substr(SceneGroupID, 21, 12) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = substr(CreatorID, 1, 8) || "-" || substr(CreatorID, 9, 4) || "-" || substr(CreatorID, 13, 4) || "-" || substr(CreatorID, 17, 4) || "-" || substr(CreatorID, 21, 12) + where CreatorID not like '%-%'; + +update prims + set OwnerID = substr(OwnerID, 1, 8) || "-" || substr(OwnerID, 9, 4) || "-" || substr(OwnerID, 13, 4) || "-" || substr(OwnerID, 17, 4) || "-" || substr(OwnerID, 21, 12) + where OwnerID not like '%-%'; + +update prims + set GroupID = substr(GroupID, 1, 8) || "-" || substr(GroupID, 9, 4) || "-" || substr(GroupID, 13, 4) || "-" || substr(GroupID, 17, 4) || "-" || substr(GroupID, 21, 12) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = substr(LastOwnerID, 1, 8) || "-" || substr(LastOwnerID, 9, 4) || "-" || substr(LastOwnerID, 13, 4) || "-" || substr(LastOwnerID, 17, 4) || "-" || substr(LastOwnerID, 21, 12) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + + +update land + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update land + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = substr(OwnerUUID, 1, 8) || "-" || substr(OwnerUUID, 9, 4) || "-" || substr(OwnerUUID, 13, 4) || "-" || substr(OwnerUUID, 17, 4) || "-" || substr(OwnerUUID, 21, 12) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = substr(GroupUUID, 1, 8) || "-" || substr(GroupUUID, 9, 4) || "-" || substr(GroupUUID, 13, 4) || "-" || substr(GroupUUID, 17, 4) || "-" || substr(GroupUUID, 21, 12) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = substr(MediaTextureUUID, 1, 8) || "-" || substr(MediaTextureUUID, 9, 4) || "-" || substr(MediaTextureUUID, 13, 4) || "-" || substr(MediaTextureUUID, 17, 4) || "-" || substr(MediaTextureUUID, 21, 12) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = substr(SnapshotUUID, 1, 8) || "-" || substr(SnapshotUUID, 9, 4) || "-" || substr(SnapshotUUID, 13, 4) || "-" || substr(SnapshotUUID, 17, 4) || "-" || substr(SnapshotUUID, 21, 12) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = substr(AuthbuyerID, 1, 8) || "-" || substr(AuthbuyerID, 9, 4) || "-" || substr(AuthbuyerID, 13, 4) || "-" || substr(AuthbuyerID, 17, 4) || "-" || substr(AuthbuyerID, 21, 12) + where AuthbuyerID not like '%-%'; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/Resources/OpenSim.Data.SQLite.addin.xml b/OpenSim/Data/SQLiteNG/Resources/OpenSim.Data.SQLite.addin.xml new file mode 100644 index 0000000000..e6764facbd --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Resources/OpenSim.Data.SQLite.addin.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs new file mode 100644 index 0000000000..a032670588 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs @@ -0,0 +1,343 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using System.Collections.Generic; +using log4net; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// An asset storage interface for the SQLite database system + /// + public class SQLiteAssetData : AssetDataBase + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const string SelectAssetSQL = "select * from assets where UUID=:UUID"; + private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, UUID from assets limit :start, :count"; + private const string DeleteAssetSQL = "delete from assets where UUID=:UUID"; + private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Data)"; + private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, Data=:Data where UUID=:UUID"; + private const string assetSelect = "select * from assets"; + + private SqliteConnection m_conn; + + override public void Dispose() + { + if (m_conn != null) + { + m_conn.Close(); + m_conn = null; + } + } + + /// + /// + /// Initialises AssetData interface + /// Loads and initialises a new SQLite connection and maintains it. + /// use default URI if connect string is empty. + /// + /// + /// connect string + override public void Initialise(string dbconnect) + { + if (dbconnect == string.Empty) + { + dbconnect = "URI=file:Asset.db,version=3"; + } + m_conn = new SqliteConnection(dbconnect); + m_conn.Open(); + + Assembly assem = GetType().Assembly; + Migration m = new Migration(m_conn, assem, "AssetStore"); + m.Update(); + + return; + } + + /// + /// Fetch Asset + /// + /// UUID of ... ? + /// Asset base + override public AssetBase GetAsset(UUID uuid) + { + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); + using (IDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + AssetBase asset = buildAsset(reader); + reader.Close(); + return asset; + } + else + { + reader.Close(); + return null; + } + } + } + } + } + + /// + /// Create an asset + /// + /// Asset Base + override public void StoreAsset(AssetBase asset) + { + //m_log.Info("[ASSET DB]: Creating Asset " + asset.FullID.ToString()); + if (ExistsAsset(asset.FullID)) + { + //LogAssetLoad(asset); + + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(UpdateAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Name", asset.Name)); + cmd.Parameters.Add(new SqliteParameter(":Description", asset.Description)); + cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type)); + cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); + cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); + cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); + + cmd.ExecuteNonQuery(); + } + } + } + else + { + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(InsertAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Name", asset.Name)); + cmd.Parameters.Add(new SqliteParameter(":Description", asset.Description)); + cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type)); + cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); + cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); + cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); + + cmd.ExecuteNonQuery(); + } + } + } + } + +// /// +// /// Some... logging functionnality +// /// +// /// +// private static void LogAssetLoad(AssetBase asset) +// { +// string temporary = asset.Temporary ? "Temporary" : "Stored"; +// string local = asset.Local ? "Local" : "Remote"; +// +// int assetLength = (asset.Data != null) ? asset.Data.Length : 0; +// +// m_log.Debug("[ASSET DB]: " + +// string.Format("Loaded {5} {4} Asset: [{0}][{3}] \"{1}\":{2} ({6} bytes)", +// asset.FullID, asset.Name, asset.Description, asset.Type, +// temporary, local, assetLength)); +// } + + /// + /// Check if an asset exist in database + /// + /// The asset UUID + /// True if exist, or false. + override public bool ExistsAsset(UUID uuid) + { + lock (this) { + using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); + using (IDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + reader.Close(); + return true; + } + else + { + reader.Close(); + return false; + } + } + } + } + } + + /// + /// Delete an asset from database + /// + /// + public void DeleteAsset(UUID uuid) + { + using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); + + cmd.ExecuteNonQuery(); + } + } + + /// + /// + /// + /// + /// + private static AssetBase buildAsset(IDataReader row) + { + // TODO: this doesn't work yet because something more + // interesting has to be done to actually get these values + // back out. Not enough time to figure it out yet. + AssetBase asset = new AssetBase( + new UUID((String)row["UUID"]), + (String)row["Name"], + Convert.ToSByte(row["Type"]), + UUID.Zero.ToString() + ); + + asset.Description = (String) row["Description"]; + asset.Local = Convert.ToBoolean(row["Local"]); + asset.Temporary = Convert.ToBoolean(row["Temporary"]); + asset.Data = (byte[]) row["Data"]; + return asset; + } + + private static AssetMetadata buildAssetMetadata(IDataReader row) + { + AssetMetadata metadata = new AssetMetadata(); + + metadata.FullID = new UUID((string) row["UUID"]); + metadata.Name = (string) row["Name"]; + metadata.Description = (string) row["Description"]; + metadata.Type = Convert.ToSByte(row["Type"]); + metadata.Temporary = Convert.ToBoolean(row["Temporary"]); // Not sure if this is correct. + + // Current SHA1s are not stored/computed. + metadata.SHA1 = new byte[] {}; + + return metadata; + } + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public override List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(SelectAssetMetadataSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":start", start)); + cmd.Parameters.Add(new SqliteParameter(":count", count)); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + AssetMetadata metadata = buildAssetMetadata(reader); + retList.Add(metadata); + } + } + } + } + + return retList; + } + + /*********************************************************************** + * + * Database Binding functions + * + * These will be db specific due to typing, and minor differences + * in databases. + * + **********************************************************************/ + + #region IPlugin interface + + /// + /// + /// + override public string Version + { + get + { + Module module = GetType().Module; + // string dllName = module.Assembly.ManifestModule.Name; + Version dllVersion = module.Assembly.GetName().Version; + + return + string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, + dllVersion.Revision); + } + } + + /// + /// Initialise the AssetData interface using default URI + /// + override public void Initialise() + { + Initialise("URI=file:Asset.db,version=3"); + } + + /// + /// Name of this DB provider + /// + override public string Name + { + get { return "SQLite Asset storage engine"; } + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs new file mode 100644 index 0000000000..aa10734d50 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs @@ -0,0 +1,262 @@ +/* + * 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 OpenSimulator 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.SqliteClient; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteAuthenticationData : SQLiteFramework, IAuthenticationData + { + private string m_Realm; + private List m_ColumnNames; + private int m_LastExpire; + private string m_connectionString; + + protected static SqliteConnection m_Connection; + private static bool m_initialized = false; + + public SQLiteAuthenticationData(string connectionString, string realm) + : base(connectionString) + { + m_Realm = realm; + m_connectionString = connectionString; + + if (!m_initialized) + { + m_Connection = new SqliteConnection(connectionString); + m_Connection.Open(); + + using (SqliteConnection dbcon = (SqliteConnection)((ICloneable)m_Connection).Clone()) + { + dbcon.Open(); + Migration m = new Migration(dbcon, GetType().Assembly, "AuthStore"); + m.Update(); + dbcon.Close(); + } + + m_initialized = true; + } + } + + public AuthenticationData Get(UUID principalID) + { + AuthenticationData ret = new AuthenticationData(); + ret.Data = new Dictionary(); + + SqliteCommand cmd = new SqliteCommand("select * from `" + m_Realm + "` where UUID = :PrincipalID"); + cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString())); + + IDataReader result = ExecuteReader(cmd, m_Connection); + + try + { + if (result.Read()) + { + ret.PrincipalID = principalID; + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + if (s == "UUID") + continue; + + ret.Data[s] = result[s].ToString(); + } + + return ret; + } + else + { + return null; + } + } + catch + { + } + finally + { + CloseCommand(cmd); + } + + return null; + } + + public bool Store(AuthenticationData data) + { + if (data.Data.ContainsKey("UUID")) + data.Data.Remove("UUID"); + + string[] fields = new List(data.Data.Keys).ToArray(); + string[] values = new string[data.Data.Count]; + int i = 0; + foreach (object o in data.Data.Values) + values[i++] = o.ToString(); + + SqliteCommand cmd = new SqliteCommand(); + + if (Get(data.PrincipalID) != null) + { + + + string update = "update `" + m_Realm + "` set "; + bool first = true; + foreach (string field in fields) + { + if (!first) + update += ", "; + update += "`" + field + "` = :" + field; + cmd.Parameters.Add(new SqliteParameter(":" + field, data.Data[field])); + + first = false; + } + + update += " where UUID = :UUID"; + cmd.Parameters.Add(new SqliteParameter(":UUID", data.PrincipalID.ToString())); + + cmd.CommandText = update; + try + { + if (ExecuteNonQuery(cmd, m_Connection) < 1) + { + CloseCommand(cmd); + return false; + } + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + CloseCommand(cmd); + return false; + } + } + + else + { + string insert = "insert into `" + m_Realm + "` (`UUID`, `" + + String.Join("`, `", fields) + + "`) values (:UUID, :" + String.Join(", :", fields) + ")"; + + cmd.Parameters.Add(new SqliteParameter(":UUID", data.PrincipalID.ToString())); + foreach (string field in fields) + cmd.Parameters.Add(new SqliteParameter(":" + field, data.Data[field])); + + cmd.CommandText = insert; + + try + { + if (ExecuteNonQuery(cmd, m_Connection) < 1) + { + CloseCommand(cmd); + return false; + } + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + CloseCommand(cmd); + return false; + } + } + + CloseCommand(cmd); + + return true; + } + + public bool SetDataItem(UUID principalID, string item, string value) + { + SqliteCommand cmd = new SqliteCommand("update `" + m_Realm + + "` set `" + item + "` = " + value + " where UUID = '" + principalID.ToString() + "'"); + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + + return false; + } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + SqliteCommand cmd = new SqliteCommand("insert into tokens (UUID, token, validity) values ('" + principalID.ToString() + + "', '" + token + "', datetime('now', 'localtime', '+" + lifetime.ToString() + " minutes'))"); + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + { + cmd.Dispose(); + return true; + } + + cmd.Dispose(); + return false; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + SqliteCommand cmd = new SqliteCommand("update tokens set validity = datetime('now', 'localtime', '+" + lifetime.ToString() + + " minutes') where UUID = '" + principalID.ToString() + "' and token = '" + token + "' and validity > datetime('now', 'localtime')"); + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + { + cmd.Dispose(); + return true; + } + + cmd.Dispose(); + + return false; + } + + private void DoExpire() + { + SqliteCommand cmd = new SqliteCommand("delete from tokens where validity < datetime('now', 'localtime')"); + ExecuteNonQuery(cmd, m_Connection); + + cmd.Dispose(); + + m_LastExpire = System.Environment.TickCount; + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs b/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs new file mode 100644 index 0000000000..b3f4a4c077 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs @@ -0,0 +1,74 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.SqliteClient; + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQLite Interface for Avatar Data + /// + public class SQLiteAvatarData : SQLiteGenericTableHandler, + IAvatarData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public SQLiteAvatarData(string connectionString, string realm) : + base(connectionString, realm, "Avatar") + { + } + + public bool Delete(UUID principalID, string name) + { + SqliteCommand cmd = new SqliteCommand(); + + cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm); + cmd.Parameters.Add(":PrincipalID", principalID.ToString()); + cmd.Parameters.Add(":Name", name); + + try + { + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + + return false; + } + finally + { + CloseCommand(cmd); + } + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs b/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs new file mode 100644 index 0000000000..bd6b776f80 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs @@ -0,0 +1,387 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using log4net; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteEstateStore : IEstateDataStore + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SqliteConnection m_connection; + private string m_connectionString; + + private FieldInfo[] m_Fields; + private Dictionary m_FieldMap = + new Dictionary(); + + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + + m_log.Info("[ESTATE DB]: Sqlite - connecting: "+m_connectionString); + + m_connection = new SqliteConnection(m_connectionString); + m_connection.Open(); + + Assembly assem = GetType().Assembly; + Migration m = new Migration(m_connection, assem, "EstateStore"); + m.Update(); + + m_connection.Close(); + m_connection.Open(); + + Type t = typeof(EstateSettings); + m_Fields = t.GetFields(BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + foreach (FieldInfo f in m_Fields) + if (f.Name.Substring(0, 2) == "m_") + m_FieldMap[f.Name.Substring(2)] = f; + } + + private string[] FieldList + { + get { return new List(m_FieldMap.Keys).ToArray(); } + } + + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_map left join estate_settings on estate_map.EstateID = estate_settings.EstateID where estate_settings.EstateID is not null and RegionID = :RegionID"; + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = sql; + cmd.Parameters.Add(":RegionID", regionID.ToString()); + + return DoLoad(cmd, regionID, create); + } + + private EstateSettings DoLoad(SqliteCommand cmd, UUID regionID, bool create) + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + IDataReader r = cmd.ExecuteReader(); + + if (r.Read()) + { + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + int v = Convert.ToInt32(r[name]); + if (v != 0) + m_FieldMap[name].SetValue(es, true); + else + m_FieldMap[name].SetValue(es, false); + } + else if (m_FieldMap[name].GetValue(es) is UUID) + { + UUID uuid = UUID.Zero; + + UUID.TryParse(r[name].ToString(), out uuid); + m_FieldMap[name].SetValue(es, uuid); + } + else + { + m_FieldMap[name].SetValue(es, Convert.ChangeType(r[name], m_FieldMap[name].FieldType)); + } + } + r.Close(); + } + else if (create) + { + r.Close(); + + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = "insert into estate_settings ("+String.Join(",", names.ToArray())+") values ( :"+String.Join(", :", names.ToArray())+")"; + + cmd.CommandText = sql; + cmd.Parameters.Clear(); + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd.Parameters.Add(":"+name, "1"); + else + cmd.Parameters.Add(":"+name, "0"); + } + else + { + cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd.ExecuteNonQuery(); + + cmd.CommandText = "select LAST_INSERT_ROWID() as id"; + cmd.Parameters.Clear(); + + r = cmd.ExecuteReader(); + + r.Read(); + + es.EstateID = Convert.ToUInt32(r["id"]); + + r.Close(); + + cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; + cmd.Parameters.Add(":RegionID", regionID.ToString()); + cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + + // This will throw on dupe key + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception) + { + } + + es.Save(); + } + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + return es; + } + + public void StoreEstateSettings(EstateSettings es) + { + List fields = new List(FieldList); + fields.Remove("EstateID"); + + List terms = new List(); + + foreach (string f in fields) + terms.Add(f+" = :"+f); + + string sql = "update estate_settings set "+String.Join(", ", terms.ToArray())+" where EstateID = :EstateID"; + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = sql; + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd.Parameters.Add(":"+name, "1"); + else + cmd.Parameters.Add(":"+name, "0"); + } + else + { + cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd.ExecuteNonQuery(); + + SaveBanList(es); + SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers); + SaveUUIDList(es.EstateID, "estate_users", es.EstateAccess); + SaveUUIDList(es.EstateID, "estate_groups", es.EstateGroups); + } + + private void LoadBanList(EstateSettings es) + { + es.ClearBans(); + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID"; + cmd.Parameters.Add(":EstateID", es.EstateID); + + IDataReader r = cmd.ExecuteReader(); + + while (r.Read()) + { + EstateBan eb = new EstateBan(); + + UUID uuid = new UUID(); + UUID.TryParse(r["bannedUUID"].ToString(), out uuid); + + eb.BannedUserID = uuid; + eb.BannedHostAddress = "0.0.0.0"; + eb.BannedHostIPMask = "0.0.0.0"; + es.AddBan(eb); + } + r.Close(); + } + + private void SaveBanList(EstateSettings es) + { + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = "delete from estateban where EstateID = :EstateID"; + cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) values ( :EstateID, :bannedUUID, '', '', '' )"; + + foreach (EstateBan b in es.EstateBans) + { + cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + cmd.Parameters.Add(":bannedUUID", b.BannedUserID.ToString()); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + + void SaveUUIDList(uint EstateID, string table, UUID[] data) + { + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = "delete from "+table+" where EstateID = :EstateID"; + cmd.Parameters.Add(":EstateID", EstateID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into "+table+" (EstateID, uuid) values ( :EstateID, :uuid )"; + + foreach (UUID uuid in data) + { + cmd.Parameters.Add(":EstateID", EstateID.ToString()); + cmd.Parameters.Add(":uuid", uuid.ToString()); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + + UUID[] LoadUUIDList(uint EstateID, string table) + { + List uuids = new List(); + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID"; + cmd.Parameters.Add(":EstateID", EstateID); + + IDataReader r = cmd.ExecuteReader(); + + while (r.Read()) + { + // EstateBan eb = new EstateBan(); + + UUID uuid = new UUID(); + UUID.TryParse(r["uuid"].ToString(), out uuid); + + uuids.Add(uuid); + } + r.Close(); + + return uuids.ToArray(); + } + + public EstateSettings LoadEstateSettings(int estateID) + { + string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_settings where estate_settings.EstateID :EstateID"; + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = sql; + cmd.Parameters.Add(":EstateID", estateID.ToString()); + + return DoLoad(cmd, UUID.Zero, false); + } + + public List GetEstates(string search) + { + List result = new List(); + + string sql = "select EstateID from estate_settings where estate_settings.EstateName :EstateName"; + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = sql; + cmd.Parameters.Add(":EstateName", search); + + IDataReader r = cmd.ExecuteReader(); + + while (r.Read()) + { + result.Add(Convert.ToInt32(r["EstateID"])); + } + r.Close(); + + return result; + } + + public bool LinkRegion(UUID regionID, int estateID) + { + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + + cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; + cmd.Parameters.Add(":RegionID", regionID.ToString()); + cmd.Parameters.Add(":EstateID", estateID.ToString()); + + if (cmd.ExecuteNonQuery() == 0) + return false; + + return true; + } + + public List GetRegions(int estateID) + { + return new List(); + } + + public bool DeleteEstate(int estateID) + { + return false; + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs b/OpenSim/Data/SQLiteNG/SQLiteFramework.cs new file mode 100644 index 0000000000..20b508515a --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteFramework.cs @@ -0,0 +1,91 @@ +/* + * 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 OpenSimulator 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.SqliteClient; + +namespace OpenSim.Data.SQLite +{ + /// + /// A database interface class to a user profile storage system + /// + public class SQLiteFramework + { + protected Object m_lockObject = new Object(); + + protected SQLiteFramework(string connectionString) + { + } + + ////////////////////////////////////////////////////////////// + // + // All non queries are funneled through one connection + // to increase performance a little + // + protected int ExecuteNonQuery(SqliteCommand cmd, SqliteConnection connection) + { + lock (connection) + { + SqliteConnection newConnection = + (SqliteConnection)((ICloneable)connection).Clone(); + newConnection.Open(); + + cmd.Connection = newConnection; + //Console.WriteLine("XXX " + cmd.CommandText); + + return cmd.ExecuteNonQuery(); + } + } + + protected IDataReader ExecuteReader(SqliteCommand cmd, SqliteConnection connection) + { + lock (connection) + { + SqliteConnection newConnection = + (SqliteConnection)((ICloneable)connection).Clone(); + newConnection.Open(); + + cmd.Connection = newConnection; + //Console.WriteLine("XXX " + cmd.CommandText); + + return cmd.ExecuteReader(); + } + } + + protected void CloseCommand(SqliteCommand cmd) + { + cmd.Connection.Close(); + cmd.Connection.Dispose(); + cmd.Dispose(); + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs b/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs new file mode 100644 index 0000000000..0b121826d6 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs @@ -0,0 +1,70 @@ +/* + * 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 OpenSimulator 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.SqliteClient; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteFriendsData : SQLiteGenericTableHandler, IFriendsData + { + public SQLiteFriendsData(string connectionString, string realm) + : base(connectionString, realm, "FriendsStore") + { + } + + public FriendsData[] GetFriends(UUID userID) + { + SqliteCommand cmd = new SqliteCommand(); + + cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = :PrincipalID", m_Realm); + cmd.Parameters.Add(":PrincipalID", userID.ToString()); + + return DoQuery(cmd); + + } + + public bool Delete(UUID principalID, string friend) + { + SqliteCommand cmd = new SqliteCommand(); + + cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm); + cmd.Parameters.Add(":PrincipalID", principalID.ToString()); + cmd.Parameters.Add(":Friend", friend); + + ExecuteNonQuery(cmd, cmd.Connection); + + return true; + } + + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs new file mode 100644 index 0000000000..b39bb19fb7 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs @@ -0,0 +1,268 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using log4net; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteGenericTableHandler : SQLiteFramework where T: class, new() + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary m_Fields = + new Dictionary(); + + protected List m_ColumnNames = null; + protected string m_Realm; + protected FieldInfo m_DataField = null; + + protected static SqliteConnection m_Connection; + private static bool m_initialized; + + public SQLiteGenericTableHandler(string connectionString, + string realm, string storeName) : base(connectionString) + { + m_Realm = realm; + + if (!m_initialized) + { + m_Connection = new SqliteConnection(connectionString); + m_Connection.Open(); + + if (storeName != String.Empty) + { + Assembly assem = GetType().Assembly; + SqliteConnection newConnection = + (SqliteConnection)((ICloneable)m_Connection).Clone(); + newConnection.Open(); + + Migration m = new Migration(newConnection, assem, storeName); + m.Update(); + newConnection.Close(); + newConnection.Dispose(); + } + + m_initialized = true; + } + + Type t = typeof(T); + FieldInfo[] fields = t.GetFields(BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + if (fields.Length == 0) + return; + + foreach (FieldInfo f in fields) + { + if (f.Name != "Data") + m_Fields[f.Name] = f; + else + m_DataField = f; + } + } + + private void CheckColumnNames(IDataReader reader) + { + if (m_ColumnNames != null) + return; + + m_ColumnNames = new List(); + + DataTable schemaTable = reader.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + { + if (row["ColumnName"] != null && + (!m_Fields.ContainsKey(row["ColumnName"].ToString()))) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + } + + public T[] Get(string field, string key) + { + return Get(new string[] { field }, new string[] { key }); + } + + public T[] Get(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return new T[0]; + + List terms = new List(); + + SqliteCommand cmd = new SqliteCommand(); + + for (int i = 0 ; i < fields.Length ; i++) + { + cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i])); + terms.Add("`" + fields[i] + "` = :" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select * from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + return DoQuery(cmd); + } + + protected T[] DoQuery(SqliteCommand cmd) + { + IDataReader reader = ExecuteReader(cmd, m_Connection); + if (reader == null) + return new T[0]; + + CheckColumnNames(reader); + + List result = new List(); + + while (reader.Read()) + { + T row = new T(); + + foreach (string name in m_Fields.Keys) + { + if (m_Fields[name].GetValue(row) is bool) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v != 0 ? true : false); + } + else if (m_Fields[name].GetValue(row) is UUID) + { + UUID uuid = UUID.Zero; + + UUID.TryParse(reader[name].ToString(), out uuid); + m_Fields[name].SetValue(row, uuid); + } + else if (m_Fields[name].GetValue(row) is int) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else + { + m_Fields[name].SetValue(row, reader[name]); + } + } + + if (m_DataField != null) + { + Dictionary data = + new Dictionary(); + + foreach (string col in m_ColumnNames) + { + data[col] = reader[col].ToString(); + if (data[col] == null) + data[col] = String.Empty; + } + + m_DataField.SetValue(row, data); + } + + result.Add(row); + } + + CloseCommand(cmd); + + return result.ToArray(); + } + + public T[] Get(string where) + { + SqliteCommand cmd = new SqliteCommand(); + + string query = String.Format("select * from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + return DoQuery(cmd); + } + + public bool Store(T row) + { + SqliteCommand cmd = new SqliteCommand(); + + string query = ""; + List names = new List(); + List values = new List(); + + foreach (FieldInfo fi in m_Fields.Values) + { + names.Add(fi.Name); + values.Add(":" + fi.Name); + cmd.Parameters.Add(new SqliteParameter(":" + fi.Name, fi.GetValue(row).ToString())); + } + + if (m_DataField != null) + { + Dictionary data = + (Dictionary)m_DataField.GetValue(row); + + foreach (KeyValuePair kvp in data) + { + names.Add(kvp.Key); + values.Add(":" + kvp.Key); + cmd.Parameters.Add(new SqliteParameter(":" + kvp.Key, kvp.Value)); + } + } + + query = String.Format("replace into {0} (`", m_Realm) + String.Join("`,`", names.ToArray()) + "`) values (" + String.Join(",", values.ToArray()) + ")"; + + cmd.CommandText = query; + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + + return false; + } + + public bool Delete(string field, string val) + { + SqliteCommand cmd = new SqliteCommand(); + + cmd.CommandText = String.Format("delete from {0} where `{1}` = :{1}", m_Realm, field); + cmd.Parameters.Add(new SqliteParameter(field, val)); + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + + return false; + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs new file mode 100644 index 0000000000..a5e051726e --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs @@ -0,0 +1,898 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using log4net; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// An Inventory Interface to the SQLite database + /// + public class SQLiteInventoryStore : SQLiteUtil, IInventoryDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const string invItemsSelect = "select * from inventoryitems"; + private const string invFoldersSelect = "select * from inventoryfolders"; + + private static SqliteConnection conn; + private static DataSet ds; + private static SqliteDataAdapter invItemsDa; + private static SqliteDataAdapter invFoldersDa; + + private static bool m_Initialized = false; + + public void Initialise() + { + m_log.Info("[SQLiteInventoryData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + /// + /// + /// Initialises Inventory interface + /// Loads and initialises a new SQLite connection and maintains it. + /// use default URI if connect string string is empty. + /// + /// + /// connect string + public void Initialise(string dbconnect) + { + if (!m_Initialized) + { + m_Initialized = true; + + if (dbconnect == string.Empty) + { + dbconnect = "URI=file:inventoryStore.db,version=3"; + } + m_log.Info("[INVENTORY DB]: Sqlite - connecting: " + dbconnect); + conn = new SqliteConnection(dbconnect); + + conn.Open(); + + Assembly assem = GetType().Assembly; + Migration m = new Migration(conn, assem, "InventoryStore"); + m.Update(); + + SqliteCommand itemsSelectCmd = new SqliteCommand(invItemsSelect, conn); + invItemsDa = new SqliteDataAdapter(itemsSelectCmd); + // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); + + SqliteCommand foldersSelectCmd = new SqliteCommand(invFoldersSelect, conn); + invFoldersDa = new SqliteDataAdapter(foldersSelectCmd); + + ds = new DataSet(); + + ds.Tables.Add(createInventoryFoldersTable()); + invFoldersDa.Fill(ds.Tables["inventoryfolders"]); + setupFoldersCommands(invFoldersDa, conn); + m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); + + ds.Tables.Add(createInventoryItemsTable()); + invItemsDa.Fill(ds.Tables["inventoryitems"]); + setupItemsCommands(invItemsDa, conn); + m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); + + ds.AcceptChanges(); + } + } + + /// + /// Closes the inventory interface + /// + public void Dispose() + { + if (conn != null) + { + conn.Close(); + conn = null; + } + if (invItemsDa != null) + { + invItemsDa.Dispose(); + invItemsDa = null; + } + if (invFoldersDa != null) + { + invFoldersDa.Dispose(); + invFoldersDa = null; + } + if (ds != null) + { + ds.Dispose(); + ds = null; + } + } + + /// + /// + /// + /// + /// + public InventoryItemBase buildItem(DataRow row) + { + InventoryItemBase item = new InventoryItemBase(); + item.ID = new UUID((string) row["UUID"]); + item.AssetID = new UUID((string) row["assetID"]); + item.AssetType = Convert.ToInt32(row["assetType"]); + item.InvType = Convert.ToInt32(row["invType"]); + item.Folder = new UUID((string) row["parentFolderID"]); + item.Owner = new UUID((string) row["avatarID"]); + item.CreatorId = (string)row["creatorsID"]; + item.Name = (string) row["inventoryName"]; + item.Description = (string) row["inventoryDescription"]; + + item.NextPermissions = Convert.ToUInt32(row["inventoryNextPermissions"]); + item.CurrentPermissions = Convert.ToUInt32(row["inventoryCurrentPermissions"]); + item.BasePermissions = Convert.ToUInt32(row["inventoryBasePermissions"]); + item.EveryOnePermissions = Convert.ToUInt32(row["inventoryEveryOnePermissions"]); + item.GroupPermissions = Convert.ToUInt32(row["inventoryGroupPermissions"]); + + // new fields + if (!Convert.IsDBNull(row["salePrice"])) + item.SalePrice = Convert.ToInt32(row["salePrice"]); + + if (!Convert.IsDBNull(row["saleType"])) + item.SaleType = Convert.ToByte(row["saleType"]); + + if (!Convert.IsDBNull(row["creationDate"])) + item.CreationDate = Convert.ToInt32(row["creationDate"]); + + if (!Convert.IsDBNull(row["groupID"])) + item.GroupID = new UUID((string)row["groupID"]); + + if (!Convert.IsDBNull(row["groupOwned"])) + item.GroupOwned = Convert.ToBoolean(row["groupOwned"]); + + if (!Convert.IsDBNull(row["Flags"])) + item.Flags = Convert.ToUInt32(row["Flags"]); + + return item; + } + + /// + /// Fill a database row with item data + /// + /// + /// + private static void fillItemRow(DataRow row, InventoryItemBase item) + { + row["UUID"] = item.ID.ToString(); + row["assetID"] = item.AssetID.ToString(); + row["assetType"] = item.AssetType; + row["invType"] = item.InvType; + row["parentFolderID"] = item.Folder.ToString(); + row["avatarID"] = item.Owner.ToString(); + row["creatorsID"] = item.CreatorId.ToString(); + row["inventoryName"] = item.Name; + row["inventoryDescription"] = item.Description; + + row["inventoryNextPermissions"] = item.NextPermissions; + row["inventoryCurrentPermissions"] = item.CurrentPermissions; + row["inventoryBasePermissions"] = item.BasePermissions; + row["inventoryEveryOnePermissions"] = item.EveryOnePermissions; + row["inventoryGroupPermissions"] = item.GroupPermissions; + + // new fields + row["salePrice"] = item.SalePrice; + row["saleType"] = item.SaleType; + row["creationDate"] = item.CreationDate; + row["groupID"] = item.GroupID.ToString(); + row["groupOwned"] = item.GroupOwned; + row["flags"] = item.Flags; + } + + /// + /// Add inventory folder + /// + /// Folder base + /// true=create folder. false=update existing folder + /// nasty + private void addFolder(InventoryFolderBase folder, bool add) + { + lock (ds) + { + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + + DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); + if (inventoryRow == null) + { + if (! add) + m_log.ErrorFormat("Interface Misuse: Attempting to Update non-existant inventory folder: {0}", folder.ID); + + inventoryRow = inventoryFolderTable.NewRow(); + fillFolderRow(inventoryRow, folder); + inventoryFolderTable.Rows.Add(inventoryRow); + } + else + { + if (add) + m_log.ErrorFormat("Interface Misuse: Attempting to Add inventory folder that already exists: {0}", folder.ID); + + fillFolderRow(inventoryRow, folder); + } + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /// + /// Move an inventory folder + /// + /// folder base + private void moveFolder(InventoryFolderBase folder) + { + lock (ds) + { + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + + DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); + if (inventoryRow == null) + { + inventoryRow = inventoryFolderTable.NewRow(); + fillFolderRow(inventoryRow, folder); + inventoryFolderTable.Rows.Add(inventoryRow); + } + else + { + moveFolderRow(inventoryRow, folder); + } + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /// + /// add an item in inventory + /// + /// the item + /// true=add item ; false=update existing item + private void addItem(InventoryItemBase item, bool add) + { + lock (ds) + { + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + + DataRow inventoryRow = inventoryItemTable.Rows.Find(item.ID.ToString()); + if (inventoryRow == null) + { + if (!add) + m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Update non-existant inventory item: {0}", item.ID); + + inventoryRow = inventoryItemTable.NewRow(); + fillItemRow(inventoryRow, item); + inventoryItemTable.Rows.Add(inventoryRow); + } + else + { + if (add) + m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Add inventory item that already exists: {0}", item.ID); + + fillItemRow(inventoryRow, item); + } + + invItemsDa.Update(ds, "inventoryitems"); + + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + + inventoryRow = inventoryFolderTable.Rows.Find(item.Folder.ToString()); + if (inventoryRow != null) //MySQL doesn't throw an exception here, so sqlite shouldn't either. + inventoryRow["version"] = (int)inventoryRow["version"] + 1; + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /// + /// TODO : DataSet commit + /// + public void Shutdown() + { + // TODO: DataSet commit + } + + /// + /// The name of this DB provider + /// + /// Name of DB provider + public string Name + { + get { return "SQLite Inventory Data Interface"; } + } + + /// + /// Returns the version of this DB provider + /// + /// A string containing the DB provider version + public string Version + { + get + { + Module module = GetType().Module; + // string dllName = module.Assembly.ManifestModule.Name; + Version dllVersion = module.Assembly.GetName().Version; + + + return + string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, + dllVersion.Revision); + } + } + + /// + /// Returns a list of inventory items contained within the specified folder + /// + /// The UUID of the target folder + /// A List of InventoryItemBase items + public List getInventoryInFolder(UUID folderID) + { + lock (ds) + { + List retval = new List(); + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + string selectExp = "parentFolderID = '" + folderID + "'"; + DataRow[] rows = inventoryItemTable.Select(selectExp); + foreach (DataRow row in rows) + { + retval.Add(buildItem(row)); + } + + return retval; + } + } + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whos inventory is to be searched + /// A list of folder objects + public List getUserRootFolders(UUID user) + { + return new List(); + } + + // see InventoryItemBase.getUserRootFolder + public InventoryFolderBase getUserRootFolder(UUID user) + { + lock (ds) + { + List folders = new List(); + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + string selectExp = "agentID = '" + user + "' AND parentID = '" + UUID.Zero + "'"; + DataRow[] rows = inventoryFolderTable.Select(selectExp); + foreach (DataRow row in rows) + { + folders.Add(buildFolder(row)); + } + + // There should only ever be one root folder for a user. However, if there's more + // than one we'll simply use the first one rather than failing. It would be even + // nicer to print some message to this effect, but this feels like it's too low a + // to put such a message out, and it's too minor right now to spare the time to + // suitably refactor. + if (folders.Count > 0) + { + return folders[0]; + } + + return null; + } + } + + /// + /// Append a list of all the child folders of a parent folder + /// + /// list where folders will be appended + /// ID of parent + protected void getInventoryFolders(ref List folders, UUID parentID) + { + lock (ds) + { + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + string selectExp = "parentID = '" + parentID + "'"; + DataRow[] rows = inventoryFolderTable.Select(selectExp); + foreach (DataRow row in rows) + { + folders.Add(buildFolder(row)); + } + + } + } + + /// + /// Returns a list of inventory folders contained in the folder 'parentID' + /// + /// The folder to get subfolders for + /// A list of inventory folders + public List getInventoryFolders(UUID parentID) + { + List folders = new List(); + getInventoryFolders(ref folders, parentID); + return folders; + } + + /// + /// See IInventoryDataPlugin + /// + /// + /// + public List getFolderHierarchy(UUID parentID) + { + /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one + * - We will only need to hit the database twice instead of n times. + * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned + * by the same person, each user only has 1 inventory heirarchy + * - The returned list is not ordered, instead of breadth-first ordered + There are basically 2 usage cases for getFolderHeirarchy: + 1) Getting the user's entire inventory heirarchy when they log in + 2) Finding a subfolder heirarchy to delete when emptying the trash. + This implementation will pull all inventory folders from the database, and then prune away any folder that + is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the + database than to make n requests. This pays off only if requested heirarchy is large. + By making this choice, we are making the worst case better at the cost of making the best case worse + - Francis + */ + + List folders = new List(); + DataRow[] folderRows = null, parentRow; + InventoryFolderBase parentFolder = null; + lock (ds) + { + /* Fetch the parent folder from the database to determine the agent ID. + * Then fetch all inventory folders for that agent from the agent ID. + */ + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + string selectExp = "UUID = '" + parentID + "'"; + parentRow = inventoryFolderTable.Select(selectExp); // Assume at most 1 result + if (parentRow.GetLength(0) >= 1) // No result means parent folder does not exist + { + parentFolder = buildFolder(parentRow[0]); + UUID agentID = parentFolder.Owner; + selectExp = "agentID = '" + agentID + "'"; + folderRows = inventoryFolderTable.Select(selectExp); + } + + if (folderRows != null && folderRows.GetLength(0) >= 1) // No result means parent folder does not exist + { // or has no children + /* if we're querying the root folder, just return an unordered list of all folders in the user's + * inventory + */ + if (parentFolder.ParentID == UUID.Zero) + { + foreach (DataRow row in folderRows) + { + InventoryFolderBase curFolder = buildFolder(row); + if (curFolder.ID != parentID) // Return all folders except the parent folder of heirarchy + folders.Add(buildFolder(row)); + } + } // If requesting root folder + /* else we are querying a non-root folder. We currently have a list of all of the user's folders, + * we must construct a list of all folders in the heirarchy below parentID. + * Our first step will be to construct a hash table of all folders, indexed by parent ID. + * Once we have constructed the hash table, we will do a breadth-first traversal on the tree using the + * hash table to find child folders. + */ + else + { // Querying a non-root folder + + // Build a hash table of all user's inventory folders, indexed by each folder's parent ID + Dictionary> hashtable = + new Dictionary>(folderRows.GetLength(0)); + + foreach (DataRow row in folderRows) + { + InventoryFolderBase curFolder = buildFolder(row); + if (curFolder.ParentID != UUID.Zero) // Discard root of tree - not needed + { + if (hashtable.ContainsKey(curFolder.ParentID)) + { + // Current folder already has a sibling - append to sibling list + hashtable[curFolder.ParentID].Add(curFolder); + } + else + { + List siblingList = new List(); + siblingList.Add(curFolder); + // Current folder has no known (yet) siblings + hashtable.Add(curFolder.ParentID, siblingList); + } + } + } // For all inventory folders + + // Note: Could release the ds lock here - we don't access folderRows or the database anymore. + // This is somewhat of a moot point as the callers of this function usually lock db anyways. + + if (hashtable.ContainsKey(parentID)) // if requested folder does have children + folders.AddRange(hashtable[parentID]); + + // BreadthFirstSearch build inventory tree **Note: folders.Count is *not* static + for (int i = 0; i < folders.Count; i++) + if (hashtable.ContainsKey(folders[i].ID)) + folders.AddRange(hashtable[folders[i].ID]); + + } // if requesting a subfolder heirarchy + } // if folder parentID exists and has children + } // lock ds + return folders; + } + + /// + /// Returns an inventory item by its UUID + /// + /// The UUID of the item to be returned + /// A class containing item information + public InventoryItemBase getInventoryItem(UUID item) + { + lock (ds) + { + DataRow row = ds.Tables["inventoryitems"].Rows.Find(item.ToString()); + if (row != null) + { + return buildItem(row); + } + else + { + return null; + } + } + } + + /// + /// Returns a specified inventory folder by its UUID + /// + /// The UUID of the folder to be returned + /// A class containing folder information + public InventoryFolderBase getInventoryFolder(UUID folder) + { + // TODO: Deep voodoo here. If you enable this code then + // multi region breaks. No idea why, but I figured it was + // better to leave multi region at this point. It does mean + // that you don't get to see system textures why creating + // clothes and the like. :( + lock (ds) + { + DataRow row = ds.Tables["inventoryfolders"].Rows.Find(folder.ToString()); + if (row != null) + { + return buildFolder(row); + } + else + { + return null; + } + } + } + + /// + /// Creates a new inventory item based on item + /// + /// The item to be created + public void addInventoryItem(InventoryItemBase item) + { + addItem(item, true); + } + + /// + /// Updates an inventory item with item (updates based on ID) + /// + /// The updated item + public void updateInventoryItem(InventoryItemBase item) + { + addItem(item, false); + } + + /// + /// Delete an inventory item + /// + /// The item UUID + public void deleteInventoryItem(UUID itemID) + { + lock (ds) + { + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + + DataRow inventoryRow = inventoryItemTable.Rows.Find(itemID.ToString()); + if (inventoryRow != null) + { + inventoryRow.Delete(); + } + + invItemsDa.Update(ds, "inventoryitems"); + } + } + + public InventoryItemBase queryInventoryItem(UUID itemID) + { + return getInventoryItem(itemID); + } + + public InventoryFolderBase queryInventoryFolder(UUID folderID) + { + return getInventoryFolder(folderID); + } + + /// + /// Delete all items in the specified folder + /// + /// id of the folder, whose item content should be deleted + /// this is horribly inefficient, but I don't want to ruin the overall structure of this implementation + private void deleteItemsInFolder(UUID folderId) + { + List items = getInventoryInFolder(folderId); + + foreach (InventoryItemBase i in items) + deleteInventoryItem(i.ID); + } + + /// + /// Adds a new folder specified by folder + /// + /// The inventory folder + public void addInventoryFolder(InventoryFolderBase folder) + { + addFolder(folder, true); + } + + /// + /// Updates a folder based on its ID with folder + /// + /// The inventory folder + public void updateInventoryFolder(InventoryFolderBase folder) + { + addFolder(folder, false); + } + + /// + /// Moves a folder based on its ID with folder + /// + /// The inventory folder + public void moveInventoryFolder(InventoryFolderBase folder) + { + moveFolder(folder); + } + + /// + /// Delete a folder + /// + /// + /// This will clean-up any child folders and child items as well + /// + /// the folder UUID + public void deleteInventoryFolder(UUID folderID) + { + lock (ds) + { + List subFolders = getFolderHierarchy(folderID); + + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + DataRow inventoryRow; + + //Delete all sub-folders + foreach (InventoryFolderBase f in subFolders) + { + inventoryRow = inventoryFolderTable.Rows.Find(f.ID.ToString()); + if (inventoryRow != null) + { + deleteItemsInFolder(f.ID); + inventoryRow.Delete(); + } + } + + //Delete the actual row + inventoryRow = inventoryFolderTable.Rows.Find(folderID.ToString()); + if (inventoryRow != null) + { + deleteItemsInFolder(folderID); + inventoryRow.Delete(); + } + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /*********************************************************************** + * + * Data Table definitions + * + **********************************************************************/ + + /// + /// Create the "inventoryitems" table + /// + private static DataTable createInventoryItemsTable() + { + DataTable inv = new DataTable("inventoryitems"); + + createCol(inv, "UUID", typeof (String)); //inventoryID + createCol(inv, "assetID", typeof (String)); + createCol(inv, "assetType", typeof (Int32)); + createCol(inv, "invType", typeof (Int32)); + createCol(inv, "parentFolderID", typeof (String)); + createCol(inv, "avatarID", typeof (String)); + createCol(inv, "creatorsID", typeof (String)); + + createCol(inv, "inventoryName", typeof (String)); + createCol(inv, "inventoryDescription", typeof (String)); + // permissions + createCol(inv, "inventoryNextPermissions", typeof (Int32)); + createCol(inv, "inventoryCurrentPermissions", typeof (Int32)); + createCol(inv, "inventoryBasePermissions", typeof (Int32)); + createCol(inv, "inventoryEveryOnePermissions", typeof (Int32)); + createCol(inv, "inventoryGroupPermissions", typeof (Int32)); + + // sale info + createCol(inv, "salePrice", typeof(Int32)); + createCol(inv, "saleType", typeof(Byte)); + + // creation date + createCol(inv, "creationDate", typeof(Int32)); + + // group info + createCol(inv, "groupID", typeof(String)); + createCol(inv, "groupOwned", typeof(Boolean)); + + // Flags + createCol(inv, "flags", typeof(UInt32)); + + inv.PrimaryKey = new DataColumn[] { inv.Columns["UUID"] }; + return inv; + } + + /// + /// Creates the "inventoryfolders" table + /// + /// + private static DataTable createInventoryFoldersTable() + { + DataTable fol = new DataTable("inventoryfolders"); + + createCol(fol, "UUID", typeof (String)); //folderID + createCol(fol, "name", typeof (String)); + createCol(fol, "agentID", typeof (String)); + createCol(fol, "parentID", typeof (String)); + createCol(fol, "type", typeof (Int32)); + createCol(fol, "version", typeof (Int32)); + + fol.PrimaryKey = new DataColumn[] {fol.Columns["UUID"]}; + return fol; + } + + /// + /// + /// + /// + /// + private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + lock (ds) + { + da.InsertCommand = createInsertCommand("inventoryitems", ds.Tables["inventoryitems"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("inventoryitems", "UUID=:UUID", ds.Tables["inventoryitems"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from inventoryitems where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + } + + /// + /// + /// + /// + /// + private void setupFoldersCommands(SqliteDataAdapter da, SqliteConnection conn) + { + lock (ds) + { + da.InsertCommand = createInsertCommand("inventoryfolders", ds.Tables["inventoryfolders"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("inventoryfolders", "UUID=:UUID", ds.Tables["inventoryfolders"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from inventoryfolders where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + } + + /// + /// + /// + /// + /// + private static InventoryFolderBase buildFolder(DataRow row) + { + InventoryFolderBase folder = new InventoryFolderBase(); + folder.ID = new UUID((string) row["UUID"]); + folder.Name = (string) row["name"]; + folder.Owner = new UUID((string) row["agentID"]); + folder.ParentID = new UUID((string) row["parentID"]); + folder.Type = Convert.ToInt16(row["type"]); + folder.Version = Convert.ToUInt16(row["version"]); + return folder; + } + + /// + /// + /// + /// + /// + private static void fillFolderRow(DataRow row, InventoryFolderBase folder) + { + row["UUID"] = folder.ID.ToString(); + row["name"] = folder.Name; + row["agentID"] = folder.Owner.ToString(); + row["parentID"] = folder.ParentID.ToString(); + row["type"] = folder.Type; + row["version"] = folder.Version; + } + + /// + /// + /// + /// + /// + private static void moveFolderRow(DataRow row, InventoryFolderBase folder) + { + row["UUID"] = folder.ID.ToString(); + row["parentID"] = folder.ParentID.ToString(); + } + + public List fetchActiveGestures (UUID avatarID) + { + lock (ds) + { + List items = new List(); + + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + string selectExp + = "avatarID = '" + avatarID + "' AND assetType = " + (int)AssetType.Gesture + " AND flags = 1"; + //m_log.DebugFormat("[SQL]: sql = " + selectExp); + DataRow[] rows = inventoryItemTable.Select(selectExp); + foreach (DataRow row in rows) + { + items.Add(buildItem(row)); + } + return items; + } + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs new file mode 100644 index 0000000000..d2ba9ae298 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs @@ -0,0 +1,2264 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using log4net; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Data.SQLite +{ + /// + /// A RegionData Interface to the SQLite database + /// + public class SQLiteRegionData : IRegionDataStore + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const string primSelect = "select * from prims"; + private const string shapeSelect = "select * from primshapes"; + private const string itemsSelect = "select * from primitems"; + private const string terrainSelect = "select * from terrain limit 1"; + private const string landSelect = "select * from land"; + private const string landAccessListSelect = "select distinct * from landaccesslist"; + private const string regionbanListSelect = "select * from regionban"; + private const string regionSettingsSelect = "select * from regionsettings"; + + private DataSet ds; + private SqliteDataAdapter primDa; + private SqliteDataAdapter shapeDa; + private SqliteDataAdapter itemsDa; + private SqliteDataAdapter terrainDa; + private SqliteDataAdapter landDa; + private SqliteDataAdapter landAccessListDa; + private SqliteDataAdapter regionSettingsDa; + + private SqliteConnection m_conn; + + private String m_connectionString; + + // Temporary attribute while this is experimental + + /*********************************************************************** + * + * Public Interface Functions + * + **********************************************************************/ + + /// + /// See IRegionDataStore + /// + /// Initialises RegionData Interface + /// Loads and initialises a new SQLite connection and maintains it. + /// + /// + /// the connection string + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + + ds = new DataSet(); + + m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); + m_conn = new SqliteConnection(m_connectionString); + m_conn.Open(); + + + + SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); + primDa = new SqliteDataAdapter(primSelectCmd); + // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); + + SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); + shapeDa = new SqliteDataAdapter(shapeSelectCmd); + // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); + + SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); + itemsDa = new SqliteDataAdapter(itemsSelectCmd); + + SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); + terrainDa = new SqliteDataAdapter(terrainSelectCmd); + + SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); + landDa = new SqliteDataAdapter(landSelectCmd); + + SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); + landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); + + SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); + regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); + // This actually does the roll forward assembly stuff + Assembly assem = GetType().Assembly; + Migration m = new Migration(m_conn, assem, "RegionStore"); + m.Update(); + + lock (ds) + { + ds.Tables.Add(createPrimTable()); + setupPrimCommands(primDa, m_conn); + primDa.Fill(ds.Tables["prims"]); + + ds.Tables.Add(createShapeTable()); + setupShapeCommands(shapeDa, m_conn); + + ds.Tables.Add(createItemsTable()); + setupItemsCommands(itemsDa, m_conn); + itemsDa.Fill(ds.Tables["primitems"]); + + ds.Tables.Add(createTerrainTable()); + setupTerrainCommands(terrainDa, m_conn); + + ds.Tables.Add(createLandTable()); + setupLandCommands(landDa, m_conn); + + ds.Tables.Add(createLandAccessListTable()); + setupLandAccessCommands(landAccessListDa, m_conn); + + ds.Tables.Add(createRegionSettingsTable()); + + setupRegionSettingsCommands(regionSettingsDa, m_conn); + + // WORKAROUND: This is a work around for sqlite on + // windows, which gets really unhappy with blob columns + // that have no sample data in them. At some point we + // need to actually find a proper way to handle this. + try + { + shapeDa.Fill(ds.Tables["primshapes"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on primshapes table"); + } + + try + { + terrainDa.Fill(ds.Tables["terrain"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on terrain table"); + } + + try + { + landDa.Fill(ds.Tables["land"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on land table"); + } + + try + { + landAccessListDa.Fill(ds.Tables["landaccesslist"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); + } + + try + { + regionSettingsDa.Fill(ds.Tables["regionsettings"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); + } + return; + } + } + + public void Dispose() + { + if (m_conn != null) + { + m_conn.Close(); + m_conn = null; + } + if (ds != null) + { + ds.Dispose(); + ds = null; + } + if (primDa != null) + { + primDa.Dispose(); + primDa = null; + } + if (shapeDa != null) + { + shapeDa.Dispose(); + shapeDa = null; + } + if (itemsDa != null) + { + itemsDa.Dispose(); + itemsDa = null; + } + if (terrainDa != null) + { + terrainDa.Dispose(); + terrainDa = null; + } + if (landDa != null) + { + landDa.Dispose(); + landDa = null; + } + if (landAccessListDa != null) + { + landAccessListDa.Dispose(); + landAccessListDa = null; + } + if (regionSettingsDa != null) + { + regionSettingsDa.Dispose(); + regionSettingsDa = null; + } + } + + public void StoreRegionSettings(RegionSettings rs) + { + lock (ds) + { + DataTable regionsettings = ds.Tables["regionsettings"]; + + DataRow settingsRow = regionsettings.Rows.Find(rs.RegionUUID.ToString()); + if (settingsRow == null) + { + settingsRow = regionsettings.NewRow(); + fillRegionSettingsRow(settingsRow, rs); + regionsettings.Rows.Add(settingsRow); + } + else + { + fillRegionSettingsRow(settingsRow, rs); + } + + Commit(); + } + } + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + //This connector doesn't support the windlight module yet + //Return default LL windlight settings + return new RegionLightShareData(); + } + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + //This connector doesn't support the windlight module yet + } + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + lock (ds) + { + DataTable regionsettings = ds.Tables["regionsettings"]; + + string searchExp = "regionUUID = '" + regionUUID.ToString() + "'"; + DataRow[] rawsettings = regionsettings.Select(searchExp); + if (rawsettings.Length == 0) + { + RegionSettings rs = new RegionSettings(); + rs.RegionUUID = regionUUID; + rs.OnSave += StoreRegionSettings; + + StoreRegionSettings(rs); + + return rs; + } + DataRow row = rawsettings[0]; + + RegionSettings newSettings = buildRegionSettings(row); + newSettings.OnSave += StoreRegionSettings; + + return newSettings; + } + } + + /// + /// Adds an object into region storage + /// + /// the object + /// the region UUID + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + uint flags = obj.RootPart.GetEffectiveObjectFlags(); + + // Eligibility check + // + if ((flags & (uint)PrimFlags.Temporary) != 0) + return; + if ((flags & (uint)PrimFlags.TemporaryOnRez) != 0) + return; + + lock (ds) + { + foreach (SceneObjectPart prim in obj.Children.Values) + { +// m_log.Info("[REGION DB]: Adding obj: " + obj.UUID + " to region: " + regionUUID); + addPrim(prim, obj.UUID, regionUUID); + } + } + + Commit(); + // m_log.Info("[Dump of prims]: " + ds.GetXml()); + } + + /// + /// Removes an object from region storage + /// + /// the object + /// the region UUID + public void RemoveObject(UUID obj, UUID regionUUID) + { + // m_log.InfoFormat("[REGION DB]: Removing obj: {0} from region: {1}", obj.Guid, regionUUID); + + DataTable prims = ds.Tables["prims"]; + DataTable shapes = ds.Tables["primshapes"]; + + string selectExp = "SceneGroupID = '" + obj + "' and RegionUUID = '" + regionUUID + "'"; + lock (ds) + { + DataRow[] primRows = prims.Select(selectExp); + foreach (DataRow row in primRows) + { + // Remove shape rows + UUID uuid = new UUID((string) row["UUID"]); + DataRow shapeRow = shapes.Rows.Find(uuid.ToString()); + if (shapeRow != null) + { + shapeRow.Delete(); + } + + RemoveItems(uuid); + + // Remove prim row + row.Delete(); + } + } + + Commit(); + } + + /// + /// Remove all persisted items of the given prim. + /// The caller must acquire the necessrary synchronization locks and commit or rollback changes. + /// + /// The item UUID + private void RemoveItems(UUID uuid) + { + DataTable items = ds.Tables["primitems"]; + + String sql = String.Format("primID = '{0}'", uuid); + DataRow[] itemRows = items.Select(sql); + + foreach (DataRow itemRow in itemRows) + { + itemRow.Delete(); + } + } + + /// + /// Load persisted objects from region storage. + /// + /// The region UUID + /// List of loaded groups + public List LoadObjects(UUID regionUUID) + { + Dictionary createdObjects = new Dictionary(); + + List retvals = new List(); + + DataTable prims = ds.Tables["prims"]; + DataTable shapes = ds.Tables["primshapes"]; + + string byRegion = "RegionUUID = '" + regionUUID + "'"; + + lock (ds) + { + DataRow[] primsForRegion = prims.Select(byRegion); + m_log.Info("[REGION DB]: Loaded " + primsForRegion.Length + " prims for region: " + regionUUID); + + // First, create all groups + foreach (DataRow primRow in primsForRegion) + { + try + { + SceneObjectPart prim = null; + + string uuid = (string) primRow["UUID"]; + string objID = (string) primRow["SceneGroupID"]; + + if (uuid == objID) //is new SceneObjectGroup ? + { + prim = buildPrim(primRow); + DataRow shapeRow = shapes.Rows.Find(prim.UUID.ToString()); + if (shapeRow != null) + { + prim.Shape = buildShape(shapeRow); + } + else + { + m_log.Info( + "[REGION DB]: No shape found for prim in storage, so setting default box shape"); + prim.Shape = PrimitiveBaseShape.Default; + } + + SceneObjectGroup group = new SceneObjectGroup(prim); + createdObjects.Add(group.UUID, group); + retvals.Add(group); + LoadItems(prim); + } + } + catch (Exception e) + { + m_log.Error("[REGION DB]: Failed create prim object in new group, exception and data follows"); + m_log.Info("[REGION DB]: " + e.ToString()); + foreach (DataColumn col in prims.Columns) + { + m_log.Info("[REGION DB]: Col: " + col.ColumnName + " => " + primRow[col]); + } + } + } + + // Now fill the groups with part data + foreach (DataRow primRow in primsForRegion) + { + try + { + SceneObjectPart prim = null; + + string uuid = (string) primRow["UUID"]; + string objID = (string) primRow["SceneGroupID"]; + if (uuid != objID) //is new SceneObjectGroup ? + { + prim = buildPrim(primRow); + DataRow shapeRow = shapes.Rows.Find(prim.UUID.ToString()); + if (shapeRow != null) + { + prim.Shape = buildShape(shapeRow); + } + else + { + m_log.Warn( + "[REGION DB]: No shape found for prim in storage, so setting default box shape"); + prim.Shape = PrimitiveBaseShape.Default; + } + + createdObjects[new UUID(objID)].AddPart(prim); + LoadItems(prim); + } + } + catch (Exception e) + { + m_log.Error("[REGION DB]: Failed create prim object in group, exception and data follows"); + m_log.Info("[REGION DB]: " + e.ToString()); + foreach (DataColumn col in prims.Columns) + { + m_log.Info("[REGION DB]: Col: " + col.ColumnName + " => " + primRow[col]); + } + } + } + } + return retvals; + } + + /// + /// Load in a prim's persisted inventory. + /// + /// the prim + private void LoadItems(SceneObjectPart prim) + { + //m_log.DebugFormat("[DATASTORE]: Loading inventory for {0}, {1}", prim.Name, prim.UUID); + + DataTable dbItems = ds.Tables["primitems"]; + String sql = String.Format("primID = '{0}'", prim.UUID.ToString()); + DataRow[] dbItemRows = dbItems.Select(sql); + IList inventory = new List(); + + foreach (DataRow row in dbItemRows) + { + TaskInventoryItem item = buildItem(row); + inventory.Add(item); + + //m_log.DebugFormat("[DATASTORE]: Restored item {0}, {1}", item.Name, item.ItemID); + } + + prim.Inventory.RestoreInventoryItems(inventory); + } + + /// + /// Store a terrain revision in region storage + /// + /// terrain heightfield + /// region UUID + public void StoreTerrain(double[,] ter, UUID regionID) + { + lock (ds) + { + int revision = Util.UnixTimeSinceEpoch(); + + // This is added to get rid of the infinitely growing + // terrain databases which negatively impact on SQLite + // over time. Before reenabling this feature there + // needs to be a limitter put on the number of + // revisions in the database, as this old + // implementation is a DOS attack waiting to happen. + + using ( + SqliteCommand cmd = + new SqliteCommand("delete from terrain where RegionUUID=:RegionUUID and Revision <= :Revision", + m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Revision", revision)); + cmd.ExecuteNonQuery(); + } + + // the following is an work around for .NET. The perf + // issues associated with it aren't as bad as you think. + m_log.Info("[REGION DB]: Storing terrain revision r" + revision.ToString()); + String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" + + " values(:RegionUUID, :Revision, :Heightfield)"; + + using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Revision", revision)); + cmd.Parameters.Add(new SqliteParameter(":Heightfield", serializeTerrain(ter))); + cmd.ExecuteNonQuery(); + } + } + } + + /// + /// Load the latest terrain revision from region storage + /// + /// the region UUID + /// Heightfield data + public double[,] LoadTerrain(UUID regionID) + { + lock (ds) + { + double[,] terret = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; + terret.Initialize(); + + String sql = "select RegionUUID, Revision, Heightfield from terrain" + + " where RegionUUID=:RegionUUID order by Revision desc"; + + using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); + + using (IDataReader row = cmd.ExecuteReader()) + { + int rev = 0; + if (row.Read()) + { + // TODO: put this into a function + using (MemoryStream str = new MemoryStream((byte[])row["Heightfield"])) + { + using (BinaryReader br = new BinaryReader(str)) + { + for (int x = 0; x < (int)Constants.RegionSize; x++) + { + for (int y = 0; y < (int)Constants.RegionSize; y++) + { + terret[x, y] = br.ReadDouble(); + } + } + } + } + rev = (int) row["Revision"]; + } + else + { + m_log.Info("[REGION DB]: No terrain found for region"); + return null; + } + + m_log.Info("[REGION DB]: Loaded terrain revision r" + rev.ToString()); + } + } + return terret; + } + } + + /// + /// + /// + /// + public void RemoveLandObject(UUID globalID) + { + lock (ds) + { + // Can't use blanket SQL statements when using SqlAdapters unless you re-read the data into the adapter + // after you're done. + // replaced below code with the SqliteAdapter version. + //using (SqliteCommand cmd = new SqliteCommand("delete from land where UUID=:UUID", m_conn)) + //{ + // cmd.Parameters.Add(new SqliteParameter(":UUID", globalID.ToString())); + // cmd.ExecuteNonQuery(); + //} + + //using (SqliteCommand cmd = new SqliteCommand("delete from landaccesslist where LandUUID=:UUID", m_conn)) + //{ + // cmd.Parameters.Add(new SqliteParameter(":UUID", globalID.ToString())); + // cmd.ExecuteNonQuery(); + //} + + DataTable land = ds.Tables["land"]; + DataTable landaccesslist = ds.Tables["landaccesslist"]; + DataRow landRow = land.Rows.Find(globalID.ToString()); + if (landRow != null) + { + land.Rows.Remove(landRow); + } + List rowsToDelete = new List(); + foreach (DataRow rowToCheck in landaccesslist.Rows) + { + if (rowToCheck["LandUUID"].ToString() == globalID.ToString()) + rowsToDelete.Add(rowToCheck); + } + for (int iter = 0; iter < rowsToDelete.Count; iter++) + { + landaccesslist.Rows.Remove(rowsToDelete[iter]); + } + + + } + Commit(); + } + + /// + /// + /// + /// + public void StoreLandObject(ILandObject parcel) + { + lock (ds) + { + DataTable land = ds.Tables["land"]; + DataTable landaccesslist = ds.Tables["landaccesslist"]; + + DataRow landRow = land.Rows.Find(parcel.LandData.GlobalID.ToString()); + if (landRow == null) + { + landRow = land.NewRow(); + fillLandRow(landRow, parcel.LandData, parcel.RegionUUID); + land.Rows.Add(landRow); + } + else + { + fillLandRow(landRow, parcel.LandData, parcel.RegionUUID); + } + + // I know this caused someone issues before, but OpenSim is unusable if we leave this stuff around + //using (SqliteCommand cmd = new SqliteCommand("delete from landaccesslist where LandUUID=:LandUUID", m_conn)) + //{ + // cmd.Parameters.Add(new SqliteParameter(":LandUUID", parcel.LandData.GlobalID.ToString())); + // cmd.ExecuteNonQuery(); + +// } + + // This is the slower.. but more appropriate thing to do + + // We can't modify the table with direct queries before calling Commit() and re-filling them. + List rowsToDelete = new List(); + foreach (DataRow rowToCheck in landaccesslist.Rows) + { + if (rowToCheck["LandUUID"].ToString() == parcel.LandData.GlobalID.ToString()) + rowsToDelete.Add(rowToCheck); + } + for (int iter = 0; iter < rowsToDelete.Count; iter++) + { + landaccesslist.Rows.Remove(rowsToDelete[iter]); + } + rowsToDelete.Clear(); + foreach (ParcelManager.ParcelAccessEntry entry in parcel.LandData.ParcelAccessList) + { + DataRow newAccessRow = landaccesslist.NewRow(); + fillLandAccessRow(newAccessRow, entry, parcel.LandData.GlobalID); + landaccesslist.Rows.Add(newAccessRow); + } + } + + Commit(); + } + + /// + /// + /// + /// + /// + public List LoadLandObjects(UUID regionUUID) + { + List landDataForRegion = new List(); + lock (ds) + { + DataTable land = ds.Tables["land"]; + DataTable landaccesslist = ds.Tables["landaccesslist"]; + string searchExp = "RegionUUID = '" + regionUUID + "'"; + DataRow[] rawDataForRegion = land.Select(searchExp); + foreach (DataRow rawDataLand in rawDataForRegion) + { + LandData newLand = buildLandData(rawDataLand); + string accessListSearchExp = "LandUUID = '" + newLand.GlobalID + "'"; + DataRow[] rawDataForLandAccessList = landaccesslist.Select(accessListSearchExp); + foreach (DataRow rawDataLandAccess in rawDataForLandAccessList) + { + newLand.ParcelAccessList.Add(buildLandAccessData(rawDataLandAccess)); + } + + landDataForRegion.Add(newLand); + } + } + return landDataForRegion; + } + + /// + /// + /// + public void Commit() + { + lock (ds) + { + primDa.Update(ds, "prims"); + shapeDa.Update(ds, "primshapes"); + + itemsDa.Update(ds, "primitems"); + + terrainDa.Update(ds, "terrain"); + landDa.Update(ds, "land"); + landAccessListDa.Update(ds, "landaccesslist"); + try + { + regionSettingsDa.Update(ds, "regionsettings"); + } + catch (SqliteExecutionException SqlEx) + { + if (SqlEx.Message.Contains("logic error")) + { + throw new Exception( + "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", + SqlEx); + } + else + { + throw SqlEx; + } + } + ds.AcceptChanges(); + } + } + + /// + /// See + /// + public void Shutdown() + { + Commit(); + } + + /*********************************************************************** + * + * Database Definition Functions + * + * This should be db agnostic as we define them in ADO.NET terms + * + **********************************************************************/ + + /// + /// + /// + /// + /// + /// + private static void createCol(DataTable dt, string name, Type type) + { + DataColumn col = new DataColumn(name, type); + dt.Columns.Add(col); + } + + /// + /// Creates the "terrain" table + /// + /// terrain table DataTable + private static DataTable createTerrainTable() + { + DataTable terrain = new DataTable("terrain"); + + createCol(terrain, "RegionUUID", typeof (String)); + createCol(terrain, "Revision", typeof (Int32)); + createCol(terrain, "Heightfield", typeof (Byte[])); + + return terrain; + } + + /// + /// Creates the "prims" table + /// + /// prim table DataTable + private static DataTable createPrimTable() + { + DataTable prims = new DataTable("prims"); + + createCol(prims, "UUID", typeof (String)); + createCol(prims, "RegionUUID", typeof (String)); + createCol(prims, "CreationDate", typeof (Int32)); + createCol(prims, "Name", typeof (String)); + createCol(prims, "SceneGroupID", typeof (String)); + // various text fields + createCol(prims, "Text", typeof (String)); + createCol(prims, "ColorR", typeof (Int32)); + createCol(prims, "ColorG", typeof (Int32)); + createCol(prims, "ColorB", typeof (Int32)); + createCol(prims, "ColorA", typeof (Int32)); + createCol(prims, "Description", typeof (String)); + createCol(prims, "SitName", typeof (String)); + createCol(prims, "TouchName", typeof (String)); + // permissions + createCol(prims, "ObjectFlags", typeof (Int32)); + createCol(prims, "CreatorID", typeof (String)); + createCol(prims, "OwnerID", typeof (String)); + createCol(prims, "GroupID", typeof (String)); + createCol(prims, "LastOwnerID", typeof (String)); + createCol(prims, "OwnerMask", typeof (Int32)); + createCol(prims, "NextOwnerMask", typeof (Int32)); + createCol(prims, "GroupMask", typeof (Int32)); + createCol(prims, "EveryoneMask", typeof (Int32)); + createCol(prims, "BaseMask", typeof (Int32)); + // vectors + createCol(prims, "PositionX", typeof (Double)); + createCol(prims, "PositionY", typeof (Double)); + createCol(prims, "PositionZ", typeof (Double)); + createCol(prims, "GroupPositionX", typeof (Double)); + createCol(prims, "GroupPositionY", typeof (Double)); + createCol(prims, "GroupPositionZ", typeof (Double)); + createCol(prims, "VelocityX", typeof (Double)); + createCol(prims, "VelocityY", typeof (Double)); + createCol(prims, "VelocityZ", typeof (Double)); + createCol(prims, "AngularVelocityX", typeof (Double)); + createCol(prims, "AngularVelocityY", typeof (Double)); + createCol(prims, "AngularVelocityZ", typeof (Double)); + createCol(prims, "AccelerationX", typeof (Double)); + createCol(prims, "AccelerationY", typeof (Double)); + createCol(prims, "AccelerationZ", typeof (Double)); + // quaternions + createCol(prims, "RotationX", typeof (Double)); + createCol(prims, "RotationY", typeof (Double)); + createCol(prims, "RotationZ", typeof (Double)); + createCol(prims, "RotationW", typeof (Double)); + + // sit target + createCol(prims, "SitTargetOffsetX", typeof (Double)); + createCol(prims, "SitTargetOffsetY", typeof (Double)); + createCol(prims, "SitTargetOffsetZ", typeof (Double)); + + createCol(prims, "SitTargetOrientW", typeof (Double)); + createCol(prims, "SitTargetOrientX", typeof (Double)); + createCol(prims, "SitTargetOrientY", typeof (Double)); + createCol(prims, "SitTargetOrientZ", typeof (Double)); + + createCol(prims, "PayPrice", typeof(Int32)); + createCol(prims, "PayButton1", typeof(Int32)); + createCol(prims, "PayButton2", typeof(Int32)); + createCol(prims, "PayButton3", typeof(Int32)); + createCol(prims, "PayButton4", typeof(Int32)); + + createCol(prims, "LoopedSound", typeof(String)); + createCol(prims, "LoopedSoundGain", typeof(Double)); + createCol(prims, "TextureAnimation", typeof(String)); + createCol(prims, "ParticleSystem", typeof(String)); + + createCol(prims, "OmegaX", typeof(Double)); + createCol(prims, "OmegaY", typeof(Double)); + createCol(prims, "OmegaZ", typeof(Double)); + + createCol(prims, "CameraEyeOffsetX", typeof(Double)); + createCol(prims, "CameraEyeOffsetY", typeof(Double)); + createCol(prims, "CameraEyeOffsetZ", typeof(Double)); + + createCol(prims, "CameraAtOffsetX", typeof(Double)); + createCol(prims, "CameraAtOffsetY", typeof(Double)); + createCol(prims, "CameraAtOffsetZ", typeof(Double)); + + createCol(prims, "ForceMouselook", typeof(Int16)); + + createCol(prims, "ScriptAccessPin", typeof(Int32)); + + createCol(prims, "AllowedDrop", typeof(Int16)); + createCol(prims, "DieAtEdge", typeof(Int16)); + + createCol(prims, "SalePrice", typeof(Int32)); + createCol(prims, "SaleType", typeof(Int16)); + + // click action + createCol(prims, "ClickAction", typeof (Byte)); + + createCol(prims, "Material", typeof(Byte)); + + createCol(prims, "CollisionSound", typeof(String)); + createCol(prims, "CollisionSoundVolume", typeof(Double)); + + createCol(prims, "VolumeDetect", typeof(Int16)); + + // Add in contraints + prims.PrimaryKey = new DataColumn[] {prims.Columns["UUID"]}; + + return prims; + } + + /// + /// Creates "primshapes" table + /// + /// shape table DataTable + private static DataTable createShapeTable() + { + DataTable shapes = new DataTable("primshapes"); + createCol(shapes, "UUID", typeof (String)); + // shape is an enum + createCol(shapes, "Shape", typeof (Int32)); + // vectors + createCol(shapes, "ScaleX", typeof (Double)); + createCol(shapes, "ScaleY", typeof (Double)); + createCol(shapes, "ScaleZ", typeof (Double)); + // paths + createCol(shapes, "PCode", typeof (Int32)); + createCol(shapes, "PathBegin", typeof (Int32)); + createCol(shapes, "PathEnd", typeof (Int32)); + createCol(shapes, "PathScaleX", typeof (Int32)); + createCol(shapes, "PathScaleY", typeof (Int32)); + createCol(shapes, "PathShearX", typeof (Int32)); + createCol(shapes, "PathShearY", typeof (Int32)); + createCol(shapes, "PathSkew", typeof (Int32)); + createCol(shapes, "PathCurve", typeof (Int32)); + createCol(shapes, "PathRadiusOffset", typeof (Int32)); + createCol(shapes, "PathRevolutions", typeof (Int32)); + createCol(shapes, "PathTaperX", typeof (Int32)); + createCol(shapes, "PathTaperY", typeof (Int32)); + createCol(shapes, "PathTwist", typeof (Int32)); + createCol(shapes, "PathTwistBegin", typeof (Int32)); + // profile + createCol(shapes, "ProfileBegin", typeof (Int32)); + createCol(shapes, "ProfileEnd", typeof (Int32)); + createCol(shapes, "ProfileCurve", typeof (Int32)); + createCol(shapes, "ProfileHollow", typeof (Int32)); + createCol(shapes, "State", typeof(Int32)); + // text TODO: this isn't right, but I'm not sure the right + // way to specify this as a blob atm + createCol(shapes, "Texture", typeof (Byte[])); + createCol(shapes, "ExtraParams", typeof (Byte[])); + + shapes.PrimaryKey = new DataColumn[] {shapes.Columns["UUID"]}; + + return shapes; + } + + /// + /// creates "primitems" table + /// + /// item table DataTable + private static DataTable createItemsTable() + { + DataTable items = new DataTable("primitems"); + + createCol(items, "itemID", typeof (String)); + createCol(items, "primID", typeof (String)); + createCol(items, "assetID", typeof (String)); + createCol(items, "parentFolderID", typeof (String)); + + createCol(items, "invType", typeof (Int32)); + createCol(items, "assetType", typeof (Int32)); + + createCol(items, "name", typeof (String)); + createCol(items, "description", typeof (String)); + + createCol(items, "creationDate", typeof (Int64)); + createCol(items, "creatorID", typeof (String)); + createCol(items, "ownerID", typeof (String)); + createCol(items, "lastOwnerID", typeof (String)); + createCol(items, "groupID", typeof (String)); + + createCol(items, "nextPermissions", typeof (UInt32)); + createCol(items, "currentPermissions", typeof (UInt32)); + createCol(items, "basePermissions", typeof (UInt32)); + createCol(items, "everyonePermissions", typeof (UInt32)); + createCol(items, "groupPermissions", typeof (UInt32)); + createCol(items, "flags", typeof (UInt32)); + + items.PrimaryKey = new DataColumn[] { items.Columns["itemID"] }; + + return items; + } + + /// + /// Creates "land" table + /// + /// land table DataTable + private static DataTable createLandTable() + { + DataTable land = new DataTable("land"); + createCol(land, "UUID", typeof (String)); + createCol(land, "RegionUUID", typeof (String)); + createCol(land, "LocalLandID", typeof (UInt32)); + + // Bitmap is a byte[512] + createCol(land, "Bitmap", typeof (Byte[])); + + createCol(land, "Name", typeof (String)); + createCol(land, "Desc", typeof (String)); + createCol(land, "OwnerUUID", typeof (String)); + createCol(land, "IsGroupOwned", typeof (Boolean)); + createCol(land, "Area", typeof (Int32)); + createCol(land, "AuctionID", typeof (Int32)); //Unemplemented + createCol(land, "Category", typeof (Int32)); //Enum OpenMetaverse.Parcel.ParcelCategory + createCol(land, "ClaimDate", typeof (Int32)); + createCol(land, "ClaimPrice", typeof (Int32)); + createCol(land, "GroupUUID", typeof (string)); + createCol(land, "SalePrice", typeof (Int32)); + createCol(land, "LandStatus", typeof (Int32)); //Enum. OpenMetaverse.Parcel.ParcelStatus + createCol(land, "LandFlags", typeof (UInt32)); + createCol(land, "LandingType", typeof (Byte)); + createCol(land, "MediaAutoScale", typeof (Byte)); + createCol(land, "MediaTextureUUID", typeof (String)); + createCol(land, "MediaURL", typeof (String)); + createCol(land, "MusicURL", typeof (String)); + createCol(land, "PassHours", typeof (Double)); + createCol(land, "PassPrice", typeof (UInt32)); + createCol(land, "SnapshotUUID", typeof (String)); + createCol(land, "UserLocationX", typeof (Double)); + createCol(land, "UserLocationY", typeof (Double)); + createCol(land, "UserLocationZ", typeof (Double)); + createCol(land, "UserLookAtX", typeof (Double)); + createCol(land, "UserLookAtY", typeof (Double)); + createCol(land, "UserLookAtZ", typeof (Double)); + createCol(land, "AuthbuyerID", typeof(String)); + createCol(land, "OtherCleanTime", typeof(Int32)); + createCol(land, "Dwell", typeof(Int32)); + + land.PrimaryKey = new DataColumn[] {land.Columns["UUID"]}; + + return land; + } + + /// + /// create "landaccesslist" table + /// + /// Landacceslist DataTable + private static DataTable createLandAccessListTable() + { + DataTable landaccess = new DataTable("landaccesslist"); + createCol(landaccess, "LandUUID", typeof (String)); + createCol(landaccess, "AccessUUID", typeof (String)); + createCol(landaccess, "Flags", typeof (UInt32)); + + return landaccess; + } + + private static DataTable createRegionSettingsTable() + { + DataTable regionsettings = new DataTable("regionsettings"); + createCol(regionsettings, "regionUUID", typeof(String)); + createCol(regionsettings, "block_terraform", typeof (Int32)); + createCol(regionsettings, "block_fly", typeof (Int32)); + createCol(regionsettings, "allow_damage", typeof (Int32)); + createCol(regionsettings, "restrict_pushing", typeof (Int32)); + createCol(regionsettings, "allow_land_resell", typeof (Int32)); + createCol(regionsettings, "allow_land_join_divide", typeof (Int32)); + createCol(regionsettings, "block_show_in_search", typeof (Int32)); + createCol(regionsettings, "agent_limit", typeof (Int32)); + createCol(regionsettings, "object_bonus", typeof (Double)); + createCol(regionsettings, "maturity", typeof (Int32)); + createCol(regionsettings, "disable_scripts", typeof (Int32)); + createCol(regionsettings, "disable_collisions", typeof (Int32)); + createCol(regionsettings, "disable_physics", typeof (Int32)); + createCol(regionsettings, "terrain_texture_1", typeof(String)); + createCol(regionsettings, "terrain_texture_2", typeof(String)); + createCol(regionsettings, "terrain_texture_3", typeof(String)); + createCol(regionsettings, "terrain_texture_4", typeof(String)); + createCol(regionsettings, "elevation_1_nw", typeof (Double)); + createCol(regionsettings, "elevation_2_nw", typeof (Double)); + createCol(regionsettings, "elevation_1_ne", typeof (Double)); + createCol(regionsettings, "elevation_2_ne", typeof (Double)); + createCol(regionsettings, "elevation_1_se", typeof (Double)); + createCol(regionsettings, "elevation_2_se", typeof (Double)); + createCol(regionsettings, "elevation_1_sw", typeof (Double)); + createCol(regionsettings, "elevation_2_sw", typeof (Double)); + createCol(regionsettings, "water_height", typeof (Double)); + createCol(regionsettings, "terrain_raise_limit", typeof (Double)); + createCol(regionsettings, "terrain_lower_limit", typeof (Double)); + createCol(regionsettings, "use_estate_sun", typeof (Int32)); + createCol(regionsettings, "sandbox", typeof (Int32)); + createCol(regionsettings, "sunvectorx",typeof (Double)); + createCol(regionsettings, "sunvectory",typeof (Double)); + createCol(regionsettings, "sunvectorz",typeof (Double)); + createCol(regionsettings, "fixed_sun", typeof (Int32)); + createCol(regionsettings, "sun_position", typeof (Double)); + createCol(regionsettings, "covenant", typeof(String)); + regionsettings.PrimaryKey = new DataColumn[] { regionsettings.Columns["regionUUID"] }; + return regionsettings; + } + + /*********************************************************************** + * + * Convert between ADO.NET <=> OpenSim Objects + * + * These should be database independant + * + **********************************************************************/ + + /// + /// + /// + /// + /// + private SceneObjectPart buildPrim(DataRow row) + { + // Code commented. Uncomment to test the unit test inline. + + // The unit test mentions this commented code for the purposes + // of debugging a unit test failure + + // SceneObjectGroup sog = new SceneObjectGroup(); + // SceneObjectPart sop = new SceneObjectPart(); + // sop.LocalId = 1; + // sop.Name = "object1"; + // sop.Description = "object1"; + // sop.Text = ""; + // sop.SitName = ""; + // sop.TouchName = ""; + // sop.UUID = UUID.Random(); + // sop.Shape = PrimitiveBaseShape.Default; + // sog.SetRootPart(sop); + // Add breakpoint in above line. Check sop fields. + + // TODO: this doesn't work yet because something more + // interesting has to be done to actually get these values + // back out. Not enough time to figure it out yet. + + SceneObjectPart prim = new SceneObjectPart(); + prim.UUID = new UUID((String) row["UUID"]); + // explicit conversion of integers is required, which sort + // of sucks. No idea if there is a shortcut here or not. + prim.CreationDate = Convert.ToInt32(row["CreationDate"]); + prim.Name = row["Name"] == DBNull.Value ? string.Empty : (string)row["Name"]; + // various text fields + prim.Text = (String) row["Text"]; + prim.Color = Color.FromArgb(Convert.ToInt32(row["ColorA"]), + Convert.ToInt32(row["ColorR"]), + Convert.ToInt32(row["ColorG"]), + Convert.ToInt32(row["ColorB"])); + prim.Description = (String) row["Description"]; + prim.SitName = (String) row["SitName"]; + prim.TouchName = (String) row["TouchName"]; + // permissions + prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]); + prim.CreatorID = new UUID((String) row["CreatorID"]); + prim.OwnerID = new UUID((String) row["OwnerID"]); + prim.GroupID = new UUID((String) row["GroupID"]); + prim.LastOwnerID = new UUID((String) row["LastOwnerID"]); + prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]); + prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]); + prim.GroupMask = Convert.ToUInt32(row["GroupMask"]); + prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]); + prim.BaseMask = Convert.ToUInt32(row["BaseMask"]); + // vectors + prim.OffsetPosition = new Vector3( + Convert.ToSingle(row["PositionX"]), + Convert.ToSingle(row["PositionY"]), + Convert.ToSingle(row["PositionZ"]) + ); + prim.GroupPosition = new Vector3( + Convert.ToSingle(row["GroupPositionX"]), + Convert.ToSingle(row["GroupPositionY"]), + Convert.ToSingle(row["GroupPositionZ"]) + ); + prim.Velocity = new Vector3( + Convert.ToSingle(row["VelocityX"]), + Convert.ToSingle(row["VelocityY"]), + Convert.ToSingle(row["VelocityZ"]) + ); + prim.AngularVelocity = new Vector3( + Convert.ToSingle(row["AngularVelocityX"]), + Convert.ToSingle(row["AngularVelocityY"]), + Convert.ToSingle(row["AngularVelocityZ"]) + ); + prim.Acceleration = new Vector3( + Convert.ToSingle(row["AccelerationX"]), + Convert.ToSingle(row["AccelerationY"]), + Convert.ToSingle(row["AccelerationZ"]) + ); + // quaternions + prim.RotationOffset = new Quaternion( + Convert.ToSingle(row["RotationX"]), + Convert.ToSingle(row["RotationY"]), + Convert.ToSingle(row["RotationZ"]), + Convert.ToSingle(row["RotationW"]) + ); + + prim.SitTargetPositionLL = new Vector3( + Convert.ToSingle(row["SitTargetOffsetX"]), + Convert.ToSingle(row["SitTargetOffsetY"]), + Convert.ToSingle(row["SitTargetOffsetZ"])); + prim.SitTargetOrientationLL = new Quaternion( + Convert.ToSingle( + row["SitTargetOrientX"]), + Convert.ToSingle( + row["SitTargetOrientY"]), + Convert.ToSingle( + row["SitTargetOrientZ"]), + Convert.ToSingle( + row["SitTargetOrientW"])); + + prim.ClickAction = Convert.ToByte(row["ClickAction"]); + prim.PayPrice[0] = Convert.ToInt32(row["PayPrice"]); + prim.PayPrice[1] = Convert.ToInt32(row["PayButton1"]); + prim.PayPrice[2] = Convert.ToInt32(row["PayButton2"]); + prim.PayPrice[3] = Convert.ToInt32(row["PayButton3"]); + prim.PayPrice[4] = Convert.ToInt32(row["PayButton4"]); + + prim.Sound = new UUID(row["LoopedSound"].ToString()); + prim.SoundGain = Convert.ToSingle(row["LoopedSoundGain"]); + prim.SoundFlags = 1; // If it's persisted at all, it's looped + + if (!row.IsNull("TextureAnimation")) + prim.TextureAnimation = Convert.FromBase64String(row["TextureAnimation"].ToString()); + if (!row.IsNull("ParticleSystem")) + prim.ParticleSystem = Convert.FromBase64String(row["ParticleSystem"].ToString()); + + prim.AngularVelocity = new Vector3( + Convert.ToSingle(row["OmegaX"]), + Convert.ToSingle(row["OmegaY"]), + Convert.ToSingle(row["OmegaZ"]) + ); + + prim.SetCameraEyeOffset(new Vector3( + Convert.ToSingle(row["CameraEyeOffsetX"]), + Convert.ToSingle(row["CameraEyeOffsetY"]), + Convert.ToSingle(row["CameraEyeOffsetZ"]) + )); + + prim.SetCameraAtOffset(new Vector3( + Convert.ToSingle(row["CameraAtOffsetX"]), + Convert.ToSingle(row["CameraAtOffsetY"]), + Convert.ToSingle(row["CameraAtOffsetZ"]) + )); + + if (Convert.ToInt16(row["ForceMouselook"]) != 0) + prim.SetForceMouselook(true); + + prim.ScriptAccessPin = Convert.ToInt32(row["ScriptAccessPin"]); + + if (Convert.ToInt16(row["AllowedDrop"]) != 0) + prim.AllowedDrop = true; + + if (Convert.ToInt16(row["DieAtEdge"]) != 0) + prim.DIE_AT_EDGE = true; + + prim.SalePrice = Convert.ToInt32(row["SalePrice"]); + prim.ObjectSaleType = Convert.ToByte(row["SaleType"]); + + prim.Material = Convert.ToByte(row["Material"]); + + prim.CollisionSound = new UUID(row["CollisionSound"].ToString()); + prim.CollisionSoundVolume = Convert.ToSingle(row["CollisionSoundVolume"]); + + if (Convert.ToInt16(row["VolumeDetect"]) != 0) + prim.VolumeDetectActive = true; + + return prim; + } + + /// + /// Build a prim inventory item from the persisted data. + /// + /// + /// + private static TaskInventoryItem buildItem(DataRow row) + { + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ItemID = new UUID((String)row["itemID"]); + taskItem.ParentPartID = new UUID((String)row["primID"]); + taskItem.AssetID = new UUID((String)row["assetID"]); + taskItem.ParentID = new UUID((String)row["parentFolderID"]); + + taskItem.InvType = Convert.ToInt32(row["invType"]); + taskItem.Type = Convert.ToInt32(row["assetType"]); + + taskItem.Name = (String)row["name"]; + taskItem.Description = (String)row["description"]; + taskItem.CreationDate = Convert.ToUInt32(row["creationDate"]); + taskItem.CreatorID = new UUID((String)row["creatorID"]); + taskItem.OwnerID = new UUID((String)row["ownerID"]); + taskItem.LastOwnerID = new UUID((String)row["lastOwnerID"]); + taskItem.GroupID = new UUID((String)row["groupID"]); + + taskItem.NextPermissions = Convert.ToUInt32(row["nextPermissions"]); + taskItem.CurrentPermissions = Convert.ToUInt32(row["currentPermissions"]); + taskItem.BasePermissions = Convert.ToUInt32(row["basePermissions"]); + taskItem.EveryonePermissions = Convert.ToUInt32(row["everyonePermissions"]); + taskItem.GroupPermissions = Convert.ToUInt32(row["groupPermissions"]); + taskItem.Flags = Convert.ToUInt32(row["flags"]); + + return taskItem; + } + + /// + /// Build a Land Data from the persisted data. + /// + /// + /// + private LandData buildLandData(DataRow row) + { + LandData newData = new LandData(); + + newData.GlobalID = new UUID((String) row["UUID"]); + newData.LocalID = Convert.ToInt32(row["LocalLandID"]); + + // Bitmap is a byte[512] + newData.Bitmap = (Byte[]) row["Bitmap"]; + + newData.Name = (String) row["Name"]; + newData.Description = (String) row["Desc"]; + newData.OwnerID = (UUID)(String) row["OwnerUUID"]; + newData.IsGroupOwned = (Boolean) row["IsGroupOwned"]; + newData.Area = Convert.ToInt32(row["Area"]); + newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unemplemented + newData.Category = (ParcelCategory) Convert.ToInt32(row["Category"]); + //Enum OpenMetaverse.Parcel.ParcelCategory + newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]); + newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]); + newData.GroupID = new UUID((String) row["GroupUUID"]); + newData.SalePrice = Convert.ToInt32(row["SalePrice"]); + newData.Status = (ParcelStatus) Convert.ToInt32(row["LandStatus"]); + //Enum. OpenMetaverse.Parcel.ParcelStatus + newData.Flags = Convert.ToUInt32(row["LandFlags"]); + newData.LandingType = (Byte) row["LandingType"]; + newData.MediaAutoScale = (Byte) row["MediaAutoScale"]; + newData.MediaID = new UUID((String) row["MediaTextureUUID"]); + newData.MediaURL = (String) row["MediaURL"]; + newData.MusicURL = (String) row["MusicURL"]; + newData.PassHours = Convert.ToSingle(row["PassHours"]); + newData.PassPrice = Convert.ToInt32(row["PassPrice"]); + newData.SnapshotID = (UUID)(String) row["SnapshotUUID"]; + try + { + + newData.UserLocation = + new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]), + Convert.ToSingle(row["UserLocationZ"])); + newData.UserLookAt = + new Vector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]), + Convert.ToSingle(row["UserLookAtZ"])); + + } + catch (InvalidCastException) + { + m_log.ErrorFormat("[PARCEL]: unable to get parcel telehub settings for {1}", newData.Name); + newData.UserLocation = Vector3.Zero; + newData.UserLookAt = Vector3.Zero; + } + newData.ParcelAccessList = new List(); + UUID authBuyerID = UUID.Zero; + + UUID.TryParse((string)row["AuthbuyerID"], out authBuyerID); + + newData.OtherCleanTime = Convert.ToInt32(row["OtherCleanTime"]); + newData.Dwell = Convert.ToInt32(row["Dwell"]); + + return newData; + } + + private RegionSettings buildRegionSettings(DataRow row) + { + RegionSettings newSettings = new RegionSettings(); + + newSettings.RegionUUID = new UUID((string) row["regionUUID"]); + newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]); + newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]); + newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]); + newSettings.RestrictPushing = Convert.ToBoolean(row["restrict_pushing"]); + newSettings.AllowLandResell = Convert.ToBoolean(row["allow_land_resell"]); + newSettings.AllowLandJoinDivide = Convert.ToBoolean(row["allow_land_join_divide"]); + newSettings.BlockShowInSearch = Convert.ToBoolean(row["block_show_in_search"]); + newSettings.AgentLimit = Convert.ToInt32(row["agent_limit"]); + newSettings.ObjectBonus = Convert.ToDouble(row["object_bonus"]); + newSettings.Maturity = Convert.ToInt32(row["maturity"]); + newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]); + newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]); + newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]); + newSettings.TerrainTexture1 = new UUID((String) row["terrain_texture_1"]); + newSettings.TerrainTexture2 = new UUID((String) row["terrain_texture_2"]); + newSettings.TerrainTexture3 = new UUID((String) row["terrain_texture_3"]); + newSettings.TerrainTexture4 = new UUID((String) row["terrain_texture_4"]); + newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]); + newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]); + newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]); + newSettings.Elevation2NE = Convert.ToDouble(row["elevation_2_ne"]); + newSettings.Elevation1SE = Convert.ToDouble(row["elevation_1_se"]); + newSettings.Elevation2SE = Convert.ToDouble(row["elevation_2_se"]); + newSettings.Elevation1SW = Convert.ToDouble(row["elevation_1_sw"]); + newSettings.Elevation2SW = Convert.ToDouble(row["elevation_2_sw"]); + newSettings.WaterHeight = Convert.ToDouble(row["water_height"]); + newSettings.TerrainRaiseLimit = Convert.ToDouble(row["terrain_raise_limit"]); + newSettings.TerrainLowerLimit = Convert.ToDouble(row["terrain_lower_limit"]); + newSettings.UseEstateSun = Convert.ToBoolean(row["use_estate_sun"]); + newSettings.Sandbox = Convert.ToBoolean(row["sandbox"]); + newSettings.SunVector = new Vector3 ( + Convert.ToSingle(row["sunvectorx"]), + Convert.ToSingle(row["sunvectory"]), + Convert.ToSingle(row["sunvectorz"]) + ); + newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); + newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); + newSettings.Covenant = new UUID((String) row["covenant"]); + + return newSettings; + } + + /// + /// Build a land access entry from the persisted data. + /// + /// + /// + private static ParcelManager.ParcelAccessEntry buildLandAccessData(DataRow row) + { + ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry(); + entry.AgentID = new UUID((string) row["AccessUUID"]); + entry.Flags = (AccessList) row["Flags"]; + entry.Time = new DateTime(); + return entry; + } + + /// + /// + /// + /// + /// + private static Array serializeTerrain(double[,] val) + { + MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize) *sizeof (double)); + BinaryWriter bw = new BinaryWriter(str); + + // TODO: COMPATIBILITY - Add byte-order conversions + for (int x = 0; x < (int)Constants.RegionSize; x++) + for (int y = 0; y < (int)Constants.RegionSize; y++) + bw.Write(val[x, y]); + + return str.ToArray(); + } + +// private void fillTerrainRow(DataRow row, UUID regionUUID, int rev, double[,] val) +// { +// row["RegionUUID"] = regionUUID; +// row["Revision"] = rev; + + // MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize)*sizeof (double)); +// BinaryWriter bw = new BinaryWriter(str); + +// // TODO: COMPATIBILITY - Add byte-order conversions + // for (int x = 0; x < (int)Constants.RegionSize; x++) + // for (int y = 0; y < (int)Constants.RegionSize; y++) +// bw.Write(val[x, y]); + +// row["Heightfield"] = str.ToArray(); +// } + + /// + /// + /// + /// + /// + /// + /// + private static void fillPrimRow(DataRow row, SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + row["UUID"] = prim.UUID.ToString(); + row["RegionUUID"] = regionUUID.ToString(); + row["CreationDate"] = prim.CreationDate; + row["Name"] = prim.Name; + row["SceneGroupID"] = sceneGroupID.ToString(); + // the UUID of the root part for this SceneObjectGroup + // various text fields + row["Text"] = prim.Text; + row["Description"] = prim.Description; + row["SitName"] = prim.SitName; + row["TouchName"] = prim.TouchName; + // permissions + row["ObjectFlags"] = prim.ObjectFlags; + row["CreatorID"] = prim.CreatorID.ToString(); + row["OwnerID"] = prim.OwnerID.ToString(); + row["GroupID"] = prim.GroupID.ToString(); + row["LastOwnerID"] = prim.LastOwnerID.ToString(); + row["OwnerMask"] = prim.OwnerMask; + row["NextOwnerMask"] = prim.NextOwnerMask; + row["GroupMask"] = prim.GroupMask; + row["EveryoneMask"] = prim.EveryoneMask; + row["BaseMask"] = prim.BaseMask; + // vectors + row["PositionX"] = prim.OffsetPosition.X; + row["PositionY"] = prim.OffsetPosition.Y; + row["PositionZ"] = prim.OffsetPosition.Z; + row["GroupPositionX"] = prim.GroupPosition.X; + row["GroupPositionY"] = prim.GroupPosition.Y; + row["GroupPositionZ"] = prim.GroupPosition.Z; + row["VelocityX"] = prim.Velocity.X; + row["VelocityY"] = prim.Velocity.Y; + row["VelocityZ"] = prim.Velocity.Z; + row["AngularVelocityX"] = prim.AngularVelocity.X; + row["AngularVelocityY"] = prim.AngularVelocity.Y; + row["AngularVelocityZ"] = prim.AngularVelocity.Z; + row["AccelerationX"] = prim.Acceleration.X; + row["AccelerationY"] = prim.Acceleration.Y; + row["AccelerationZ"] = prim.Acceleration.Z; + // quaternions + row["RotationX"] = prim.RotationOffset.X; + row["RotationY"] = prim.RotationOffset.Y; + row["RotationZ"] = prim.RotationOffset.Z; + row["RotationW"] = prim.RotationOffset.W; + + // Sit target + Vector3 sitTargetPos = prim.SitTargetPositionLL; + row["SitTargetOffsetX"] = sitTargetPos.X; + row["SitTargetOffsetY"] = sitTargetPos.Y; + row["SitTargetOffsetZ"] = sitTargetPos.Z; + + Quaternion sitTargetOrient = prim.SitTargetOrientationLL; + row["SitTargetOrientW"] = sitTargetOrient.W; + row["SitTargetOrientX"] = sitTargetOrient.X; + row["SitTargetOrientY"] = sitTargetOrient.Y; + row["SitTargetOrientZ"] = sitTargetOrient.Z; + row["ColorR"] = Convert.ToInt32(prim.Color.R); + row["ColorG"] = Convert.ToInt32(prim.Color.G); + row["ColorB"] = Convert.ToInt32(prim.Color.B); + row["ColorA"] = Convert.ToInt32(prim.Color.A); + row["PayPrice"] = prim.PayPrice[0]; + row["PayButton1"] = prim.PayPrice[1]; + row["PayButton2"] = prim.PayPrice[2]; + row["PayButton3"] = prim.PayPrice[3]; + row["PayButton4"] = prim.PayPrice[4]; + + + row["TextureAnimation"] = Convert.ToBase64String(prim.TextureAnimation); + row["ParticleSystem"] = Convert.ToBase64String(prim.ParticleSystem); + + row["OmegaX"] = prim.AngularVelocity.X; + row["OmegaY"] = prim.AngularVelocity.Y; + row["OmegaZ"] = prim.AngularVelocity.Z; + + row["CameraEyeOffsetX"] = prim.GetCameraEyeOffset().X; + row["CameraEyeOffsetY"] = prim.GetCameraEyeOffset().Y; + row["CameraEyeOffsetZ"] = prim.GetCameraEyeOffset().Z; + + row["CameraAtOffsetX"] = prim.GetCameraAtOffset().X; + row["CameraAtOffsetY"] = prim.GetCameraAtOffset().Y; + row["CameraAtOffsetZ"] = prim.GetCameraAtOffset().Z; + + + if ((prim.SoundFlags & 1) != 0) // Looped + { + row["LoopedSound"] = prim.Sound.ToString(); + row["LoopedSoundGain"] = prim.SoundGain; + } + else + { + row["LoopedSound"] = UUID.Zero.ToString(); + row["LoopedSoundGain"] = 0.0f; + } + + if (prim.GetForceMouselook()) + row["ForceMouselook"] = 1; + else + row["ForceMouselook"] = 0; + + row["ScriptAccessPin"] = prim.ScriptAccessPin; + + if (prim.AllowedDrop) + row["AllowedDrop"] = 1; + else + row["AllowedDrop"] = 0; + + if (prim.DIE_AT_EDGE) + row["DieAtEdge"] = 1; + else + row["DieAtEdge"] = 0; + + row["SalePrice"] = prim.SalePrice; + row["SaleType"] = Convert.ToInt16(prim.ObjectSaleType); + + // click action + row["ClickAction"] = prim.ClickAction; + + row["SalePrice"] = prim.SalePrice; + row["Material"] = prim.Material; + + row["CollisionSound"] = prim.CollisionSound.ToString(); + row["CollisionSoundVolume"] = prim.CollisionSoundVolume; + if (prim.VolumeDetectActive) + row["VolumeDetect"] = 1; + else + row["VolumeDetect"] = 0; + + } + + /// + /// + /// + /// + /// + private static void fillItemRow(DataRow row, TaskInventoryItem taskItem) + { + row["itemID"] = taskItem.ItemID.ToString(); + row["primID"] = taskItem.ParentPartID.ToString(); + row["assetID"] = taskItem.AssetID.ToString(); + row["parentFolderID"] = taskItem.ParentID.ToString(); + + row["invType"] = taskItem.InvType; + row["assetType"] = taskItem.Type; + + row["name"] = taskItem.Name; + row["description"] = taskItem.Description; + row["creationDate"] = taskItem.CreationDate; + row["creatorID"] = taskItem.CreatorID.ToString(); + row["ownerID"] = taskItem.OwnerID.ToString(); + row["lastOwnerID"] = taskItem.LastOwnerID.ToString(); + row["groupID"] = taskItem.GroupID.ToString(); + row["nextPermissions"] = taskItem.NextPermissions; + row["currentPermissions"] = taskItem.CurrentPermissions; + row["basePermissions"] = taskItem.BasePermissions; + row["everyonePermissions"] = taskItem.EveryonePermissions; + row["groupPermissions"] = taskItem.GroupPermissions; + row["flags"] = taskItem.Flags; + } + + /// + /// + /// + /// + /// + /// + private static void fillLandRow(DataRow row, LandData land, UUID regionUUID) + { + row["UUID"] = land.GlobalID.ToString(); + row["RegionUUID"] = regionUUID.ToString(); + row["LocalLandID"] = land.LocalID; + + // Bitmap is a byte[512] + row["Bitmap"] = land.Bitmap; + + row["Name"] = land.Name; + row["Desc"] = land.Description; + row["OwnerUUID"] = land.OwnerID.ToString(); + row["IsGroupOwned"] = land.IsGroupOwned; + row["Area"] = land.Area; + row["AuctionID"] = land.AuctionID; //Unemplemented + row["Category"] = land.Category; //Enum OpenMetaverse.Parcel.ParcelCategory + row["ClaimDate"] = land.ClaimDate; + row["ClaimPrice"] = land.ClaimPrice; + row["GroupUUID"] = land.GroupID.ToString(); + row["SalePrice"] = land.SalePrice; + row["LandStatus"] = land.Status; //Enum. OpenMetaverse.Parcel.ParcelStatus + row["LandFlags"] = land.Flags; + row["LandingType"] = land.LandingType; + row["MediaAutoScale"] = land.MediaAutoScale; + row["MediaTextureUUID"] = land.MediaID.ToString(); + row["MediaURL"] = land.MediaURL; + row["MusicURL"] = land.MusicURL; + row["PassHours"] = land.PassHours; + row["PassPrice"] = land.PassPrice; + row["SnapshotUUID"] = land.SnapshotID.ToString(); + row["UserLocationX"] = land.UserLocation.X; + row["UserLocationY"] = land.UserLocation.Y; + row["UserLocationZ"] = land.UserLocation.Z; + row["UserLookAtX"] = land.UserLookAt.X; + row["UserLookAtY"] = land.UserLookAt.Y; + row["UserLookAtZ"] = land.UserLookAt.Z; + row["AuthbuyerID"] = land.AuthBuyerID.ToString(); + row["OtherCleanTime"] = land.OtherCleanTime; + row["Dwell"] = land.Dwell; + } + + /// + /// + /// + /// + /// + /// + private static void fillLandAccessRow(DataRow row, ParcelManager.ParcelAccessEntry entry, UUID parcelID) + { + row["LandUUID"] = parcelID.ToString(); + row["AccessUUID"] = entry.AgentID.ToString(); + row["Flags"] = entry.Flags; + } + + private static void fillRegionSettingsRow(DataRow row, RegionSettings settings) + { + row["regionUUID"] = settings.RegionUUID.ToString(); + row["block_terraform"] = settings.BlockTerraform; + row["block_fly"] = settings.BlockFly; + row["allow_damage"] = settings.AllowDamage; + row["restrict_pushing"] = settings.RestrictPushing; + row["allow_land_resell"] = settings.AllowLandResell; + row["allow_land_join_divide"] = settings.AllowLandJoinDivide; + row["block_show_in_search"] = settings.BlockShowInSearch; + row["agent_limit"] = settings.AgentLimit; + row["object_bonus"] = settings.ObjectBonus; + row["maturity"] = settings.Maturity; + row["disable_scripts"] = settings.DisableScripts; + row["disable_collisions"] = settings.DisableCollisions; + row["disable_physics"] = settings.DisablePhysics; + row["terrain_texture_1"] = settings.TerrainTexture1.ToString(); + row["terrain_texture_2"] = settings.TerrainTexture2.ToString(); + row["terrain_texture_3"] = settings.TerrainTexture3.ToString(); + row["terrain_texture_4"] = settings.TerrainTexture4.ToString(); + row["elevation_1_nw"] = settings.Elevation1NW; + row["elevation_2_nw"] = settings.Elevation2NW; + row["elevation_1_ne"] = settings.Elevation1NE; + row["elevation_2_ne"] = settings.Elevation2NE; + row["elevation_1_se"] = settings.Elevation1SE; + row["elevation_2_se"] = settings.Elevation2SE; + row["elevation_1_sw"] = settings.Elevation1SW; + row["elevation_2_sw"] = settings.Elevation2SW; + row["water_height"] = settings.WaterHeight; + row["terrain_raise_limit"] = settings.TerrainRaiseLimit; + row["terrain_lower_limit"] = settings.TerrainLowerLimit; + row["use_estate_sun"] = settings.UseEstateSun; + row["Sandbox"] = settings.Sandbox; // database uses upper case S for sandbox + row["sunvectorx"] = settings.SunVector.X; + row["sunvectory"] = settings.SunVector.Y; + row["sunvectorz"] = settings.SunVector.Z; + row["fixed_sun"] = settings.FixedSun; + row["sun_position"] = settings.SunPosition; + row["covenant"] = settings.Covenant.ToString(); + } + + /// + /// + /// + /// + /// + private PrimitiveBaseShape buildShape(DataRow row) + { + PrimitiveBaseShape s = new PrimitiveBaseShape(); + s.Scale = new Vector3( + Convert.ToSingle(row["ScaleX"]), + Convert.ToSingle(row["ScaleY"]), + Convert.ToSingle(row["ScaleZ"]) + ); + // paths + s.PCode = Convert.ToByte(row["PCode"]); + s.PathBegin = Convert.ToUInt16(row["PathBegin"]); + s.PathEnd = Convert.ToUInt16(row["PathEnd"]); + s.PathScaleX = Convert.ToByte(row["PathScaleX"]); + s.PathScaleY = Convert.ToByte(row["PathScaleY"]); + s.PathShearX = Convert.ToByte(row["PathShearX"]); + s.PathShearY = Convert.ToByte(row["PathShearY"]); + s.PathSkew = Convert.ToSByte(row["PathSkew"]); + s.PathCurve = Convert.ToByte(row["PathCurve"]); + s.PathRadiusOffset = Convert.ToSByte(row["PathRadiusOffset"]); + s.PathRevolutions = Convert.ToByte(row["PathRevolutions"]); + s.PathTaperX = Convert.ToSByte(row["PathTaperX"]); + s.PathTaperY = Convert.ToSByte(row["PathTaperY"]); + s.PathTwist = Convert.ToSByte(row["PathTwist"]); + s.PathTwistBegin = Convert.ToSByte(row["PathTwistBegin"]); + // profile + s.ProfileBegin = Convert.ToUInt16(row["ProfileBegin"]); + s.ProfileEnd = Convert.ToUInt16(row["ProfileEnd"]); + s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]); + s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]); + s.State = Convert.ToByte(row["State"]); + + byte[] textureEntry = (byte[])row["Texture"]; + s.TextureEntry = textureEntry; + + s.ExtraParams = (byte[]) row["ExtraParams"]; + return s; + } + + /// + /// + /// + /// + /// + private static void fillShapeRow(DataRow row, SceneObjectPart prim) + { + PrimitiveBaseShape s = prim.Shape; + row["UUID"] = prim.UUID.ToString(); + // shape is an enum + row["Shape"] = 0; + // vectors + row["ScaleX"] = s.Scale.X; + row["ScaleY"] = s.Scale.Y; + row["ScaleZ"] = s.Scale.Z; + // paths + row["PCode"] = s.PCode; + row["PathBegin"] = s.PathBegin; + row["PathEnd"] = s.PathEnd; + row["PathScaleX"] = s.PathScaleX; + row["PathScaleY"] = s.PathScaleY; + row["PathShearX"] = s.PathShearX; + row["PathShearY"] = s.PathShearY; + row["PathSkew"] = s.PathSkew; + row["PathCurve"] = s.PathCurve; + row["PathRadiusOffset"] = s.PathRadiusOffset; + row["PathRevolutions"] = s.PathRevolutions; + row["PathTaperX"] = s.PathTaperX; + row["PathTaperY"] = s.PathTaperY; + row["PathTwist"] = s.PathTwist; + row["PathTwistBegin"] = s.PathTwistBegin; + // profile + row["ProfileBegin"] = s.ProfileBegin; + row["ProfileEnd"] = s.ProfileEnd; + row["ProfileCurve"] = s.ProfileCurve; + row["ProfileHollow"] = s.ProfileHollow; + row["State"] = s.State; + + row["Texture"] = s.TextureEntry; + row["ExtraParams"] = s.ExtraParams; + } + + /// + /// + /// + /// + /// + /// + private void addPrim(SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + + DataTable prims = ds.Tables["prims"]; + DataTable shapes = ds.Tables["primshapes"]; + + DataRow primRow = prims.Rows.Find(prim.UUID.ToString()); + if (primRow == null) + { + primRow = prims.NewRow(); + fillPrimRow(primRow, prim, sceneGroupID, regionUUID); + prims.Rows.Add(primRow); + } + else + { + fillPrimRow(primRow, prim, sceneGroupID, regionUUID); + } + + DataRow shapeRow = shapes.Rows.Find(prim.UUID.ToString()); + if (shapeRow == null) + { + shapeRow = shapes.NewRow(); + fillShapeRow(shapeRow, prim); + shapes.Rows.Add(shapeRow); + } + else + { + fillShapeRow(shapeRow, prim); + } + } + + /// + /// see IRegionDatastore + /// + /// + /// + public void StorePrimInventory(UUID primID, ICollection items) + { + m_log.InfoFormat("[REGION DB]: Entered StorePrimInventory with prim ID {0}", primID); + + DataTable dbItems = ds.Tables["primitems"]; + + // For now, we're just going to crudely remove all the previous inventory items + // no matter whether they have changed or not, and replace them with the current set. + lock (ds) + { + RemoveItems(primID); + + // repalce with current inventory details + foreach (TaskInventoryItem newItem in items) + { +// m_log.InfoFormat( +// "[DATASTORE]: ", +// "Adding item {0}, {1} to prim ID {2}", +// newItem.Name, newItem.ItemID, newItem.ParentPartID); + + DataRow newItemRow = dbItems.NewRow(); + fillItemRow(newItemRow, newItem); + dbItems.Rows.Add(newItemRow); + } + } + + Commit(); + } + + /*********************************************************************** + * + * SQL Statement Creation Functions + * + * These functions create SQL statements for update, insert, and create. + * They can probably be factored later to have a db independant + * portion and a db specific portion + * + **********************************************************************/ + + /// + /// Create an insert command + /// + /// table name + /// data table + /// the created command + /// + /// This is subtle enough to deserve some commentary. + /// Instead of doing *lots* and *lots of hardcoded strings + /// for database definitions we'll use the fact that + /// realistically all insert statements look like "insert + /// into A(b, c) values(:b, :c) on the parameterized query + /// front. If we just have a list of b, c, etc... we can + /// generate these strings instead of typing them out. + /// + private static SqliteCommand createInsertCommand(string table, DataTable dt) + { + string[] cols = new string[dt.Columns.Count]; + for (int i = 0; i < dt.Columns.Count; i++) + { + DataColumn col = dt.Columns[i]; + cols[i] = col.ColumnName; + } + + string sql = "insert into " + table + "("; + sql += String.Join(", ", cols); + // important, the first ':' needs to be here, the rest get added in the join + sql += ") values (:"; + sql += String.Join(", :", cols); + sql += ")"; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + + /// + /// create an update command + /// + /// table name + /// + /// + /// the created command + private static SqliteCommand createUpdateCommand(string table, string pk, DataTable dt) + { + string sql = "update " + table + " set "; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ", "; + } + subsql += col.ColumnName + "= :" + col.ColumnName; + } + sql += subsql; + sql += " where " + pk; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// create an update command + /// + /// table name + /// + /// + /// the created command + private static SqliteCommand createUpdateCommand(string table, string pk1, string pk2, DataTable dt) + { + string sql = "update " + table + " set "; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ", "; + } + subsql += col.ColumnName + "= :" + col.ColumnName; + } + sql += subsql; + sql += " where " + pk1 + " and " + pk2; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// + /// + /// Data Table + /// + // private static string defineTable(DataTable dt) + // { + // string sql = "create table " + dt.TableName + "("; + // string subsql = String.Empty; + // foreach (DataColumn col in dt.Columns) + // { + // if (subsql.Length > 0) + // { + // // a map function would rock so much here + // subsql += ",\n"; + // } + // subsql += col.ColumnName + " " + sqliteType(col.DataType); + // if (dt.PrimaryKey.Length > 0 && col == dt.PrimaryKey[0]) + // { + // subsql += " primary key"; + // } + // } + // sql += subsql; + // sql += ")"; + // return sql; + // } + + /*********************************************************************** + * + * Database Binding functions + * + * These will be db specific due to typing, and minor differences + * in databases. + * + **********************************************************************/ + + /// + /// This is a convenience function that collapses 5 repetitive + /// lines for defining SqliteParameters to 2 parameters: + /// column name and database type. + /// + /// It assumes certain conventions like :param as the param + /// name to replace in parametrized queries, and that source + /// version is always current version, both of which are fine + /// for us. + /// + ///a built sqlite parameter + private static SqliteParameter createSqliteParameter(string name, Type type) + { + SqliteParameter param = new SqliteParameter(); + param.ParameterName = ":" + name; + param.DbType = dbtypeFromType(type); + param.SourceColumn = name; + param.SourceVersion = DataRowVersion.Current; + return param; + } + + /// + /// + /// + /// + /// + private void setupPrimCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("prims", ds.Tables["prims"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("prims", "UUID=:UUID", ds.Tables["prims"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from prims where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof (String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + + /// + /// + /// + /// + /// + private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("primitems", ds.Tables["primitems"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("primitems", "itemID = :itemID", ds.Tables["primitems"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from primitems where itemID = :itemID"); + delete.Parameters.Add(createSqliteParameter("itemID", typeof (String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + + /// + /// + /// + /// + /// + private void setupTerrainCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("terrain", ds.Tables["terrain"]); + da.InsertCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupLandCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("land", ds.Tables["land"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("land", "UUID=:UUID", ds.Tables["land"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from land where UUID=:UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + da.DeleteCommand = delete; + da.DeleteCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupLandAccessCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("landaccesslist", ds.Tables["landaccesslist"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("landaccesslist", "LandUUID=:landUUID", "AccessUUID=:AccessUUID", ds.Tables["landaccesslist"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from landaccesslist where LandUUID= :LandUUID and AccessUUID= :AccessUUID"); + delete.Parameters.Add(createSqliteParameter("LandUUID", typeof(String))); + delete.Parameters.Add(createSqliteParameter("AccessUUID", typeof(String))); + da.DeleteCommand = delete; + da.DeleteCommand.Connection = conn; + + } + + private void setupRegionSettingsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("regionsettings", ds.Tables["regionsettings"]); + da.InsertCommand.Connection = conn; + da.UpdateCommand = createUpdateCommand("regionsettings", "regionUUID=:regionUUID", ds.Tables["regionsettings"]); + da.UpdateCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupShapeCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("primshapes", ds.Tables["primshapes"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("primshapes", "UUID=:UUID", ds.Tables["primshapes"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from primshapes where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof (String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + + /*********************************************************************** + * + * Type conversion functions + * + **********************************************************************/ + + /// + /// Type conversion function + /// + /// + /// + private static DbType dbtypeFromType(Type type) + { + if (type == typeof (String)) + { + return DbType.String; + } + else if (type == typeof (Int32)) + { + return DbType.Int32; + } + else if (type == typeof (Double)) + { + return DbType.Double; + } + else if (type == typeof (Byte)) + { + return DbType.Byte; + } + else if (type == typeof (Double)) + { + return DbType.Double; + } + else if (type == typeof (Byte[])) + { + return DbType.Binary; + } + else + { + return DbType.String; + } + } + + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs b/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs new file mode 100644 index 0000000000..67cf7165b1 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs @@ -0,0 +1,81 @@ +/* + * 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 OpenSimulator 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.SqliteClient; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteUserAccountData : SQLiteGenericTableHandler, IUserAccountData + { + public SQLiteUserAccountData(string connectionString, string realm) + : base(connectionString, realm, "UserAccount") + { + } + + public UserAccountData[] GetUsers(UUID scopeID, string query) + { + string[] words = query.Split(new char[] {' '}); + + for (int i = 0 ; i < words.Length ; i++) + { + if (words[i].Length < 3) + { + if (i != words.Length - 1) + Array.Copy(words, i + 1, words, i, words.Length - i - 1); + Array.Resize(ref words, words.Length - 1); + } + } + + if (words.Length == 0) + return new UserAccountData[0]; + + if (words.Length > 2) + return new UserAccountData[0]; + + SqliteCommand cmd = new SqliteCommand(); + + if (words.Length == 1) + { + cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", + m_Realm, scopeID.ToString(), words[0]); + } + else + { + cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{3}%')", + m_Realm, scopeID.ToString(), words[0], words[1]); + } + + return DoQuery(cmd); + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs b/OpenSim/Data/SQLiteNG/SQLiteUtils.cs new file mode 100644 index 0000000000..4a835ce52f --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteUtils.cs @@ -0,0 +1,307 @@ +/* + * 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 OpenSimulator 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.Data; +using Mono.Data.SqliteClient; + +namespace OpenSim.Data.SQLite +{ + /// + /// A base class for methods needed by all SQLite database classes + /// + public class SQLiteUtil + { + /*********************************************************************** + * + * Database Definition Helper Functions + * + * This should be db agnostic as we define them in ADO.NET terms + * + **********************************************************************/ + + /// + /// + /// + /// + /// + /// + public static void createCol(DataTable dt, string name, Type type) + { + DataColumn col = new DataColumn(name, type); + dt.Columns.Add(col); + } + + /*********************************************************************** + * + * SQL Statement Creation Functions + * + * These functions create SQL statements for update, insert, and create. + * They can probably be factored later to have a db independant + * portion and a db specific portion + * + **********************************************************************/ + + /// + /// Create an insert command + /// + /// table name + /// data table + /// the created command + /// + /// This is subtle enough to deserve some commentary. + /// Instead of doing *lots* and *lots of hardcoded strings + /// for database definitions we'll use the fact that + /// realistically all insert statements look like "insert + /// into A(b, c) values(:b, :c) on the parameterized query + /// front. If we just have a list of b, c, etc... we can + /// generate these strings instead of typing them out. + /// + public static SqliteCommand createInsertCommand(string table, DataTable dt) + { + + string[] cols = new string[dt.Columns.Count]; + for (int i = 0; i < dt.Columns.Count; i++) + { + DataColumn col = dt.Columns[i]; + cols[i] = col.ColumnName; + } + + string sql = "insert into " + table + "("; + sql += String.Join(", ", cols); + // important, the first ':' needs to be here, the rest get added in the join + sql += ") values (:"; + sql += String.Join(", :", cols); + sql += ")"; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// create an update command + /// + /// table name + /// + /// + /// the created command + public static SqliteCommand createUpdateCommand(string table, string pk, DataTable dt) + { + string sql = "update " + table + " set "; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ", "; + } + subsql += col.ColumnName + "= :" + col.ColumnName; + } + sql += subsql; + sql += " where " + pk; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// + /// + /// Data Table + /// + public static string defineTable(DataTable dt) + { + string sql = "create table " + dt.TableName + "("; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ",\n"; + } + subsql += col.ColumnName + " " + sqliteType(col.DataType); + if (dt.PrimaryKey.Length > 0) + { + if (col == dt.PrimaryKey[0]) + { + subsql += " primary key"; + } + } + } + sql += subsql; + sql += ")"; + return sql; + } + + /*********************************************************************** + * + * Database Binding functions + * + * These will be db specific due to typing, and minor differences + * in databases. + * + **********************************************************************/ + + /// + /// + /// This is a convenience function that collapses 5 repetitive + /// lines for defining SqliteParameters to 2 parameters: + /// column name and database type. + /// + /// + /// + /// It assumes certain conventions like :param as the param + /// name to replace in parametrized queries, and that source + /// version is always current version, both of which are fine + /// for us. + /// + /// + /// + /// + ///a built sqlite parameter + public static SqliteParameter createSqliteParameter(string name, Type type) + { + SqliteParameter param = new SqliteParameter(); + param.ParameterName = ":" + name; + param.DbType = dbtypeFromType(type); + param.SourceColumn = name; + param.SourceVersion = DataRowVersion.Current; + return param; + } + + /*********************************************************************** + * + * Type conversion functions + * + **********************************************************************/ + + /// + /// Type conversion function + /// + /// a type + /// a DbType + public static DbType dbtypeFromType(Type type) + { + if (type == typeof (String)) + { + return DbType.String; + } + else if (type == typeof (Int32)) + { + return DbType.Int32; + } + else if (type == typeof (UInt32)) + { + return DbType.UInt32; + } + else if (type == typeof (Int64)) + { + return DbType.Int64; + } + else if (type == typeof (UInt64)) + { + return DbType.UInt64; + } + else if (type == typeof (Double)) + { + return DbType.Double; + } + else if (type == typeof (Boolean)) + { + return DbType.Boolean; + } + else if (type == typeof (Byte[])) + { + return DbType.Binary; + } + else + { + return DbType.String; + } + } + + /// + /// + /// a Type + /// a string + /// this is something we'll need to implement for each db slightly differently. + public static string sqliteType(Type type) + { + if (type == typeof (String)) + { + return "varchar(255)"; + } + else if (type == typeof (Int32)) + { + return "integer"; + } + else if (type == typeof (UInt32)) + { + return "integer"; + } + else if (type == typeof (Int64)) + { + return "varchar(255)"; + } + else if (type == typeof (UInt64)) + { + return "varchar(255)"; + } + else if (type == typeof (Double)) + { + return "float"; + } + else if (type == typeof (Boolean)) + { + return "integer"; + } + else if (type == typeof (Byte[])) + { + return "blob"; + } + else + { + return "string"; + } + } + } +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs b/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs new file mode 100644 index 0000000000..a66e0c6fe0 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs @@ -0,0 +1,155 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using System.Collections.Generic; +using Mono.Data.SqliteClient; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// A MySQL Interface for the Asset Server + /// + public class SQLiteXInventoryData : IXInventoryData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SQLiteGenericTableHandler m_Folders; + private SqliteItemHandler m_Items; + + public SQLiteXInventoryData(string conn, string realm) + { + m_Folders = new SQLiteGenericTableHandler( + conn, "inventoryfolders", "InventoryStore"); + m_Items = new SqliteItemHandler( + conn, "inventoryitems", String.Empty); + } + + public XInventoryFolder[] GetFolders(string[] fields, string[] vals) + { + return m_Folders.Get(fields, vals); + } + + public XInventoryItem[] GetItems(string[] fields, string[] vals) + { + return m_Items.Get(fields, vals); + } + + public bool StoreFolder(XInventoryFolder folder) + { + return m_Folders.Store(folder); + } + + public bool StoreItem(XInventoryItem item) + { + return m_Items.Store(item); + } + + public bool DeleteFolders(string field, string val) + { + return m_Folders.Delete(field, val); + } + + public bool DeleteItems(string field, string val) + { + return m_Items.Delete(field, val); + } + + public bool MoveItem(string id, string newParent) + { + return m_Items.MoveItem(id, newParent); + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + return m_Items.GetActiveGestures(principalID); + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + return m_Items.GetAssetPermissions(principalID, assetID); + } + } + + public class SqliteItemHandler : SQLiteGenericTableHandler + { + public SqliteItemHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public bool MoveItem(string id, string newParent) + { + SqliteCommand cmd = new SqliteCommand(); + + cmd.CommandText = String.Format("update {0} set parentFolderID = :ParentFolderID where inventoryID = :InventoryID", m_Realm); + cmd.Parameters.Add(new SqliteParameter(":ParentFolderID", newParent)); + cmd.Parameters.Add(new SqliteParameter(":InventoryID", id)); + + return ExecuteNonQuery(cmd, m_Connection) == 0 ? false : true; + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + SqliteCommand cmd = new SqliteCommand(); + cmd.CommandText = String.Format("select * from inventoryitems where avatarId = :uuid and assetType = :type and flags = 1", m_Realm); + + cmd.Parameters.Add(new SqliteParameter(":uuid", principalID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":type", (int)AssetType.Gesture)); + + return DoQuery(cmd); + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + SqliteCommand cmd = new SqliteCommand(); + + cmd.CommandText = String.Format("select inventoryCurrentPermissions from inventoryitems where avatarID = :PrincipalID and assetID = :AssetID", m_Realm); + cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":AssetID", assetID.ToString())); + + IDataReader reader = ExecuteReader(cmd, m_Connection); + + int perms = 0; + + while (reader.Read()) + { + perms |= Convert.ToInt32(reader["inventoryCurrentPermissions"]); + } + + reader.Close(); + CloseCommand(cmd); + + return perms; + } + } +} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs new file mode 100644 index 0000000000..0c2f5dfefc --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs @@ -0,0 +1,64 @@ +/* + * 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 OpenSimulator 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.IO; +using NUnit.Framework; +using OpenSim.Data.Tests; +using OpenSim.Tests.Common; + +namespace OpenSim.Data.SQLite.Tests +{ + [TestFixture, DatabaseTest] + public class SQLiteAssetTest : BasicAssetTest + { + public string file; + public string connect; + + [TestFixtureSetUp] + public void Init() + { + // SQLite doesn't work on power or z linux + if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) + { + Assert.Ignore(); + } + + SuperInit(); + file = Path.GetTempFileName() + ".db"; + connect = "URI=file:" + file + ",version=3"; + db = new SQLiteAssetData(); + db.Initialise(connect); + } + + [TestFixtureTearDown] + public void Cleanup() + { + db.Dispose(); + File.Delete(file); + } + } +} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs new file mode 100644 index 0000000000..30f66414a9 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs @@ -0,0 +1,65 @@ +/* + * 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 OpenSimulator 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.IO; +using NUnit.Framework; +using OpenSim.Data.Tests; +using OpenSim.Tests.Common; + +namespace OpenSim.Data.SQLite.Tests +{ + [TestFixture, DatabaseTest] + public class SQLiteEstateTest : BasicEstateTest + { + public string file = "regiontest.db"; + public string connect; + + [TestFixtureSetUp] + public void Init() + { + // SQLite doesn't work on power or z linux + if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) + { + Assert.Ignore(); + } + + SuperInit(); + file = Path.GetTempFileName() + ".db"; + connect = "URI=file:" + file + ",version=3"; + db = new SQLiteEstateStore(); + db.Initialise(connect); + regionDb = new SQLiteRegionData(); + regionDb.Initialise(connect); + } + + [TestFixtureTearDown] + public void Cleanup() + { + regionDb.Dispose(); + } + } +} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs new file mode 100644 index 0000000000..98458a30ec --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs @@ -0,0 +1,66 @@ +/* + * 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 OpenSimulator 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.IO; +using NUnit.Framework; +using OpenSim.Data.Tests; +using OpenSim.Tests.Common; + +namespace OpenSim.Data.SQLite.Tests +{ + [TestFixture, DatabaseTest] + public class SQLiteInventoryTest : BasicInventoryTest + { + public string file; + public string connect; + + [TestFixtureSetUp] + public void Init() + { + // SQLite doesn't work on power or z linux + if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) + { + Assert.Ignore(); + } + + SuperInit(); + + file = Path.GetTempFileName() + ".db"; + connect = "URI=file:" + file + ",version=3"; + + db = new SQLiteInventoryStore(); + db.Initialise(connect); + } + + [TestFixtureTearDown] + public void Cleanup() + { + db.Dispose(); + File.Delete(file); + } + } +} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs new file mode 100644 index 0000000000..abb97cfa4f --- /dev/null +++ b/OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs @@ -0,0 +1,64 @@ +/* + * 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 OpenSimulator 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.IO; +using NUnit.Framework; +using OpenSim.Data.Tests; +using OpenSim.Tests.Common; + +namespace OpenSim.Data.SQLite.Tests +{ + [TestFixture, DatabaseTest] + public class SQLiteRegionTest : BasicRegionTest + { + public string file = "regiontest.db"; + public string connect; + + [TestFixtureSetUp] + public void Init() + { + // SQLite doesn't work on power or z linux + if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) + { + Assert.Ignore(); + } + + SuperInit(); + file = Path.GetTempFileName() + ".db"; + connect = "URI=file:" + file + ",version=3"; + db = new SQLiteRegionData(); + db.Initialise(connect); + } + + [TestFixtureTearDown] + public void Cleanup() + { + db.Dispose(); + File.Delete(file); + } + } +} diff --git a/prebuild.xml b/prebuild.xml index 5cc742c45a..d6b6361182 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2215,6 +2215,43 @@ + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + From e84cc2f9db9e03a45f9fa315cf598dd5ef83b7ef Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 18:56:50 +0100 Subject: [PATCH 007/260] Change SQLiteNG to work with mono 2.6 and above using the Mono.Data.Sqlite.dll Include the library so that Windows builds correctly It appears that Windows is okay with either SQLite or SQLiteNG Incorporate the latest fixes made by Diva to OpenSim.Data.SQLite --- OpenSim/Data/SQLiteNG/SQLiteAssetData.cs | 6 +- .../Data/SQLiteNG/SQLiteAuthenticationData.cs | 23 +- OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs | 8 +- OpenSim/Data/SQLiteNG/SQLiteEstateData.cs | 48 +-- OpenSim/Data/SQLiteNG/SQLiteFramework.cs | 14 +- OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs | 8 +- .../SQLiteNG/SQLiteGenericTableHandler.cs | 18 +- OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs | 13 +- OpenSim/Data/SQLiteNG/SQLiteRegionData.cs | 276 +++++++++++------- .../Data/SQLiteNG/SQLiteUserAccountData.cs | 4 +- OpenSim/Data/SQLiteNG/SQLiteUtils.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs | 4 +- .../Data/SQLiteNG/Tests/SQLiteAssetTest.cs | 64 ---- .../Data/SQLiteNG/Tests/SQLiteEstateTest.cs | 65 ----- .../SQLiteNG/Tests/SQLiteInventoryTest.cs | 66 ----- .../Data/SQLiteNG/Tests/SQLiteRegionTest.cs | 64 ---- prebuild.xml | 3 +- 17 files changed, 246 insertions(+), 440 deletions(-) delete mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs delete mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs delete mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs delete mode 100644 OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs diff --git a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs index a032670588..636bf8645b 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs @@ -30,7 +30,7 @@ using System.Data; using System.Reflection; using System.Collections.Generic; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; @@ -137,7 +137,7 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); - + cmd.ExecuteNonQuery(); } } @@ -340,4 +340,4 @@ namespace OpenSim.Data.SQLite #endregion } -} \ No newline at end of file +} diff --git a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs index aa10734d50..086ac0a972 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -56,13 +56,8 @@ namespace OpenSim.Data.SQLite m_Connection = new SqliteConnection(connectionString); m_Connection.Open(); - using (SqliteConnection dbcon = (SqliteConnection)((ICloneable)m_Connection).Clone()) - { - dbcon.Open(); - Migration m = new Migration(dbcon, GetType().Assembly, "AuthStore"); - m.Update(); - dbcon.Close(); - } + Migration m = new Migration(m_Connection, GetType().Assembly, "AuthStore"); + m.Update(); m_initialized = true; } @@ -113,7 +108,7 @@ namespace OpenSim.Data.SQLite } finally { - CloseCommand(cmd); + //CloseCommand(cmd); } return null; @@ -156,14 +151,14 @@ namespace OpenSim.Data.SQLite { if (ExecuteNonQuery(cmd, m_Connection) < 1) { - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } catch (Exception e) { Console.WriteLine(e.ToString()); - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } @@ -184,19 +179,19 @@ namespace OpenSim.Data.SQLite { if (ExecuteNonQuery(cmd, m_Connection) < 1) { - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } catch (Exception e) { Console.WriteLine(e.ToString()); - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } - CloseCommand(cmd); + //CloseCommand(cmd); return true; } diff --git a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs b/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs index b3f4a4c077..c093884db4 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs @@ -33,7 +33,7 @@ using System.Threading; using log4net; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -55,8 +55,8 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm); - cmd.Parameters.Add(":PrincipalID", principalID.ToString()); - cmd.Parameters.Add(":Name", name); + cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue(":Name", name); try { @@ -67,7 +67,7 @@ namespace OpenSim.Data.SQLite } finally { - CloseCommand(cmd); + //CloseCommand(cmd); } } } diff --git a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs b/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs index bd6b776f80..9dd4a2e69b 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; @@ -62,8 +62,8 @@ namespace OpenSim.Data.SQLite Migration m = new Migration(m_connection, assem, "EstateStore"); m.Update(); - m_connection.Close(); - m_connection.Open(); + //m_connection.Close(); + // m_connection.Open(); Type t = typeof(EstateSettings); m_Fields = t.GetFields(BindingFlags.NonPublic | @@ -87,7 +87,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.Add(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); return DoLoad(cmd, regionID, create); } @@ -143,13 +143,13 @@ namespace OpenSim.Data.SQLite if (m_FieldMap[name].GetValue(es) is bool) { if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.Add(":"+name, "1"); + cmd.Parameters.AddWithValue(":"+name, "1"); else - cmd.Parameters.Add(":"+name, "0"); + cmd.Parameters.AddWithValue(":"+name, "0"); } else { - cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); } } @@ -167,8 +167,8 @@ namespace OpenSim.Data.SQLite r.Close(); cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.Add(":RegionID", regionID.ToString()); - cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); // This will throw on dupe key try @@ -211,13 +211,13 @@ namespace OpenSim.Data.SQLite if (m_FieldMap[name].GetValue(es) is bool) { if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.Add(":"+name, "1"); + cmd.Parameters.AddWithValue(":"+name, "1"); else - cmd.Parameters.Add(":"+name, "0"); + cmd.Parameters.AddWithValue(":"+name, "0"); } else { - cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); } } @@ -236,7 +236,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", es.EstateID); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID); IDataReader r = cmd.ExecuteReader(); @@ -260,7 +260,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "delete from estateban where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); cmd.ExecuteNonQuery(); @@ -270,8 +270,8 @@ namespace OpenSim.Data.SQLite foreach (EstateBan b in es.EstateBans) { - cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); - cmd.Parameters.Add(":bannedUUID", b.BannedUserID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":bannedUUID", b.BannedUserID.ToString()); cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); @@ -283,7 +283,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "delete from "+table+" where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", EstateID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); cmd.ExecuteNonQuery(); @@ -293,8 +293,8 @@ namespace OpenSim.Data.SQLite foreach (UUID uuid in data) { - cmd.Parameters.Add(":EstateID", EstateID.ToString()); - cmd.Parameters.Add(":uuid", uuid.ToString()); + cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); + cmd.Parameters.AddWithValue(":uuid", uuid.ToString()); cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); @@ -308,7 +308,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", EstateID); + cmd.Parameters.AddWithValue(":EstateID", EstateID); IDataReader r = cmd.ExecuteReader(); @@ -333,7 +333,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.Add(":EstateID", estateID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); return DoLoad(cmd, UUID.Zero, false); } @@ -347,7 +347,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.Add(":EstateName", search); + cmd.Parameters.AddWithValue(":EstateName", search); IDataReader r = cmd.ExecuteReader(); @@ -365,8 +365,8 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.Add(":RegionID", regionID.ToString()); - cmd.Parameters.Add(":EstateID", estateID.ToString()); + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); if (cmd.ExecuteNonQuery() == 0) return false; diff --git a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs b/OpenSim/Data/SQLiteNG/SQLiteFramework.cs index 20b508515a..cf114d1a07 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteFramework.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -55,11 +55,14 @@ namespace OpenSim.Data.SQLite { lock (connection) { +/* SqliteConnection newConnection = (SqliteConnection)((ICloneable)connection).Clone(); newConnection.Open(); cmd.Connection = newConnection; +*/ + cmd.Connection = connection; //Console.WriteLine("XXX " + cmd.CommandText); return cmd.ExecuteNonQuery(); @@ -70,11 +73,12 @@ namespace OpenSim.Data.SQLite { lock (connection) { - SqliteConnection newConnection = - (SqliteConnection)((ICloneable)connection).Clone(); - newConnection.Open(); + //SqliteConnection newConnection = + // (SqliteConnection)((ICloneable)connection).Clone(); + //newConnection.Open(); - cmd.Connection = newConnection; + //cmd.Connection = newConnection; + cmd.Connection = connection; //Console.WriteLine("XXX " + cmd.CommandText); return cmd.ExecuteReader(); diff --git a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs b/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs index 0b121826d6..b06853ce68 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -47,7 +47,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = :PrincipalID", m_Realm); - cmd.Parameters.Add(":PrincipalID", userID.ToString()); + cmd.Parameters.AddWithValue(":PrincipalID", userID.ToString()); return DoQuery(cmd); @@ -58,8 +58,8 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm); - cmd.Parameters.Add(":PrincipalID", principalID.ToString()); - cmd.Parameters.Add(":Friend", friend); + cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue(":Friend", friend); ExecuteNonQuery(cmd, cmd.Connection); diff --git a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs index b39bb19fb7..3c70aeffc4 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; @@ -59,19 +59,21 @@ namespace OpenSim.Data.SQLite if (!m_initialized) { m_Connection = new SqliteConnection(connectionString); + Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString)); m_Connection.Open(); if (storeName != String.Empty) { Assembly assem = GetType().Assembly; - SqliteConnection newConnection = - (SqliteConnection)((ICloneable)m_Connection).Clone(); - newConnection.Open(); + //SqliteConnection newConnection = + // (SqliteConnection)((ICloneable)m_Connection).Clone(); + //newConnection.Open(); - Migration m = new Migration(newConnection, assem, storeName); + //Migration m = new Migration(newConnection, assem, storeName); + Migration m = new Migration(m_Connection, assem, storeName); m.Update(); - newConnection.Close(); - newConnection.Dispose(); + //newConnection.Close(); + //newConnection.Dispose(); } m_initialized = true; @@ -197,7 +199,7 @@ namespace OpenSim.Data.SQLite result.Add(row); } - CloseCommand(cmd); + //CloseCommand(cmd); return result.ToArray(); } diff --git a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs index a5e051726e..ece2495c2b 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; @@ -98,11 +98,13 @@ namespace OpenSim.Data.SQLite ds.Tables.Add(createInventoryFoldersTable()); invFoldersDa.Fill(ds.Tables["inventoryfolders"]); setupFoldersCommands(invFoldersDa, conn); + CreateDataSetMapping(invFoldersDa, "inventoryfolders"); m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); ds.Tables.Add(createInventoryItemsTable()); invItemsDa.Fill(ds.Tables["inventoryitems"]); setupItemsCommands(invItemsDa, conn); + CreateDataSetMapping(invItemsDa, "inventoryitems"); m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); ds.AcceptChanges(); @@ -728,6 +730,15 @@ namespace OpenSim.Data.SQLite * **********************************************************************/ + protected void CreateDataSetMapping(IDataAdapter da, string tableName) + { + ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); + foreach (DataColumn col in ds.Tables[tableName].Columns) + { + dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); + } + } + /// /// Create the "inventoryitems" table /// diff --git a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs index d2ba9ae298..bad8adc425 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs @@ -32,7 +32,7 @@ using System.Drawing; using System.IO; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; @@ -87,119 +87,139 @@ namespace OpenSim.Data.SQLite /// the connection string public void Initialise(string connectionString) { - m_connectionString = connectionString; - - ds = new DataSet(); - - m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); - m_conn = new SqliteConnection(m_connectionString); - m_conn.Open(); - - - - SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); - primDa = new SqliteDataAdapter(primSelectCmd); - // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); - - SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); - shapeDa = new SqliteDataAdapter(shapeSelectCmd); - // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); - - SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); - itemsDa = new SqliteDataAdapter(itemsSelectCmd); - - SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); - terrainDa = new SqliteDataAdapter(terrainSelectCmd); - - SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); - landDa = new SqliteDataAdapter(landSelectCmd); - - SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); - landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); - - SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); - regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); - // This actually does the roll forward assembly stuff - Assembly assem = GetType().Assembly; - Migration m = new Migration(m_conn, assem, "RegionStore"); - m.Update(); - - lock (ds) + try { - ds.Tables.Add(createPrimTable()); - setupPrimCommands(primDa, m_conn); - primDa.Fill(ds.Tables["prims"]); + m_connectionString = connectionString; - ds.Tables.Add(createShapeTable()); - setupShapeCommands(shapeDa, m_conn); + ds = new DataSet("Region"); - ds.Tables.Add(createItemsTable()); - setupItemsCommands(itemsDa, m_conn); - itemsDa.Fill(ds.Tables["primitems"]); + m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); + m_conn = new SqliteConnection(m_connectionString); + m_conn.Open(); - ds.Tables.Add(createTerrainTable()); - setupTerrainCommands(terrainDa, m_conn); + SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); + //SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); + //primDa = new SqliteDataAdapter(primSelectCmd); + primDa = new SqliteDataAdapter(primSelect, m_connectionString); + // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); - ds.Tables.Add(createLandTable()); - setupLandCommands(landDa, m_conn); + SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); + shapeDa = new SqliteDataAdapter(shapeSelectCmd); + // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); - ds.Tables.Add(createLandAccessListTable()); - setupLandAccessCommands(landAccessListDa, m_conn); + SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); + itemsDa = new SqliteDataAdapter(itemsSelectCmd); - ds.Tables.Add(createRegionSettingsTable()); - - setupRegionSettingsCommands(regionSettingsDa, m_conn); + SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); + terrainDa = new SqliteDataAdapter(terrainSelectCmd); - // WORKAROUND: This is a work around for sqlite on - // windows, which gets really unhappy with blob columns - // that have no sample data in them. At some point we - // need to actually find a proper way to handle this. - try + SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); + landDa = new SqliteDataAdapter(landSelectCmd); + + SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); + landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); + + SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); + regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); + // This actually does the roll forward assembly stuff + Assembly assem = GetType().Assembly; + Migration m = new Migration(m_conn, assem, "RegionStore"); + m.Update(); + + lock (ds) { - shapeDa.Fill(ds.Tables["primshapes"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on primshapes table"); - } + //ds.Tables.Add(createPrimTable()); + //setupPrimCommands(primDa, m_conn); + //primDa.Fill(ds.Tables["prims"]); + primDa.Fill(ds, "prims"); - try - { - terrainDa.Fill(ds.Tables["terrain"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on terrain table"); - } + ds.Tables.Add(createShapeTable()); + setupShapeCommands(shapeDa, m_conn); - try - { - landDa.Fill(ds.Tables["land"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on land table"); - } + ds.Tables.Add(createItemsTable()); + setupItemsCommands(itemsDa, m_conn); + itemsDa.Fill(ds.Tables["primitems"]); - try - { - landAccessListDa.Fill(ds.Tables["landaccesslist"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); - } + ds.Tables.Add(createTerrainTable()); + setupTerrainCommands(terrainDa, m_conn); - try - { - regionSettingsDa.Fill(ds.Tables["regionsettings"]); + ds.Tables.Add(createLandTable()); + setupLandCommands(landDa, m_conn); + + ds.Tables.Add(createLandAccessListTable()); + setupLandAccessCommands(landAccessListDa, m_conn); + + ds.Tables.Add(createRegionSettingsTable()); + + setupRegionSettingsCommands(regionSettingsDa, m_conn); + + // WORKAROUND: This is a work around for sqlite on + // windows, which gets really unhappy with blob columns + // that have no sample data in them. At some point we + // need to actually find a proper way to handle this. + try + { + shapeDa.Fill(ds.Tables["primshapes"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on primshapes table"); + } + + try + { + terrainDa.Fill(ds.Tables["terrain"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on terrain table"); + } + + try + { + landDa.Fill(ds.Tables["land"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on land table"); + } + + try + { + landAccessListDa.Fill(ds.Tables["landaccesslist"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); + } + + try + { + regionSettingsDa.Fill(ds.Tables["regionsettings"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); + } + + // We have to create a data set mapping for every table, otherwise the IDataAdaptor.Update() will not populate rows with values! + // Not sure exactly why this is - this kind of thing was not necessary before - justincc 20100409 + CreateDataSetMapping(primDa, "prims"); + CreateDataSetMapping(shapeDa, "primshapes"); + CreateDataSetMapping(itemsDa, "primitems"); + CreateDataSetMapping(terrainDa, "terrain"); + CreateDataSetMapping(landDa, "land"); + CreateDataSetMapping(landAccessListDa, "landaccesslist"); + CreateDataSetMapping(regionSettingsDa, "regionsettings"); } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); - } - return; } + catch (Exception e) + { + m_log.Error(e); + Environment.Exit(23); + } + + return; } public void Dispose() @@ -603,7 +623,7 @@ namespace OpenSim.Data.SQLite } } } - rev = (int) row["Revision"]; + rev = Convert.ToInt32(row["Revision"]); } else { @@ -755,6 +775,7 @@ namespace OpenSim.Data.SQLite /// public void Commit() { + m_log.Debug("[SQLITE]: Starting commit"); lock (ds) { primDa.Update(ds, "prims"); @@ -769,18 +790,11 @@ namespace OpenSim.Data.SQLite { regionSettingsDa.Update(ds, "regionsettings"); } - catch (SqliteExecutionException SqlEx) + catch (SqliteException SqlEx) { - if (SqlEx.Message.Contains("logic error")) - { - throw new Exception( - "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", - SqlEx); - } - else - { - throw SqlEx; - } + throw new Exception( + "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", + SqlEx); } ds.AcceptChanges(); } @@ -802,6 +816,15 @@ namespace OpenSim.Data.SQLite * **********************************************************************/ + protected void CreateDataSetMapping(IDataAdapter da, string tableName) + { + ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); + foreach (DataColumn col in ds.Tables[tableName].Columns) + { + dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); + } + } + /// /// /// @@ -1964,6 +1987,7 @@ namespace OpenSim.Data.SQLite sql += ") values (:"; sql += String.Join(", :", cols); sql += ")"; + m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql); SqliteCommand cmd = new SqliteCommand(sql); // this provides the binding for all our parameters, so @@ -2259,6 +2283,36 @@ namespace OpenSim.Data.SQLite return DbType.String; } } + + static void PrintDataSet(DataSet ds) + { + // Print out any name and extended properties. + Console.WriteLine("DataSet is named: {0}", ds.DataSetName); + foreach (System.Collections.DictionaryEntry de in ds.ExtendedProperties) + { + Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value); + } + Console.WriteLine(); + foreach (DataTable dt in ds.Tables) + { + Console.WriteLine("=> {0} Table:", dt.TableName); + // Print out the column names. + for (int curCol = 0; curCol < dt.Columns.Count; curCol++) + { + Console.Write(dt.Columns[curCol].ColumnName + "\t"); + } + Console.WriteLine("\n----------------------------------"); + // Print the DataTable. + for (int curRow = 0; curRow < dt.Rows.Count; curRow++) + { + for (int curCol = 0; curCol < dt.Columns.Count; curCol++) + { + Console.Write(dt.Rows[curRow][curCol].ToString() + "\t"); + } + Console.WriteLine(); + } + } + } } } diff --git a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs b/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs index 67cf7165b1..893f105604 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -66,7 +66,7 @@ namespace OpenSim.Data.SQLite if (words.Length == 1) { - cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", + cmd.CommandText = String.Format("select * from {0} where ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", m_Realm, scopeID.ToString(), words[0]); } else diff --git a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs b/OpenSim/Data/SQLiteNG/SQLiteUtils.cs index 4a835ce52f..07c6b69a3b 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteUtils.cs @@ -27,7 +27,7 @@ using System; using System.Data; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { diff --git a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs b/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs index a66e0c6fe0..be1d0412fd 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs @@ -29,7 +29,7 @@ using System; using System.Data; using System.Reflection; using System.Collections.Generic; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using log4net; using OpenMetaverse; using OpenSim.Framework; @@ -147,7 +147,7 @@ namespace OpenSim.Data.SQLite } reader.Close(); - CloseCommand(cmd); + //CloseCommand(cmd); return perms; } diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs deleted file mode 100644 index 0c2f5dfefc..0000000000 --- a/OpenSim/Data/SQLiteNG/Tests/SQLiteAssetTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteAssetTest : BasicAssetTest - { - public string file; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - db = new SQLiteAssetData(); - db.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - db.Dispose(); - File.Delete(file); - } - } -} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs deleted file mode 100644 index 30f66414a9..0000000000 --- a/OpenSim/Data/SQLiteNG/Tests/SQLiteEstateTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteEstateTest : BasicEstateTest - { - public string file = "regiontest.db"; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - db = new SQLiteEstateStore(); - db.Initialise(connect); - regionDb = new SQLiteRegionData(); - regionDb.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - regionDb.Dispose(); - } - } -} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs deleted file mode 100644 index 98458a30ec..0000000000 --- a/OpenSim/Data/SQLiteNG/Tests/SQLiteInventoryTest.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteInventoryTest : BasicInventoryTest - { - public string file; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - - db = new SQLiteInventoryStore(); - db.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - db.Dispose(); - File.Delete(file); - } - } -} diff --git a/OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs b/OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs deleted file mode 100644 index abb97cfa4f..0000000000 --- a/OpenSim/Data/SQLiteNG/Tests/SQLiteRegionTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteRegionTest : BasicRegionTest - { - public string file = "regiontest.db"; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - db = new SQLiteRegionData(); - db.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - db.Dispose(); - File.Delete(file); - } - } -} diff --git a/prebuild.xml b/prebuild.xml index d6b6361182..406d57eaae 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2231,7 +2231,6 @@ - @@ -2239,7 +2238,7 @@ - + From fcbac431148b8f0e538cfae330dd49bc898e0c03 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 19:14:43 +0100 Subject: [PATCH 008/260] minor: correct the assembly information for SQLiteNG --- OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs b/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs index d45ab5092a..4aeb67b375 100644 --- a/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs @@ -32,11 +32,11 @@ using System.Runtime.InteropServices; // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly : AssemblyTitle("OpenSim.Data.SQLite")] +[assembly : AssemblyTitle("OpenSim.Data.SQLiteNG")] [assembly : AssemblyDescription("")] [assembly : AssemblyConfiguration("")] [assembly : AssemblyCompany("http://opensimulator.org")] -[assembly : AssemblyProduct("OpenSim.Data.SQLite")] +[assembly : AssemblyProduct("OpenSim.Data.SQLiteNG")] [assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] [assembly : AssemblyTrademark("")] [assembly : AssemblyCulture("")] From e78f874cfd341cf13b88013095e06dd966f51fe6 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey Date: Fri, 23 Apr 2010 19:40:15 +0100 Subject: [PATCH 009/260] put SQLiteNG classes in their own namespace to avoid confusion --- OpenSim/Data/SQLiteNG/SQLiteAssetData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteEstateData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteFramework.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteRegionData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteUtils.cs | 2 +- OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs index 636bf8645b..9b34a21592 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs @@ -34,7 +34,7 @@ using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// An asset storage interface for the SQLite database system diff --git a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs index 086ac0a972..4a5dc2eeba 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs @@ -33,7 +33,7 @@ using OpenMetaverse; using OpenSim.Framework; using Mono.Data.Sqlite; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { public class SQLiteAuthenticationData : SQLiteFramework, IAuthenticationData { diff --git a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs b/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs index c093884db4..d0fd49c5ae 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs @@ -35,7 +35,7 @@ using OpenMetaverse; using OpenSim.Framework; using Mono.Data.Sqlite; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// A SQLite Interface for Avatar Data diff --git a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs b/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs index 9dd4a2e69b..2e2d717cc9 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs @@ -35,7 +35,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { public class SQLiteEstateStore : IEstateDataStore { diff --git a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs b/OpenSim/Data/SQLiteNG/SQLiteFramework.cs index cf114d1a07..f0ddc597bd 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteFramework.cs @@ -33,7 +33,7 @@ using OpenMetaverse; using OpenSim.Framework; using Mono.Data.Sqlite; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// A database interface class to a user profile storage system diff --git a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs b/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs index b06853ce68..702a1d8643 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs @@ -33,7 +33,7 @@ using OpenMetaverse; using OpenSim.Framework; using Mono.Data.Sqlite; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { public class SQLiteFriendsData : SQLiteGenericTableHandler, IFriendsData { diff --git a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs index 3c70aeffc4..632c5bf8c9 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs @@ -35,7 +35,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { public class SQLiteGenericTableHandler : SQLiteFramework where T: class, new() { diff --git a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs index ece2495c2b..9207ca3cc4 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs @@ -34,7 +34,7 @@ using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// An Inventory Interface to the SQLite database diff --git a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs index bad8adc425..ee454db5e3 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs @@ -38,7 +38,7 @@ using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// A RegionData Interface to the SQLite database diff --git a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs b/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs index 893f105604..f77159c772 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs @@ -33,7 +33,7 @@ using OpenMetaverse; using OpenSim.Framework; using Mono.Data.Sqlite; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { public class SQLiteUserAccountData : SQLiteGenericTableHandler, IUserAccountData { diff --git a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs b/OpenSim/Data/SQLiteNG/SQLiteUtils.cs index 07c6b69a3b..82a2e37993 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteUtils.cs @@ -29,7 +29,7 @@ using System; using System.Data; using Mono.Data.Sqlite; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// A base class for methods needed by all SQLite database classes diff --git a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs b/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs index be1d0412fd..a0c17f8fd9 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs @@ -34,7 +34,7 @@ using log4net; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// A MySQL Interface for the Asset Server From b5ec101cc8af2a4b3313ba7466b79b5fa6961c95 Mon Sep 17 00:00:00 2001 From: dahlia Date: Fri, 23 Apr 2010 12:05:10 -0700 Subject: [PATCH 010/260] add a comment about deprecating IRegionModule --- OpenSim/Region/Framework/Interfaces/IRegionModule.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenSim/Region/Framework/Interfaces/IRegionModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionModule.cs index 8eb906c7f8..8365fe300e 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionModule.cs @@ -30,6 +30,9 @@ using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.Framework.Interfaces { + /// + /// DEPRECATED! Use INonSharedRegionModule or ISharedRegionModule instead + /// public interface IRegionModule { void Initialise(Scene scene, IConfigSource source); From 1b488c25816fd01e893f64cec1a89f5dbac21104 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 20:08:24 +0100 Subject: [PATCH 011/260] dispose of the DbCommand used to execute migrations after we've finished with it rather than within the loop disposing of it within the loop causes Mono.Data.Sqlite.dll to get upset, and it's the wrong behaviour anyway --- OpenSim/Data/Migration.cs | 3 ++- OpenSim/Data/SQLiteNG/SQLiteAssetData.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 68e25ef0aa..0fb9e783bd 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -159,8 +159,9 @@ namespace OpenSim.Data UpdateVersion(_type, newversion); } version = newversion; - cmd.Dispose(); } + + cmd.Dispose(); } // private int MaxVersion() diff --git a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs index 636bf8645b..9b34a21592 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs @@ -34,7 +34,7 @@ using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLite +namespace OpenSim.Data.SQLiteNG { /// /// An asset storage interface for the SQLite database system From 6e99e5b47a2d792cca46a55789ca56de9fb45ef2 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 20:56:24 +0100 Subject: [PATCH 012/260] get region prim saving working properly in SQLiteNG it wasn't working because of debug work that I'd forgotton to take out --- OpenSim/Data/SQLiteNG/SQLiteRegionData.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs index ee454db5e3..289d626543 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs @@ -98,10 +98,7 @@ namespace OpenSim.Data.SQLiteNG m_conn.Open(); SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); - //SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); - //primDa = new SqliteDataAdapter(primSelectCmd); - primDa = new SqliteDataAdapter(primSelect, m_connectionString); - // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); + primDa = new SqliteDataAdapter(primSelectCmd); SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); shapeDa = new SqliteDataAdapter(shapeSelectCmd); @@ -128,17 +125,14 @@ namespace OpenSim.Data.SQLiteNG lock (ds) { - //ds.Tables.Add(createPrimTable()); - //setupPrimCommands(primDa, m_conn); - //primDa.Fill(ds.Tables["prims"]); - primDa.Fill(ds, "prims"); + ds.Tables.Add(createPrimTable()); + setupPrimCommands(primDa, m_conn); ds.Tables.Add(createShapeTable()); setupShapeCommands(shapeDa, m_conn); ds.Tables.Add(createItemsTable()); setupItemsCommands(itemsDa, m_conn); - itemsDa.Fill(ds.Tables["primitems"]); ds.Tables.Add(createTerrainTable()); setupTerrainCommands(terrainDa, m_conn); @@ -150,13 +144,21 @@ namespace OpenSim.Data.SQLiteNG setupLandAccessCommands(landAccessListDa, m_conn); ds.Tables.Add(createRegionSettingsTable()); - setupRegionSettingsCommands(regionSettingsDa, m_conn); // WORKAROUND: This is a work around for sqlite on // windows, which gets really unhappy with blob columns // that have no sample data in them. At some point we // need to actually find a proper way to handle this. + try + { + primDa.Fill(ds.Tables["prims"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on prims table"); + } + try { shapeDa.Fill(ds.Tables["primshapes"]); @@ -204,6 +206,7 @@ namespace OpenSim.Data.SQLiteNG // We have to create a data set mapping for every table, otherwise the IDataAdaptor.Update() will not populate rows with values! // Not sure exactly why this is - this kind of thing was not necessary before - justincc 20100409 + // Possibly because we manually set up our own DataTables before connecting to the database CreateDataSetMapping(primDa, "prims"); CreateDataSetMapping(shapeDa, "primshapes"); CreateDataSetMapping(itemsDa, "primitems"); From 10e18f465e0540e9d09d47462c8166cb60a38426 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 21:29:09 +0100 Subject: [PATCH 013/260] minor: eliminate configuration section handling deprecation of OpenSim.DataStore.MonoSqlite.dll, since this hasn't exist for a long, long time --- OpenSim/Region/Application/ConfigurationLoader.cs | 6 ------ bin/OpenSim.ini.example | 1 - 2 files changed, 7 deletions(-) diff --git a/OpenSim/Region/Application/ConfigurationLoader.cs b/OpenSim/Region/Application/ConfigurationLoader.cs index 4ca6595501..cac5fa99c8 100644 --- a/OpenSim/Region/Application/ConfigurationLoader.cs +++ b/OpenSim/Region/Application/ConfigurationLoader.cs @@ -349,12 +349,6 @@ namespace OpenSim m_configSettings.See_into_region_from_neighbor = startupConfig.GetBoolean("see_into_this_sim_from_neighbor", true); m_configSettings.StorageDll = startupConfig.GetString("storage_plugin"); - if (m_configSettings.StorageDll == "OpenSim.DataStore.MonoSqlite.dll") - { - m_configSettings.StorageDll = "OpenSim.Data.SQLite.dll"; - m_log.Warn("WARNING: OpenSim.DataStore.MonoSqlite.dll is deprecated. Set storage_plugin to OpenSim.Data.SQLite.dll."); - Thread.Sleep(3000); - } m_configSettings.StorageConnectionString = startupConfig.GetString("storage_connection_string"); diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 06306885db..d49692e6c0 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1232,7 +1232,6 @@ ;; default standalone, overridable in StandaloneCommon.ini StorageProvider = "OpenSim.Data.SQLite.dll" - [AssetService] DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" AssetLoaderArgs = "assets/AssetSets.xml" From 847cc4fceb8ac363cdfdb2a5360b93230c9068e5 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 21:31:08 +0100 Subject: [PATCH 014/260] remove [DatabaseService] section from OpenSim.ini since this is always present in included configuration files --- bin/OpenSim.ini.example | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index d49692e6c0..7d65e77bec 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1228,10 +1228,6 @@ ;; These defaults allow OpenSim to work out of the box with ;; zero configuration ;; -[DatabaseService] - ;; default standalone, overridable in StandaloneCommon.ini - StorageProvider = "OpenSim.Data.SQLite.dll" - [AssetService] DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" AssetLoaderArgs = "assets/AssetSets.xml" From 15d42d211161b304bc4281018b891a063be4612d Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 23 Apr 2010 22:06:02 +0100 Subject: [PATCH 015/260] Add instructions for using SQLiteNG to OpenSim.ini and config-include/StandaloneCommon.ini Unfortunately, database settings need to be changed in two places. --- bin/OpenSim.ini.example | 6 ++++-- bin/config-include/StandaloneCommon.ini.example | 8 ++++++-- .../storage/SQLiteNGStandalone.ini | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 bin/config-include/storage/SQLiteNGStandalone.ini diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 7d65e77bec..59bce363cd 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -112,9 +112,11 @@ ;storage_plugin = "OpenSim.Data.Null.dll" ; --- To use sqlite as region storage: - ; NOTE: SQLite and OpenSim are not functioning properly with Mono 2.4.3 or greater. - ; If you are using Mono you probably should be using MySQL + ; PLEASE NOTE: If you use want to use SQLite with Mono 2.6 and above, you must use the SQLiteNG plugin rather than the existing SQLite one + ; do this by commenting out the OpenSim.Data.SQLite.dll line below and uncommenting the OpenSim.Data.SQLiteNG.dll one + ; You will also need to do the same thing in config-include/StandaloneCommon.ini if you are running in standalone mode storage_plugin = "OpenSim.Data.SQLite.dll" + ; storage_plugin = "OpenSim.Data.SQLiteNG.dll" storage_connection_string="URI=file:OpenSim.db,version=3"; ; --- To use MySQL storage, supply your own connection string (this is only an example): diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index f89c67a779..74bdbe2d23 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -6,10 +6,14 @@ ; ; SQLite - ; Uncomment this line if you want to use sqlite storage + ; Uncomment this line if you want to use sqlite storage with Mono 2.4 Include-Storage = "config-include/storage/SQLiteStandalone.ini"; - ; For MySql. + ; If you want to use sqlite with Mono 2.6 and above, uncomment this line instead. + ; Don't forget to do the same thing for the storage_plugin setting in OpenSim.ini + ; Include-Storage = "config-include/storage/SQLiteNGStandalone.ini"; + + ; MySql ; Uncomment these lines if you want to use mysql storage ; Change the connection string to your db details ;StorageProvider = "OpenSim.Data.MySQL.dll" diff --git a/bin/config-include/storage/SQLiteNGStandalone.ini b/bin/config-include/storage/SQLiteNGStandalone.ini new file mode 100644 index 0000000000..ba00acaf6c --- /dev/null +++ b/bin/config-include/storage/SQLiteNGStandalone.ini @@ -0,0 +1,16 @@ +; These are the initialization settings for running OpenSim Standalone with an SQLite database + +[DatabaseService] + StorageProvider = "OpenSim.Data.SQLiteNG.dll" + +[AvatarService] + ConnectionString = "URI=file:avatars.db,version=3" + +[AuthenticationService] + ConnectionString = "URI=file:auth.db,version=3" + +[UserAccountService] + ConnectionString = "URI=file:userprofiles.db,version=3" + +[FriendsService] + ConnectionString = "URI=file:friends.db,version=3" From 2ac00b7f96b01cf9494d2a5fef9a84443301a04c Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 26 Apr 2010 14:52:22 -0700 Subject: [PATCH 016/260] * Commenting SQLiteNG out of prebuild.xml, because it's making compile fail in Windows. Justin: you forgot to add Mono.Sqlite.dll, and I can't figure out where to grab it from! * IRegionModule.cs wants to be committed too -- EOF. --- OpenSim/Region/Framework/Interfaces/IRegionModule.cs | 4 ++-- prebuild.xml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/Framework/Interfaces/IRegionModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionModule.cs index 8365fe300e..e25a6e86dd 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionModule.cs @@ -30,8 +30,8 @@ using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.Framework.Interfaces { - /// - /// DEPRECATED! Use INonSharedRegionModule or ISharedRegionModule instead + /// + /// DEPRECATED! Use INonSharedRegionModule or ISharedRegionModule instead /// public interface IRegionModule { diff --git a/prebuild.xml b/prebuild.xml index 602750ac84..dd163f0238 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2215,6 +2215,7 @@ + + From 2686573b1cfd2a4493d589b6f2987c3a61829b3a Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 26 Apr 2010 15:03:47 -0700 Subject: [PATCH 017/260] Uncommenting SQLiteNG project again. I found a Mono.Data.Sqlite.dll that makes the compilation in Windows happy again. Not sure if this is the right dll, Justin -- I grabbed it somewhere from mono 2.4.6 for Windows. --- bin/Mono.Data.Sqlite.dll | Bin 0 -> 169472 bytes prebuild.xml | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 bin/Mono.Data.Sqlite.dll diff --git a/bin/Mono.Data.Sqlite.dll b/bin/Mono.Data.Sqlite.dll new file mode 100644 index 0000000000000000000000000000000000000000..2b81a44957dcce036ee794bca4bcfa37c8e543f7 GIT binary patch literal 169472 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

TJoGQ2oh&)XCBoIg2M020u^S~H-cM`XO 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 literal 0 HcmV?d00001 diff --git a/prebuild.xml b/prebuild.xml index dd163f0238..123f56955e 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2215,7 +2215,6 @@ - + From 31d040dc1e3126ff10e0ce4a1553cc6fa056b449 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 26 Apr 2010 17:40:00 -0700 Subject: [PATCH 018/260] Better error message. --- .../Servers/HttpServer/SynchronousRestFormsRequester.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs b/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs index 4543fd5e99..b0cf34d16e 100644 --- a/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs +++ b/OpenSim/Framework/Servers/HttpServer/SynchronousRestFormsRequester.cs @@ -81,7 +81,7 @@ namespace OpenSim.Framework.Servers.HttpServer } catch (Exception e) { - m_log.DebugFormat("[FORMS]: exception occured on sending request {0}", e.Message); + m_log.DebugFormat("[FORMS]: exception occured on sending request to {0}: {1}", requestUrl, e.Message); } finally { From 6928ec024060bb7181553206c9ee6a125e7c9a2d Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 27 Apr 2010 00:25:29 +0100 Subject: [PATCH 019/260] Add a parameter to prim inventory update to prevent event firing --- .../Framework/Interfaces/IEntityInventory.cs | 1 + .../Scenes/SceneObjectPartInventory.cs | 41 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs index 2b90960936..fd43923cb3 100644 --- a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs +++ b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs @@ -161,6 +161,7 @@ namespace OpenSim.Region.Framework.Interfaces /// in this prim's inventory. /// false if the item did not exist, true if the update occurred successfully bool UpdateInventoryItem(TaskInventoryItem item); + bool UpdateInventoryItem(TaskInventoryItem item, bool fireScriptEvents); ///

public void Commit() { + m_log.Debug("[SQLITE]: Starting commit"); lock (ds) { primDa.Update(ds, "prims"); @@ -769,18 +793,11 @@ namespace OpenSim.Data.SQLite { regionSettingsDa.Update(ds, "regionsettings"); } - catch (SqliteExecutionException SqlEx) + catch (SqliteException SqlEx) { - if (SqlEx.Message.Contains("logic error")) - { - throw new Exception( - "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", - SqlEx); - } - else - { - throw SqlEx; - } + throw new Exception( + "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", + SqlEx); } ds.AcceptChanges(); } @@ -802,6 +819,15 @@ namespace OpenSim.Data.SQLite * **********************************************************************/ + protected void CreateDataSetMapping(IDataAdapter da, string tableName) + { + ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); + foreach (DataColumn col in ds.Tables[tableName].Columns) + { + dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); + } + } + /// /// /// @@ -1964,6 +1990,7 @@ namespace OpenSim.Data.SQLite sql += ") values (:"; sql += String.Join(", :", cols); sql += ")"; + m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql); SqliteCommand cmd = new SqliteCommand(sql); // this provides the binding for all our parameters, so @@ -2259,6 +2286,36 @@ namespace OpenSim.Data.SQLite return DbType.String; } } + + static void PrintDataSet(DataSet ds) + { + // Print out any name and extended properties. + Console.WriteLine("DataSet is named: {0}", ds.DataSetName); + foreach (System.Collections.DictionaryEntry de in ds.ExtendedProperties) + { + Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value); + } + Console.WriteLine(); + foreach (DataTable dt in ds.Tables) + { + Console.WriteLine("=> {0} Table:", dt.TableName); + // Print out the column names. + for (int curCol = 0; curCol < dt.Columns.Count; curCol++) + { + Console.Write(dt.Columns[curCol].ColumnName + "\t"); + } + Console.WriteLine("\n----------------------------------"); + // Print the DataTable. + for (int curRow = 0; curRow < dt.Rows.Count; curRow++) + { + for (int curCol = 0; curCol < dt.Columns.Count; curCol++) + { + Console.Write(dt.Rows[curRow][curCol].ToString() + "\t"); + } + Console.WriteLine(); + } + } + } } } diff --git a/OpenSim/Data/SQLite/SQLiteUserAccountData.cs b/OpenSim/Data/SQLite/SQLiteUserAccountData.cs index 67cf7165b1..893f105604 100644 --- a/OpenSim/Data/SQLite/SQLiteUserAccountData.cs +++ b/OpenSim/Data/SQLite/SQLiteUserAccountData.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -66,7 +66,7 @@ namespace OpenSim.Data.SQLite if (words.Length == 1) { - cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", + cmd.CommandText = String.Format("select * from {0} where ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", m_Realm, scopeID.ToString(), words[0]); } else diff --git a/OpenSim/Data/SQLite/SQLiteUtils.cs b/OpenSim/Data/SQLite/SQLiteUtils.cs index 4a835ce52f..07c6b69a3b 100644 --- a/OpenSim/Data/SQLite/SQLiteUtils.cs +++ b/OpenSim/Data/SQLite/SQLiteUtils.cs @@ -27,7 +27,7 @@ using System; using System.Data; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { diff --git a/OpenSim/Data/SQLite/SQLiteXInventoryData.cs b/OpenSim/Data/SQLite/SQLiteXInventoryData.cs index a66e0c6fe0..be1d0412fd 100644 --- a/OpenSim/Data/SQLite/SQLiteXInventoryData.cs +++ b/OpenSim/Data/SQLite/SQLiteXInventoryData.cs @@ -29,7 +29,7 @@ using System; using System.Data; using System.Reflection; using System.Collections.Generic; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using log4net; using OpenMetaverse; using OpenSim.Framework; @@ -147,7 +147,7 @@ namespace OpenSim.Data.SQLite } reader.Close(); - CloseCommand(cmd); + //CloseCommand(cmd); return perms; } diff --git a/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs b/OpenSim/Data/SQLiteLegacy/Properties/AssemblyInfo.cs similarity index 96% rename from OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs rename to OpenSim/Data/SQLiteLegacy/Properties/AssemblyInfo.cs index 4aeb67b375..609a024b73 100644 --- a/OpenSim/Data/SQLiteNG/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/SQLiteLegacy/Properties/AssemblyInfo.cs @@ -32,11 +32,11 @@ using System.Runtime.InteropServices; // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly : AssemblyTitle("OpenSim.Data.SQLiteNG")] +[assembly : AssemblyTitle("OpenSim.Data.SQLiteLegacy")] [assembly : AssemblyDescription("")] [assembly : AssemblyConfiguration("")] [assembly : AssemblyCompany("http://opensimulator.org")] -[assembly : AssemblyProduct("OpenSim.Data.SQLiteNG")] +[assembly : AssemblyProduct("OpenSim.Data.SQLiteLegacy")] [assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] [assembly : AssemblyTrademark("")] [assembly : AssemblyCulture("")] diff --git a/OpenSim/Data/SQLiteNG/Resources/001_AssetStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_AssetStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_AssetStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_AssetStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_AuthStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_AuthStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_AuthStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_AuthStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_Avatar.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_Avatar.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_Avatar.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_Avatar.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_FriendsStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_FriendsStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_FriendsStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_FriendsStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_InventoryStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_InventoryStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_InventoryStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_InventoryStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_UserAccount.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_UserAccount.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_UserAccount.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_UserAccount.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/001_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/001_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/001_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/001_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_AssetStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_AssetStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_AssetStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_AssetStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_AuthStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_AuthStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_AuthStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_AuthStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_FriendsStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_FriendsStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_FriendsStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_FriendsStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_InventoryStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_InventoryStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_InventoryStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_InventoryStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_UserAccount.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_UserAccount.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_UserAccount.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_UserAccount.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/002_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/002_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/002_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/002_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/003_AssetStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/003_AssetStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/003_AssetStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/003_AssetStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/003_InventoryStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/003_InventoryStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/003_InventoryStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/003_InventoryStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/003_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/003_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/003_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/003_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/003_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/003_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/003_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/003_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/004_AssetStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/004_AssetStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/004_AssetStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/004_AssetStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/004_InventoryStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/004_InventoryStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/004_InventoryStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/004_InventoryStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/004_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/004_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/004_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/004_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/004_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/004_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/004_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/004_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/005_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/005_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/005_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/005_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/005_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/005_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/005_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/005_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/006_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/006_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/006_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/006_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/006_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/006_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/006_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/006_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/007_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/007_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/007_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/007_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/007_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/007_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/007_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/007_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/008_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/008_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/008_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/008_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/008_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/008_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/008_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/008_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/009_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/009_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/009_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/009_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/009_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/009_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/009_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/009_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/010_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/010_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/010_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/010_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/010_UserStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/010_UserStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/010_UserStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/010_UserStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/011_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/011_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/011_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/011_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/012_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/012_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/012_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/012_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/013_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/013_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/013_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/013_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/014_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/014_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/014_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/014_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/015_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/015_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/015_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/015_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/016_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/016_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/016_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/016_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/017_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/017_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/017_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/017_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/018_RegionStore.sql b/OpenSim/Data/SQLiteLegacy/Resources/018_RegionStore.sql similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/018_RegionStore.sql rename to OpenSim/Data/SQLiteLegacy/Resources/018_RegionStore.sql diff --git a/OpenSim/Data/SQLiteNG/Resources/OpenSim.Data.SQLite.addin.xml b/OpenSim/Data/SQLiteLegacy/Resources/OpenSim.Data.SQLite.addin.xml similarity index 100% rename from OpenSim/Data/SQLiteNG/Resources/OpenSim.Data.SQLite.addin.xml rename to OpenSim/Data/SQLiteLegacy/Resources/OpenSim.Data.SQLite.addin.xml diff --git a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs similarity index 99% rename from OpenSim/Data/SQLiteNG/SQLiteAssetData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs index 9b34a21592..0d63deacd8 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs @@ -30,11 +30,11 @@ using System.Data; using System.Reflection; using System.Collections.Generic; using log4net; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// An asset storage interface for the SQLite database system @@ -137,7 +137,7 @@ namespace OpenSim.Data.SQLiteNG cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); - + cmd.ExecuteNonQuery(); } } @@ -340,4 +340,4 @@ namespace OpenSim.Data.SQLiteNG #endregion } -} +} \ No newline at end of file diff --git a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs similarity index 93% rename from OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs index 4a5dc2eeba..c64830a455 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs @@ -31,9 +31,9 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { public class SQLiteAuthenticationData : SQLiteFramework, IAuthenticationData { @@ -56,8 +56,13 @@ namespace OpenSim.Data.SQLiteNG m_Connection = new SqliteConnection(connectionString); m_Connection.Open(); - Migration m = new Migration(m_Connection, GetType().Assembly, "AuthStore"); - m.Update(); + using (SqliteConnection dbcon = (SqliteConnection)((ICloneable)m_Connection).Clone()) + { + dbcon.Open(); + Migration m = new Migration(dbcon, GetType().Assembly, "AuthStore"); + m.Update(); + dbcon.Close(); + } m_initialized = true; } @@ -108,7 +113,7 @@ namespace OpenSim.Data.SQLiteNG } finally { - //CloseCommand(cmd); + CloseCommand(cmd); } return null; @@ -151,14 +156,14 @@ namespace OpenSim.Data.SQLiteNG { if (ExecuteNonQuery(cmd, m_Connection) < 1) { - //CloseCommand(cmd); + CloseCommand(cmd); return false; } } catch (Exception e) { Console.WriteLine(e.ToString()); - //CloseCommand(cmd); + CloseCommand(cmd); return false; } } @@ -179,19 +184,19 @@ namespace OpenSim.Data.SQLiteNG { if (ExecuteNonQuery(cmd, m_Connection) < 1) { - //CloseCommand(cmd); + CloseCommand(cmd); return false; } } catch (Exception e) { Console.WriteLine(e.ToString()); - //CloseCommand(cmd); + CloseCommand(cmd); return false; } } - //CloseCommand(cmd); + CloseCommand(cmd); return true; } diff --git a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteAvatarData.cs similarity index 92% rename from OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteAvatarData.cs index d0fd49c5ae..660632ca07 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteAvatarData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteAvatarData.cs @@ -33,9 +33,9 @@ using System.Threading; using log4net; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// A SQLite Interface for Avatar Data @@ -55,8 +55,8 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm); - cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); - cmd.Parameters.AddWithValue(":Name", name); + cmd.Parameters.Add(":PrincipalID", principalID.ToString()); + cmd.Parameters.Add(":Name", name); try { @@ -67,7 +67,7 @@ namespace OpenSim.Data.SQLiteNG } finally { - //CloseCommand(cmd); + CloseCommand(cmd); } } } diff --git a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteEstateData.cs similarity index 87% rename from OpenSim/Data/SQLiteNG/SQLiteEstateData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteEstateData.cs index 2e2d717cc9..e135eaa524 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteEstateData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteEstateData.cs @@ -30,12 +30,12 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { public class SQLiteEstateStore : IEstateDataStore { @@ -62,8 +62,8 @@ namespace OpenSim.Data.SQLiteNG Migration m = new Migration(m_connection, assem, "EstateStore"); m.Update(); - //m_connection.Close(); - // m_connection.Open(); + m_connection.Close(); + m_connection.Open(); Type t = typeof(EstateSettings); m_Fields = t.GetFields(BindingFlags.NonPublic | @@ -87,7 +87,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.Add(":RegionID", regionID.ToString()); return DoLoad(cmd, regionID, create); } @@ -143,13 +143,13 @@ namespace OpenSim.Data.SQLiteNG if (m_FieldMap[name].GetValue(es) is bool) { if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.AddWithValue(":"+name, "1"); + cmd.Parameters.Add(":"+name, "1"); else - cmd.Parameters.AddWithValue(":"+name, "0"); + cmd.Parameters.Add(":"+name, "0"); } else { - cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); + cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); } } @@ -167,8 +167,8 @@ namespace OpenSim.Data.SQLiteNG r.Close(); cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); - cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); + cmd.Parameters.Add(":RegionID", regionID.ToString()); + cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); // This will throw on dupe key try @@ -211,13 +211,13 @@ namespace OpenSim.Data.SQLiteNG if (m_FieldMap[name].GetValue(es) is bool) { if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.AddWithValue(":"+name, "1"); + cmd.Parameters.Add(":"+name, "1"); else - cmd.Parameters.AddWithValue(":"+name, "0"); + cmd.Parameters.Add(":"+name, "0"); } else { - cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); + cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); } } @@ -236,7 +236,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID"; - cmd.Parameters.AddWithValue(":EstateID", es.EstateID); + cmd.Parameters.Add(":EstateID", es.EstateID); IDataReader r = cmd.ExecuteReader(); @@ -260,7 +260,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "delete from estateban where EstateID = :EstateID"; - cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); + cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); cmd.ExecuteNonQuery(); @@ -270,8 +270,8 @@ namespace OpenSim.Data.SQLiteNG foreach (EstateBan b in es.EstateBans) { - cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); - cmd.Parameters.AddWithValue(":bannedUUID", b.BannedUserID.ToString()); + cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + cmd.Parameters.Add(":bannedUUID", b.BannedUserID.ToString()); cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); @@ -283,7 +283,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "delete from "+table+" where EstateID = :EstateID"; - cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); + cmd.Parameters.Add(":EstateID", EstateID.ToString()); cmd.ExecuteNonQuery(); @@ -293,8 +293,8 @@ namespace OpenSim.Data.SQLiteNG foreach (UUID uuid in data) { - cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); - cmd.Parameters.AddWithValue(":uuid", uuid.ToString()); + cmd.Parameters.Add(":EstateID", EstateID.ToString()); + cmd.Parameters.Add(":uuid", uuid.ToString()); cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); @@ -308,7 +308,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID"; - cmd.Parameters.AddWithValue(":EstateID", EstateID); + cmd.Parameters.Add(":EstateID", EstateID); IDataReader r = cmd.ExecuteReader(); @@ -333,7 +333,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); + cmd.Parameters.Add(":EstateID", estateID.ToString()); return DoLoad(cmd, UUID.Zero, false); } @@ -347,7 +347,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.AddWithValue(":EstateName", search); + cmd.Parameters.Add(":EstateName", search); IDataReader r = cmd.ExecuteReader(); @@ -365,8 +365,8 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); - cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); + cmd.Parameters.Add(":RegionID", regionID.ToString()); + cmd.Parameters.Add(":EstateID", estateID.ToString()); if (cmd.ExecuteNonQuery() == 0) return false; diff --git a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs b/OpenSim/Data/SQLiteLegacy/SQLiteFramework.cs similarity index 89% rename from OpenSim/Data/SQLiteNG/SQLiteFramework.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteFramework.cs index f0ddc597bd..606478ea7e 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteFramework.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteFramework.cs @@ -31,9 +31,9 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// A database interface class to a user profile storage system @@ -55,14 +55,11 @@ namespace OpenSim.Data.SQLiteNG { lock (connection) { -/* SqliteConnection newConnection = (SqliteConnection)((ICloneable)connection).Clone(); newConnection.Open(); cmd.Connection = newConnection; -*/ - cmd.Connection = connection; //Console.WriteLine("XXX " + cmd.CommandText); return cmd.ExecuteNonQuery(); @@ -73,12 +70,11 @@ namespace OpenSim.Data.SQLiteNG { lock (connection) { - //SqliteConnection newConnection = - // (SqliteConnection)((ICloneable)connection).Clone(); - //newConnection.Open(); + SqliteConnection newConnection = + (SqliteConnection)((ICloneable)connection).Clone(); + newConnection.Open(); - //cmd.Connection = newConnection; - cmd.Connection = connection; + cmd.Connection = newConnection; //Console.WriteLine("XXX " + cmd.CommandText); return cmd.ExecuteReader(); diff --git a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteFriendsData.cs similarity index 91% rename from OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteFriendsData.cs index 702a1d8643..d529d4d8ab 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteFriendsData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteFriendsData.cs @@ -31,9 +31,9 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { public class SQLiteFriendsData : SQLiteGenericTableHandler, IFriendsData { @@ -47,7 +47,7 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = :PrincipalID", m_Realm); - cmd.Parameters.AddWithValue(":PrincipalID", userID.ToString()); + cmd.Parameters.Add(":PrincipalID", userID.ToString()); return DoQuery(cmd); @@ -58,8 +58,8 @@ namespace OpenSim.Data.SQLiteNG SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm); - cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); - cmd.Parameters.AddWithValue(":Friend", friend); + cmd.Parameters.Add(":PrincipalID", principalID.ToString()); + cmd.Parameters.Add(":Friend", friend); ExecuteNonQuery(cmd, cmd.Connection); diff --git a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLiteLegacy/SQLiteGenericTableHandler.cs similarity index 93% rename from OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteGenericTableHandler.cs index 632c5bf8c9..1c1fe8cc0f 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteGenericTableHandler.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteGenericTableHandler.cs @@ -30,12 +30,12 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { public class SQLiteGenericTableHandler : SQLiteFramework where T: class, new() { @@ -59,21 +59,19 @@ namespace OpenSim.Data.SQLiteNG if (!m_initialized) { m_Connection = new SqliteConnection(connectionString); - Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString)); m_Connection.Open(); if (storeName != String.Empty) { Assembly assem = GetType().Assembly; - //SqliteConnection newConnection = - // (SqliteConnection)((ICloneable)m_Connection).Clone(); - //newConnection.Open(); + SqliteConnection newConnection = + (SqliteConnection)((ICloneable)m_Connection).Clone(); + newConnection.Open(); - //Migration m = new Migration(newConnection, assem, storeName); - Migration m = new Migration(m_Connection, assem, storeName); + Migration m = new Migration(newConnection, assem, storeName); m.Update(); - //newConnection.Close(); - //newConnection.Dispose(); + newConnection.Close(); + newConnection.Dispose(); } m_initialized = true; @@ -199,7 +197,7 @@ namespace OpenSim.Data.SQLiteNG result.Add(row); } - //CloseCommand(cmd); + CloseCommand(cmd); return result.ToArray(); } diff --git a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs b/OpenSim/Data/SQLiteLegacy/SQLiteInventoryStore.cs similarity index 98% rename from OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteInventoryStore.cs index 9207ca3cc4..726703b79d 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteInventoryStore.cs @@ -30,11 +30,11 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// An Inventory Interface to the SQLite database @@ -98,13 +98,11 @@ namespace OpenSim.Data.SQLiteNG ds.Tables.Add(createInventoryFoldersTable()); invFoldersDa.Fill(ds.Tables["inventoryfolders"]); setupFoldersCommands(invFoldersDa, conn); - CreateDataSetMapping(invFoldersDa, "inventoryfolders"); m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); ds.Tables.Add(createInventoryItemsTable()); invItemsDa.Fill(ds.Tables["inventoryitems"]); setupItemsCommands(invItemsDa, conn); - CreateDataSetMapping(invItemsDa, "inventoryitems"); m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); ds.AcceptChanges(); @@ -730,15 +728,6 @@ namespace OpenSim.Data.SQLiteNG * **********************************************************************/ - protected void CreateDataSetMapping(IDataAdapter da, string tableName) - { - ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); - foreach (DataColumn col in ds.Tables[tableName].Columns) - { - dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); - } - } - /// /// Create the "inventoryitems" table /// diff --git a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteRegionData.cs similarity index 91% rename from OpenSim/Data/SQLiteNG/SQLiteRegionData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteRegionData.cs index 289d626543..eb78037bb1 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteRegionData.cs @@ -32,13 +32,13 @@ using System.Drawing; using System.IO; using System.Reflection; using log4net; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// A RegionData Interface to the SQLite database @@ -87,142 +87,119 @@ namespace OpenSim.Data.SQLiteNG /// the connection string public void Initialise(string connectionString) { - try + m_connectionString = connectionString; + + ds = new DataSet(); + + m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); + m_conn = new SqliteConnection(m_connectionString); + m_conn.Open(); + + + + SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); + primDa = new SqliteDataAdapter(primSelectCmd); + // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); + + SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); + shapeDa = new SqliteDataAdapter(shapeSelectCmd); + // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); + + SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); + itemsDa = new SqliteDataAdapter(itemsSelectCmd); + + SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); + terrainDa = new SqliteDataAdapter(terrainSelectCmd); + + SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); + landDa = new SqliteDataAdapter(landSelectCmd); + + SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); + landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); + + SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); + regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); + // This actually does the roll forward assembly stuff + Assembly assem = GetType().Assembly; + Migration m = new Migration(m_conn, assem, "RegionStore"); + m.Update(); + + lock (ds) { - m_connectionString = connectionString; + ds.Tables.Add(createPrimTable()); + setupPrimCommands(primDa, m_conn); + primDa.Fill(ds.Tables["prims"]); - ds = new DataSet("Region"); + ds.Tables.Add(createShapeTable()); + setupShapeCommands(shapeDa, m_conn); - m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); - m_conn = new SqliteConnection(m_connectionString); - m_conn.Open(); + ds.Tables.Add(createItemsTable()); + setupItemsCommands(itemsDa, m_conn); + itemsDa.Fill(ds.Tables["primitems"]); - SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); - primDa = new SqliteDataAdapter(primSelectCmd); + ds.Tables.Add(createTerrainTable()); + setupTerrainCommands(terrainDa, m_conn); - SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); - shapeDa = new SqliteDataAdapter(shapeSelectCmd); - // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); + ds.Tables.Add(createLandTable()); + setupLandCommands(landDa, m_conn); - SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); - itemsDa = new SqliteDataAdapter(itemsSelectCmd); + ds.Tables.Add(createLandAccessListTable()); + setupLandAccessCommands(landAccessListDa, m_conn); - SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); - terrainDa = new SqliteDataAdapter(terrainSelectCmd); + ds.Tables.Add(createRegionSettingsTable()); + + setupRegionSettingsCommands(regionSettingsDa, m_conn); - SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); - landDa = new SqliteDataAdapter(landSelectCmd); - - SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); - landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); - - SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); - regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); - // This actually does the roll forward assembly stuff - Assembly assem = GetType().Assembly; - Migration m = new Migration(m_conn, assem, "RegionStore"); - m.Update(); - - lock (ds) + // WORKAROUND: This is a work around for sqlite on + // windows, which gets really unhappy with blob columns + // that have no sample data in them. At some point we + // need to actually find a proper way to handle this. + try { - ds.Tables.Add(createPrimTable()); - setupPrimCommands(primDa, m_conn); - - ds.Tables.Add(createShapeTable()); - setupShapeCommands(shapeDa, m_conn); - - ds.Tables.Add(createItemsTable()); - setupItemsCommands(itemsDa, m_conn); - - ds.Tables.Add(createTerrainTable()); - setupTerrainCommands(terrainDa, m_conn); - - ds.Tables.Add(createLandTable()); - setupLandCommands(landDa, m_conn); - - ds.Tables.Add(createLandAccessListTable()); - setupLandAccessCommands(landAccessListDa, m_conn); - - ds.Tables.Add(createRegionSettingsTable()); - setupRegionSettingsCommands(regionSettingsDa, m_conn); - - // WORKAROUND: This is a work around for sqlite on - // windows, which gets really unhappy with blob columns - // that have no sample data in them. At some point we - // need to actually find a proper way to handle this. - try - { - primDa.Fill(ds.Tables["prims"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on prims table"); - } - - try - { - shapeDa.Fill(ds.Tables["primshapes"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on primshapes table"); - } - - try - { - terrainDa.Fill(ds.Tables["terrain"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on terrain table"); - } - - try - { - landDa.Fill(ds.Tables["land"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on land table"); - } - - try - { - landAccessListDa.Fill(ds.Tables["landaccesslist"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); - } - - try - { - regionSettingsDa.Fill(ds.Tables["regionsettings"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); - } - - // We have to create a data set mapping for every table, otherwise the IDataAdaptor.Update() will not populate rows with values! - // Not sure exactly why this is - this kind of thing was not necessary before - justincc 20100409 - // Possibly because we manually set up our own DataTables before connecting to the database - CreateDataSetMapping(primDa, "prims"); - CreateDataSetMapping(shapeDa, "primshapes"); - CreateDataSetMapping(itemsDa, "primitems"); - CreateDataSetMapping(terrainDa, "terrain"); - CreateDataSetMapping(landDa, "land"); - CreateDataSetMapping(landAccessListDa, "landaccesslist"); - CreateDataSetMapping(regionSettingsDa, "regionsettings"); + shapeDa.Fill(ds.Tables["primshapes"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on primshapes table"); } - } - catch (Exception e) - { - m_log.Error(e); - Environment.Exit(23); - } - return; + try + { + terrainDa.Fill(ds.Tables["terrain"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on terrain table"); + } + + try + { + landDa.Fill(ds.Tables["land"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on land table"); + } + + try + { + landAccessListDa.Fill(ds.Tables["landaccesslist"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); + } + + try + { + regionSettingsDa.Fill(ds.Tables["regionsettings"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); + } + return; + } } public void Dispose() @@ -626,7 +603,7 @@ namespace OpenSim.Data.SQLiteNG } } } - rev = Convert.ToInt32(row["Revision"]); + rev = (int) row["Revision"]; } else { @@ -778,7 +755,6 @@ namespace OpenSim.Data.SQLiteNG /// public void Commit() { - m_log.Debug("[SQLITE]: Starting commit"); lock (ds) { primDa.Update(ds, "prims"); @@ -793,11 +769,18 @@ namespace OpenSim.Data.SQLiteNG { regionSettingsDa.Update(ds, "regionsettings"); } - catch (SqliteException SqlEx) + catch (SqliteExecutionException SqlEx) { - throw new Exception( - "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", - SqlEx); + if (SqlEx.Message.Contains("logic error")) + { + throw new Exception( + "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", + SqlEx); + } + else + { + throw SqlEx; + } } ds.AcceptChanges(); } @@ -819,15 +802,6 @@ namespace OpenSim.Data.SQLiteNG * **********************************************************************/ - protected void CreateDataSetMapping(IDataAdapter da, string tableName) - { - ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); - foreach (DataColumn col in ds.Tables[tableName].Columns) - { - dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); - } - } - /// /// /// @@ -1990,7 +1964,6 @@ namespace OpenSim.Data.SQLiteNG sql += ") values (:"; sql += String.Join(", :", cols); sql += ")"; - m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql); SqliteCommand cmd = new SqliteCommand(sql); // this provides the binding for all our parameters, so @@ -2286,36 +2259,6 @@ namespace OpenSim.Data.SQLiteNG return DbType.String; } } - - static void PrintDataSet(DataSet ds) - { - // Print out any name and extended properties. - Console.WriteLine("DataSet is named: {0}", ds.DataSetName); - foreach (System.Collections.DictionaryEntry de in ds.ExtendedProperties) - { - Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value); - } - Console.WriteLine(); - foreach (DataTable dt in ds.Tables) - { - Console.WriteLine("=> {0} Table:", dt.TableName); - // Print out the column names. - for (int curCol = 0; curCol < dt.Columns.Count; curCol++) - { - Console.Write(dt.Columns[curCol].ColumnName + "\t"); - } - Console.WriteLine("\n----------------------------------"); - // Print the DataTable. - for (int curRow = 0; curRow < dt.Rows.Count; curRow++) - { - for (int curCol = 0; curCol < dt.Columns.Count; curCol++) - { - Console.Write(dt.Rows[curRow][curCol].ToString() + "\t"); - } - Console.WriteLine(); - } - } - } } } diff --git a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteUserAccountData.cs similarity index 94% rename from OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteUserAccountData.cs index f77159c772..27553c61eb 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteUserAccountData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteUserAccountData.cs @@ -31,9 +31,9 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { public class SQLiteUserAccountData : SQLiteGenericTableHandler, IUserAccountData { @@ -66,7 +66,7 @@ namespace OpenSim.Data.SQLiteNG if (words.Length == 1) { - cmd.CommandText = String.Format("select * from {0} where ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", + cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", m_Realm, scopeID.ToString(), words[0]); } else diff --git a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs b/OpenSim/Data/SQLiteLegacy/SQLiteUtils.cs similarity index 99% rename from OpenSim/Data/SQLiteNG/SQLiteUtils.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteUtils.cs index 82a2e37993..095a26251f 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteUtils.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteUtils.cs @@ -27,9 +27,9 @@ using System; using System.Data; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// A base class for methods needed by all SQLite database classes diff --git a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteXInventoryData.cs similarity index 98% rename from OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs rename to OpenSim/Data/SQLiteLegacy/SQLiteXInventoryData.cs index a0c17f8fd9..5422cbf6ad 100644 --- a/OpenSim/Data/SQLiteNG/SQLiteXInventoryData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteXInventoryData.cs @@ -29,12 +29,12 @@ using System; using System.Data; using System.Reflection; using System.Collections.Generic; -using Mono.Data.Sqlite; +using Mono.Data.SqliteClient; using log4net; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Data.SQLiteNG +namespace OpenSim.Data.SQLiteLegacy { /// /// A MySQL Interface for the Asset Server @@ -147,7 +147,7 @@ namespace OpenSim.Data.SQLiteNG } reader.Close(); - //CloseCommand(cmd); + CloseCommand(cmd); return perms; } diff --git a/OpenSim/Data/Tests/DataTestUtil.cs b/OpenSim/Data/Tests/DataTestUtil.cs index d211ab3b40..5393529592 100644 --- a/OpenSim/Data/Tests/DataTestUtil.cs +++ b/OpenSim/Data/Tests/DataTestUtil.cs @@ -39,7 +39,8 @@ namespace OpenSim.Data.Tests public class DataTestUtil { public const uint UNSIGNED_INTEGER_MIN = uint.MinValue; - public const uint UNSIGNED_INTEGER_MAX = uint.MaxValue; + //public const uint UNSIGNED_INTEGER_MAX = uint.MaxValue; + public const uint UNSIGNED_INTEGER_MAX = INTEGER_MAX; public const int INTEGER_MIN = int.MinValue + 1; // Postgresql requires +1 to .NET int.MinValue public const int INTEGER_MAX = int.MaxValue; diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index bbc6f78721..f49cd975f1 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -113,14 +113,14 @@ ; --- To use sqlite as region storage: ; - ; PLEASE NOTE: Unfortunately, the SQLiteNG database plugin, while necessary to use sqlite with Mono on Linux, is + ; PLEASE NOTE: Unfortunately, the current SQLite database plugin (necessary to use SQLite with Mono on Linux) is ; not compatible with the sqlite3 library installed on Mac OSX. If you're using Mono 2.4 you can still use the old sqlite - ; library by uncommenting the SQLite.dll storage plugin (and commenting out SQLiteNG). Unfortunately, the older library - ; will not work with Mono 2.6 on Mac OSX so you will either need to replace the sqlite3 system library or use MySQL instead + ; library by uncommenting the SQLiteLegacy.dll storage plugin (and commenting out SQLite.dll). Unfortunately, the older library + ; will not work with Mono 2.6 on Mac OSX so you will either need to replace the OSX sqlite3 system library or use MySQL instead ; ; You will also need to do the same thing in config-include/StandaloneCommon.ini if you are running in standalone mode - storage_plugin = "OpenSim.Data.SQLiteNG.dll" - ;storage_plugin = "OpenSim.Data.SQLite.dll" + storage_plugin = "OpenSim.Data.SQLite.dll" + ;storage_plugin = "OpenSim.Data.SQLiteLegacy.dll" storage_connection_string="URI=file:OpenSim.db,version=3"; ; --- To use MySQL storage, supply your own connection string (this is only an example): diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index 58860d157f..572c153bef 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -6,12 +6,12 @@ ; ; SQLite - Include-Storage = "config-include/storage/SQLiteNGStandalone.ini"; + Include-Storage = "config-include/storage/SQLiteStandalone.ini"; - ; Unfortunately SQLiteNG is not compatible with Mac OSX. You can still use the older - ; sqlite library if you are using Mono 2.4. Please see the notes in OpenSim.ini for sqlite + ; Unfortunately the current SQLite database plugin is not compatible with Mac OSX. You can still use the older + ; legacy sqlite library if you are using Mono 2.4. Please see the notes in OpenSim.ini (search for sqlite) ; for more details - ;Include-Storage = "config-include/storage/SQLiteStandalone.ini"; + ;Include-Storage = "config-include/storage/SQLiteLegacyStandalone.ini"; ; MySql ; Uncomment these lines if you want to use mysql storage diff --git a/bin/config-include/storage/SQLiteNGStandalone.ini b/bin/config-include/storage/SQLiteLegacyStandalone.ini similarity index 88% rename from bin/config-include/storage/SQLiteNGStandalone.ini rename to bin/config-include/storage/SQLiteLegacyStandalone.ini index ba00acaf6c..1d4dd29d60 100644 --- a/bin/config-include/storage/SQLiteNGStandalone.ini +++ b/bin/config-include/storage/SQLiteLegacyStandalone.ini @@ -1,7 +1,7 @@ ; These are the initialization settings for running OpenSim Standalone with an SQLite database [DatabaseService] - StorageProvider = "OpenSim.Data.SQLiteNG.dll" + StorageProvider = "OpenSim.Data.SQLiteLegacy.dll" [AvatarService] ConnectionString = "URI=file:avatars.db,version=3" diff --git a/prebuild.xml b/prebuild.xml index 602750ac84..9802349ae5 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2178,7 +2178,7 @@ - + ../../../bin/ @@ -2215,7 +2215,7 @@ - + ../../../bin/ From d9213297992e531ee4f83d65f8955eef3f07861d Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 30 Apr 2010 18:18:21 +0100 Subject: [PATCH 045/260] take out some debug logging in the sqlite db adaptor --- OpenSim/Data/SQLite/SQLiteRegionData.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/SQLite/SQLiteRegionData.cs b/OpenSim/Data/SQLite/SQLiteRegionData.cs index 0217748b93..997664a45c 100644 --- a/OpenSim/Data/SQLite/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLite/SQLiteRegionData.cs @@ -778,7 +778,7 @@ namespace OpenSim.Data.SQLite /// public void Commit() { - m_log.Debug("[SQLITE]: Starting commit"); + //m_log.Debug("[SQLITE]: Starting commit"); lock (ds) { primDa.Update(ds, "prims"); @@ -1923,7 +1923,7 @@ namespace OpenSim.Data.SQLite /// public void StorePrimInventory(UUID primID, ICollection items) { - m_log.InfoFormat("[REGION DB]: Entered StorePrimInventory with prim ID {0}", primID); + //m_log.InfoFormat("[REGION DB]: Entered StorePrimInventory with prim ID {0}", primID); DataTable dbItems = ds.Tables["primitems"]; @@ -1990,7 +1990,7 @@ namespace OpenSim.Data.SQLite sql += ") values (:"; sql += String.Join(", :", cols); sql += ")"; - m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql); + //m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql); SqliteCommand cmd = new SqliteCommand(sql); // this provides the binding for all our parameters, so From d1fcd2217327579d21d4c5a33341d4e51c91a089 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 30 Apr 2010 19:28:44 +0100 Subject: [PATCH 046/260] Fix a null ref from trying to access a dictionary that was never initialized. --- .../ServiceConnectorsOut/Inventory/InventoryCache.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs index 9c6e1cdd3c..c97ab9e8da 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs @@ -52,7 +52,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory protected Dictionary> m_InventoryCache; // A cache of userIDs --> ServiceURLs, for HGBroker only - protected Dictionary m_InventoryURLs; + protected Dictionary m_InventoryURLs = + new Dictionary(); public virtual void Init(IConfigSource source, BaseInventoryConnector connector) { From 5fda81e6bbb80cbe904e69638f5f405aca78f111 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 30 Apr 2010 11:39:02 -0700 Subject: [PATCH 047/260] * XInventory fairly tested, including for HG. Almost ready to switch. * Removed a few buglets and added better exception handling. --- .../Avatar/Friends/FriendsModule.cs | 7 +- .../Resources/CoreModulePlugin.addin.xml | 1 + .../Inventory/HGInventoryBroker2.cs | 220 +++++++----------- .../RemoteXInventoryServiceConnector.cs | 8 +- .../RemotePresenceServiceConnector.cs | 2 +- .../Hypergrid/HGInventoryServerInConnector.cs | 104 --------- .../Inventory/XInventoryInConnector.cs | 50 ---- .../Inventory/XInventoryConnector.cs | 119 +++++----- .../Presence/PresenceServiceConnector.cs | 2 +- 9 files changed, 144 insertions(+), 369 deletions(-) delete mode 100644 OpenSim/Server/Handlers/Hypergrid/HGInventoryServerInConnector.cs diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 23d5b3c2ec..febd4ca993 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -365,6 +365,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends List GetOnlineFriends(UUID userID) { List friendList = new List(); + List online = new List(); foreach (FriendInfo fi in m_Friends[userID].Friends) { @@ -372,9 +373,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends friendList.Add(fi.Friend); } - PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray()); + if (friendList.Count == 0) + // no friends whatsoever + return online; - List online = new List(); + PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray()); foreach (PresenceInfo pi in presence) { diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml index 0a5ff3f982..c7382965d6 100644 --- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml +++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml @@ -47,6 +47,7 @@ + diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs index 3509161daa..fc3393f53d 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs @@ -41,27 +41,22 @@ using OpenMetaverse; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { - public class HGInventoryBroker2 : INonSharedRegionModule, IInventoryService + public class HGInventoryBroker2 : ISharedRegionModule, IInventoryService { private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); - private static bool m_Initialized = false; private static bool m_Enabled = false; private static IInventoryService m_LocalGridInventoryService; - private static ISessionAuthInventoryService m_HGService; // obsolete private Dictionary m_connectors = new Dictionary(); // A cache of userIDs --> ServiceURLs, for HGBroker only - protected Dictionary m_InventoryURLs; + protected Dictionary m_InventoryURLs = new Dictionary(); - private Scene m_Scene; private List m_Scenes = new List(); - private IUserAccountService m_UserAccountService; - public Type ReplaceableInterface { get { return null; } @@ -74,65 +69,45 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public void Initialise(IConfigSource source) { - if (!m_Initialized) + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) { - IConfig moduleConfig = source.Configs["Modules"]; - if (moduleConfig != null) + string name = moduleConfig.GetString("InventoryServices", ""); + if (name == Name) { - string name = moduleConfig.GetString("InventoryServices", ""); - if (name == Name) + IConfig inventoryConfig = source.Configs["InventoryService"]; + if (inventoryConfig == null) { - IConfig inventoryConfig = source.Configs["InventoryService"]; - if (inventoryConfig == null) - { - m_log.Error("[HG INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); - return; - } - - string localDll = inventoryConfig.GetString("LocalGridInventoryService", - String.Empty); - string HGDll = inventoryConfig.GetString("HypergridInventoryService", - String.Empty); - - if (localDll == String.Empty) - { - m_log.Error("[HG INVENTORY CONNECTOR]: No LocalGridInventoryService named in section InventoryService"); - //return; - throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); - } - - if (HGDll == String.Empty) - { - m_log.Error("[HG INVENTORY CONNECTOR]: No HypergridInventoryService named in section InventoryService"); - //return; - throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); - } - - Object[] args = new Object[] { source }; - m_LocalGridInventoryService = - ServerUtils.LoadPlugin(localDll, - args); - - m_HGService = - ServerUtils.LoadPlugin(HGDll, - args); - - if (m_LocalGridInventoryService == null) - { - m_log.Error("[HG INVENTORY CONNECTOR]: Can't load local inventory service"); - return; - } - if (m_HGService == null) - { - m_log.Error("[HG INVENTORY CONNECTOR]: Can't load hypergrid inventory service"); - return; - } - - m_Enabled = true; - m_log.Info("[HG INVENTORY CONNECTOR]: HG inventory broker enabled"); + m_log.Error("[HG INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); + return; } + + string localDll = inventoryConfig.GetString("LocalGridInventoryService", + String.Empty); + //string HGDll = inventoryConfig.GetString("HypergridInventoryService", + // String.Empty); + + if (localDll == String.Empty) + { + m_log.Error("[HG INVENTORY CONNECTOR]: No LocalGridInventoryService named in section InventoryService"); + //return; + throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); + } + + Object[] args = new Object[] { source }; + m_LocalGridInventoryService = + ServerUtils.LoadPlugin(localDll, + args); + + if (m_LocalGridInventoryService == null) + { + m_log.Error("[HG INVENTORY CONNECTOR]: Can't load local inventory service"); + return; + } + + m_Enabled = true; + m_log.InfoFormat("[HG INVENTORY CONNECTOR]: HG inventory broker enabled with inner connector of type {0}", m_LocalGridInventoryService.GetType()); } - m_Initialized = true; } } @@ -149,13 +124,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (!m_Enabled) return; - m_Scene = scene; m_Scenes.Add(scene); - m_UserAccountService = m_Scene.UserAccountService; scene.RegisterModuleInterface(this); - scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; scene.EventManager.OnClientClosed += OnClientClosed; } @@ -177,13 +149,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory } - #region Cache - - void OnMakeRootAgent(ScenePresence presence) - { - if (!m_InventoryURLs.ContainsKey(presence.UUID)) - CacheInventoryServiceURL(presence.Scene, presence.UUID); - } + #region URL Cache void OnClientClosed(UUID clientID, Scene scene) { @@ -200,10 +166,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return; } } - - m_log.DebugFormat( - "[INVENTORY CACHE]: OnClientClosed in {0}, user {1} out of sim. Dropping inventory URL", - scene.RegionInfo.RegionName, clientID); DropInventoryServiceURL(clientID); } } @@ -213,35 +175,47 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory /// and sticks it in the cache /// /// - private void CacheInventoryServiceURL(Scene scene, UUID userID) + private void CacheInventoryServiceURL(UUID userID) { - if (scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, userID) == null) + if (m_Scenes[0].UserAccountService.GetUserAccount(m_Scenes[0].RegionInfo.ScopeID, userID) == null) { // The user does not have a local account; let's cache its service URL string inventoryURL = string.Empty; ScenePresence sp = null; - scene.TryGetScenePresence(userID, out sp); - if (sp != null) + foreach (Scene scene in m_Scenes) { - AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); - if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) + scene.TryGetScenePresence(userID, out sp); + if (sp != null) { - inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); - if (inventoryURL != null && inventoryURL != string.Empty) + AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); + if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) { - inventoryURL = inventoryURL.Trim(new char[] { '/' }); - m_InventoryURLs.Add(userID, inventoryURL); + inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); + if (inventoryURL != null && inventoryURL != string.Empty) + { + inventoryURL = inventoryURL.Trim(new char[] { '/' }); + m_InventoryURLs.Add(userID, inventoryURL); + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Added {0} to the cache of inventory URLs", inventoryURL); + return; + } } } } } + + // else put a null; it means that the methods should forward to local grid's inventory + m_InventoryURLs.Add(userID, null); } private void DropInventoryServiceURL(UUID userID) { lock (m_InventoryURLs) if (m_InventoryURLs.ContainsKey(userID)) + { + string url = m_InventoryURLs[userID]; m_InventoryURLs.Remove(userID); + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Removed {0} from the cache of inventory URLs", url); + } } public string GetInventoryServiceURL(UUID userID) @@ -249,7 +223,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (m_InventoryURLs.ContainsKey(userID)) return m_InventoryURLs[userID]; - return null; + else + CacheInventoryServiceURL(userID); + + return m_InventoryURLs[userID]; } #endregion @@ -276,7 +253,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public InventoryFolderBase GetRootFolder(UUID userID) { - m_log.DebugFormat("[HGInventory]: GetRootFolder for {0}", userID); + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetRootFolder for {0}", userID); string invURL = GetInventoryServiceURL(userID); @@ -290,7 +267,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) { - m_log.DebugFormat("[HGInventory]: GetFolderForType {0} type {1}", userID, type); + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetFolderForType {0} type {1}", userID, type); string invURL = GetInventoryServiceURL(userID); @@ -304,7 +281,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public InventoryCollection GetFolderContent(UUID userID, UUID folderID) { - m_log.Debug("[HGInventory]: GetFolderContent " + folderID); + m_log.Debug("[HG INVENTORY CONNECTOR]: GetFolderContent " + folderID); string invURL = GetInventoryServiceURL(userID); @@ -319,7 +296,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public List GetFolderItems(UUID userID, UUID folderID) { - m_log.Debug("[HGInventory]: GetFolderItems " + folderID); + m_log.Debug("[HG INVENTORY CONNECTOR]: GetFolderItems " + folderID); string invURL = GetInventoryServiceURL(userID); @@ -337,7 +314,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (folder == null) return false; - m_log.Debug("[HGInventory]: AddFolder " + folder.ID); + m_log.Debug("[HG INVENTORY CONNECTOR]: AddFolder " + folder.ID); string invURL = GetInventoryServiceURL(folder.Owner); @@ -354,7 +331,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (folder == null) return false; - m_log.Debug("[HGInventory]: UpdateFolder " + folder.ID); + m_log.Debug("[HG INVENTORY CONNECTOR]: UpdateFolder " + folder.ID); string invURL = GetInventoryServiceURL(folder.Owner); @@ -373,7 +350,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (folderIDs.Count == 0) return false; - m_log.Debug("[HGInventory]: DeleteFolders for " + ownerID); + m_log.Debug("[HG INVENTORY CONNECTOR]: DeleteFolders for " + ownerID); string invURL = GetInventoryServiceURL(ownerID); @@ -390,7 +367,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (folder == null) return false; - m_log.Debug("[HGInventory]: MoveFolder for " + folder.Owner); + m_log.Debug("[HG INVENTORY CONNECTOR]: MoveFolder for " + folder.Owner); string invURL = GetInventoryServiceURL(folder.Owner); @@ -407,7 +384,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (folder == null) return false; - m_log.Debug("[HGInventory]: PurgeFolder for " + folder.Owner); + m_log.Debug("[HG INVENTORY CONNECTOR]: PurgeFolder for " + folder.Owner); string invURL = GetInventoryServiceURL(folder.Owner); @@ -424,7 +401,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (item == null) return false; - m_log.Debug("[HGInventory]: AddItem " + item.ID); + m_log.Debug("[HG INVENTORY CONNECTOR]: AddItem " + item.ID); string invURL = GetInventoryServiceURL(item.Owner); @@ -441,7 +418,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (item == null) return false; - m_log.Debug("[HGInventory]: UpdateItem " + item.ID); + m_log.Debug("[HG INVENTORY CONNECTOR]: UpdateItem " + item.ID); string invURL = GetInventoryServiceURL(item.Owner); @@ -460,7 +437,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (items.Count == 0) return true; - m_log.Debug("[HGInventory]: MoveItems for " + ownerID); + m_log.Debug("[HG INVENTORY CONNECTOR]: MoveItems for " + ownerID); string invURL = GetInventoryServiceURL(ownerID); @@ -481,7 +458,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (itemIDs.Count == 0) return true; - m_log.Debug("[HGInventory]: DeleteItems for " + ownerID); + m_log.Debug("[HG INVENTORY CONNECTOR]: DeleteItems for " + ownerID); string invURL = GetInventoryServiceURL(ownerID); @@ -497,7 +474,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { if (item == null) return null; - m_log.Debug("[HGInventory]: GetItem " + item.ID); + m_log.Debug("[HG INVENTORY CONNECTOR]: GetItem " + item.ID); string invURL = GetInventoryServiceURL(item.Owner); @@ -514,7 +491,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (folder == null) return null; - m_log.Debug("[HGInventory]: GetFolder " + folder.ID); + m_log.Debug("[HG INVENTORY CONNECTOR]: GetFolder " + folder.ID); string invURL = GetInventoryServiceURL(folder.Owner); @@ -538,7 +515,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public int GetAssetPermissions(UUID userID, UUID assetID) { - m_log.Debug("[HGInventory]: GetAssetPermissions " + assetID); + m_log.Debug("[HG INVENTORY CONNECTOR]: GetAssetPermissions " + assetID); string invURL = GetInventoryServiceURL(userID); @@ -575,44 +552,5 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return connector; } - - private UUID GetSessionID(UUID userID) - { - ScenePresence sp = null; - if (m_Scene.TryGetScenePresence(userID, out sp)) - { - return sp.ControllingClient.SessionId; - } - - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: scene presence for {0} not found", userID); - return UUID.Zero; - } - - private bool IsForeignUser(UUID userID, out string inventoryURL) - { - inventoryURL = string.Empty; - UserAccount account = null; - if (m_Scene.UserAccountService != null) - account = m_Scene.UserAccountService.GetUserAccount(m_Scene.RegionInfo.ScopeID, userID); - - if (account == null) // foreign user - { - ScenePresence sp = null; - m_Scene.TryGetScenePresence(userID, out sp); - if (sp != null) - { - AgentCircuitData aCircuit = m_Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); - if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) - { - inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); - inventoryURL = inventoryURL.Trim(new char[] { '/' }); - return true; - } - } - } - return false; - } - - } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs index ac9e792317..277060d2aa 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs @@ -172,11 +172,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.GetFolderForType(userID, type); } - public Dictionary GetSystemFolders(UUID userID) - { - return m_RemoteConnector.GetSystemFolders(userID); - } - public InventoryCollection GetFolderContent(UUID userID, UUID folderID) { return m_RemoteConnector.GetFolderContent(userID, folderID); @@ -267,9 +262,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public InventoryItemBase GetItem(InventoryItemBase item) { + m_log.DebugFormat("[XINVENTORY CONNECTOR]: GetItem {0}", item.ID); if (item == null) return null; + if (m_RemoteConnector == null) + m_log.DebugFormat("[XINVENTORY CONNECTOR]: connector stub is null!!!"); return m_RemoteConnector.GetItem(item); } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs index 865f99e10a..5f3666e323 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs @@ -76,7 +76,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence m_PresenceDetector = new PresenceDetector(this); - m_log.Info("[INVENTORY CONNECTOR]: Remote presence enabled"); + m_log.Info("[REMOTE PRESENCE CONNECTOR]: Remote presence enabled"); } } diff --git a/OpenSim/Server/Handlers/Hypergrid/HGInventoryServerInConnector.cs b/OpenSim/Server/Handlers/Hypergrid/HGInventoryServerInConnector.cs deleted file mode 100644 index 41897eb5a2..0000000000 --- a/OpenSim/Server/Handlers/Hypergrid/HGInventoryServerInConnector.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 OpenSimulator 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.Net; -using System.Reflection; -using log4net; -using Nini.Config; -using Nwc.XmlRpc; -using OpenSim.Server.Base; -using OpenSim.Server.Handlers.Inventory; -using OpenSim.Services.Interfaces; -using OpenSim.Framework; -using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Server.Handlers.Base; -using OpenMetaverse; - -namespace OpenSim.Server.Handlers.Hypergrid -{ - public class HGInventoryServiceInConnector : InventoryServiceInConnector - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - //private static readonly int INVENTORY_DEFAULT_SESSION_TIME = 30; // secs - //private AuthedSessionCache m_session_cache = new AuthedSessionCache(INVENTORY_DEFAULT_SESSION_TIME); - - private IUserAgentService m_UserAgentService; - - public HGInventoryServiceInConnector(IConfigSource config, IHttpServer server, string configName) : - base(config, server, configName) - { - IConfig serverConfig = config.Configs[m_ConfigName]; - if (serverConfig == null) - throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); - - string userAgentService = serverConfig.GetString("UserAgentService", string.Empty); - string m_userserver_url = serverConfig.GetString("UserAgentURI", String.Empty); - if (m_userserver_url != string.Empty) - { - Object[] args = new Object[] { m_userserver_url }; - m_UserAgentService = ServerUtils.LoadPlugin(userAgentService, args); - } - - AddHttpHandlers(server); - m_log.Debug("[HG INVENTORY HANDLER]: handlers initialized"); - } - - /// - /// Check that the source of an inventory request for a particular agent is a current session belonging to - /// that agent. - /// - /// - /// - /// - public override bool CheckAuthSession(string session_id, string avatar_id) - { - //m_log.InfoFormat("[HG INVENTORY IN CONNECTOR]: checking authed session {0} {1}", session_id, avatar_id); - // This doesn't work - - // if (m_session_cache.getCachedSession(session_id, avatar_id) == null) - // { - // //cache miss, ask userserver - // m_UserAgentService.VerifyAgent(session_id, ???); - // } - // else - // { - // // cache hits - // m_log.Info("[HG INVENTORY IN CONNECTOR]: got authed session from cache"); - // return true; - // } - - // m_log.Warn("[HG INVENTORY IN CONNECTOR]: unknown session_id, request rejected"); - // return false; - - return true; - } - } -} diff --git a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs index 16b05df9e2..b0fee6d4cb 100644 --- a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs +++ b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs @@ -144,8 +144,6 @@ namespace OpenSim.Server.Handlers.Asset return HandleGetActiveGestures(request); case "GETASSETPERMISSIONS": return HandleGetAssetPermissions(request); - case "GETSYSTEMFOLDERS": - return HandleGetSystemFolders(request); } m_log.DebugFormat("[XINVENTORY HANDLER]: unknown method request: {0}", method); } @@ -575,29 +573,6 @@ namespace OpenSim.Server.Handlers.Asset return encoding.GetBytes(xmlString); } - byte[] HandleGetSystemFolders(Dictionary request) - { - Dictionary result = new Dictionary(); - UUID principal = UUID.Zero; - UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); - - Dictionary sfolders = GetSystemFolders(principal); - //m_log.DebugFormat("[XXX]: SystemFolders got {0} folders", sfolders.Count); - - Dictionary folders = new Dictionary(); - int i = 0; - foreach (KeyValuePair kvp in sfolders) - { - folders["folder_" + i.ToString()] = EncodeFolder(kvp.Value); - i++; - } - result["FOLDERS"] = folders; - - string xmlString = ServerUtils.BuildXmlResponse(result); - //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); - UTF8Encoding encoding = new UTF8Encoding(); - return encoding.GetBytes(xmlString); - } private Dictionary EncodeFolder(InventoryFolderBase f) { @@ -683,30 +658,5 @@ namespace OpenSim.Server.Handlers.Asset return item; } - #region Extra - private Dictionary GetSystemFolders(UUID userID) - { - InventoryFolderBase root = m_InventoryService.GetRootFolder(userID); - if (root != null) - { - InventoryCollection content = m_InventoryService.GetFolderContent(userID, root.ID); - if (content != null) - { - Dictionary folders = new Dictionary(); - foreach (InventoryFolderBase folder in content.Folders) - { - if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) - folders[(AssetType)folder.Type] = folder; - } - // Put the root folder there, as type Folder - folders[AssetType.Folder] = root; - return folders; - } - } - m_log.WarnFormat("[XINVENTORY SERVICE]: System folders for {0} not found", userID); - return new Dictionary(); - } - #endregion - } } diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs index 52294dab71..e25e7ebd3c 100644 --- a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs @@ -158,30 +158,31 @@ namespace OpenSim.Services.Connectors public InventoryCollection GetFolderContent(UUID principalID, UUID folderID) { - Dictionary ret = MakeRequest("GETFOLDERCONTENT", - new Dictionary { - { "PRINCIPAL", principalID.ToString() }, - { "FOLDER", folderID.ToString() } - }); - - if (ret == null) - return null; - if (ret.Count == 0) - return null; - - InventoryCollection inventory = new InventoryCollection(); - inventory.Folders = new List(); - inventory.Items = new List(); - inventory.UserID = principalID; - Dictionary folders = - (Dictionary)ret["FOLDERS"]; - Dictionary items = - (Dictionary)ret["ITEMS"]; - try { + Dictionary ret = MakeRequest("GETFOLDERCONTENT", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "FOLDER", folderID.ToString() } + }); + + if (ret == null) + return null; + if (ret.Count == 0) + return null; + + + inventory.Folders = new List(); + inventory.Items = new List(); + inventory.UserID = principalID; + + Dictionary folders = + (Dictionary)ret["FOLDERS"]; + Dictionary items = + (Dictionary)ret["ITEMS"]; + foreach (Object o in folders.Values) // getting the values directly, we don't care about the keys folder_i inventory.Folders.Add(BuildFolder((Dictionary)o)); foreach (Object o in items.Values) // getting the values directly, we don't care about the keys item_i @@ -189,7 +190,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception unwrapping content list: {0}", e.Message); + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetFolderContent: {0}", e.Message); } return inventory; @@ -408,32 +409,50 @@ namespace OpenSim.Services.Connectors public InventoryItemBase GetItem(InventoryItemBase item) { - Dictionary ret = MakeRequest("GETITEM", - new Dictionary { + try + { + Dictionary ret = MakeRequest("GETITEM", + new Dictionary { { "ID", item.ID.ToString() } }); - if (ret == null) - return null; - if (ret.Count == 0) - return null; + if (ret == null) + return null; + if (ret.Count == 0) + return null; - return BuildItem((Dictionary)ret["item"]); + return BuildItem((Dictionary)ret["item"]); + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetItem: {0}", e.Message); + } + + return null; } public InventoryFolderBase GetFolder(InventoryFolderBase folder) { - Dictionary ret = MakeRequest("GETFOLDER", - new Dictionary { + try + { + Dictionary ret = MakeRequest("GETFOLDER", + new Dictionary { { "ID", folder.ID.ToString() } }); - if (ret == null) - return null; - if (ret.Count == 0) - return null; + if (ret == null) + return null; + if (ret.Count == 0) + return null; - return BuildFolder((Dictionary)ret["folder"]); + return BuildFolder((Dictionary)ret["folder"]); + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetFolder: {0}", e.Message); + } + + return null; } public List GetActiveGestures(UUID principalID) @@ -468,36 +487,6 @@ namespace OpenSim.Services.Connectors return int.Parse(ret["RESULT"].ToString()); } - public Dictionary GetSystemFolders(UUID userID) - { - Dictionary ret = MakeRequest("GETSYSTEMFOLDERS", - new Dictionary { - { "PRINCIPAL", userID.ToString() }, - }); - - if (ret == null) - return new Dictionary(); - - Dictionary sfolders = new Dictionary(); - - try - { - Dictionary folders = (Dictionary)ret["FOLDERS"]; - - foreach (Object o in folders.Values) // getting the values directly, we don't care about the keys folder_i - { - InventoryFolderBase folder = BuildFolder((Dictionary)o); - sfolders.Add((AssetType)folder.Type, folder); - } - - } - catch (Exception e) - { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: exception {0}", e.Message); - } - - return sfolders; - } // These are either obsolete or unused // diff --git a/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs b/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs index 4dadd9ebb7..23621b7387 100644 --- a/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs @@ -329,7 +329,7 @@ namespace OpenSim.Services.Connectors reqString); if (reply == null || (reply != null && reply == string.Empty)) { - m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgent received null or empty reply"); + m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgents received null or empty reply"); return null; } } From 177048a65120c5b15e731802921a31b71a078e6f Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 30 Apr 2010 22:35:07 +0200 Subject: [PATCH 048/260] Fix linking issue introduced in my earlier commit --- .../Framework/Scenes/Scene.Inventory.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 6ebfd31896..911722444c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1967,8 +1967,17 @@ namespace OpenSim.Region.Framework.Scenes List children = new List(); SceneObjectPart root = GetSceneObjectPart(parentPrimId); - if (Permissions.CanLinkObject(client.AgentId, root.ParentGroup.RootPart.UUID)) + if (root == null) + { + m_log.DebugFormat("[LINK]: Can't find linkset root prim {0{", parentPrimId); return; + } + + if (!Permissions.CanLinkObject(client.AgentId, root.ParentGroup.RootPart.UUID)) + { + m_log.DebugFormat("[LINK]: Refusing link. No permissions on root prim"); + return; + } foreach (uint localID in childPrimIds) { @@ -1987,7 +1996,16 @@ namespace OpenSim.Region.Framework.Scenes // Must be all one owner // if (owners.Count > 1) + { + m_log.DebugFormat("[LINK]: Refusing link. Too many owners"); return; + } + + if (children.Count == 0) + { + m_log.DebugFormat("[LINK]: Refusing link. No permissions to link any of the children"); + return; + } m_sceneGraph.LinkObjects(root, children); } From d0accc073272af838bc4ba44a71d41b19aa05906 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 30 Apr 2010 23:32:58 +0200 Subject: [PATCH 049/260] Allow retrieval if admin users in scope mode --- .../UserAccountService/UserAccountService.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/OpenSim/Services/UserAccountService/UserAccountService.cs b/OpenSim/Services/UserAccountService/UserAccountService.cs index 7b38aa69f9..35e2826a71 100644 --- a/OpenSim/Services/UserAccountService/UserAccountService.cs +++ b/OpenSim/Services/UserAccountService/UserAccountService.cs @@ -104,6 +104,12 @@ namespace OpenSim.Services.UserAccountService d = m_Database.Get( new string[] { "ScopeID", "FirstName", "LastName" }, new string[] { scopeID.ToString(), firstName, lastName }); + if (d.Length < 1) + { + d = m_Database.Get( + new string[] { "ScopeID", "FirstName", "LastName" }, + new string[] { UUID.Zero.ToString(), firstName, lastName }); + } } else { @@ -172,6 +178,12 @@ namespace OpenSim.Services.UserAccountService d = m_Database.Get( new string[] { "ScopeID", "Email" }, new string[] { scopeID.ToString(), email }); + if (d.Length < 1) + { + d = m_Database.Get( + new string[] { "ScopeID", "Email" }, + new string[] { UUID.Zero.ToString(), email }); + } } else { @@ -195,6 +207,12 @@ namespace OpenSim.Services.UserAccountService d = m_Database.Get( new string[] { "ScopeID", "PrincipalID" }, new string[] { scopeID.ToString(), principalID.ToString() }); + if (d.Length < 1) + { + d = m_Database.Get( + new string[] { "ScopeID", "PrincipalID" }, + new string[] { UUID.Zero.ToString(), principalID.ToString() }); + } } else { From f3662e3d15073aa7da22c3faf34afd6cb0e19dc9 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 30 Apr 2010 22:22:03 +0100 Subject: [PATCH 050/260] minor: eliminate more debug Console.WriteLines, convert one to logging instead --- OpenSim/Data/SQLite/SQLiteAuthenticationData.cs | 6 +++++- OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs | 2 +- OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs index 086ac0a972..a1412ff0fa 100644 --- a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs @@ -29,6 +29,8 @@ using System; using System.Collections; using System.Collections.Generic; using System.Data; +using System.Reflection; +using log4net; using OpenMetaverse; using OpenSim.Framework; using Mono.Data.Sqlite; @@ -37,6 +39,8 @@ namespace OpenSim.Data.SQLite { public class SQLiteAuthenticationData : SQLiteFramework, IAuthenticationData { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private string m_Realm; private List m_ColumnNames; private int m_LastExpire; @@ -157,7 +161,7 @@ namespace OpenSim.Data.SQLite } catch (Exception e) { - Console.WriteLine(e.ToString()); + m_log.Error("[SQLITE]: Exception storing authentication data", e); //CloseCommand(cmd); return false; } diff --git a/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs index 3c70aeffc4..9b8e2fa7f3 100644 --- a/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs +++ b/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs @@ -59,7 +59,7 @@ namespace OpenSim.Data.SQLite if (!m_initialized) { m_Connection = new SqliteConnection(connectionString); - Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString)); + //Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString)); m_Connection.Open(); if (storeName != String.Empty) diff --git a/OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs index c64830a455..760221d9fe 100644 --- a/OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteAuthenticationData.cs @@ -29,6 +29,8 @@ using System; using System.Collections; using System.Collections.Generic; using System.Data; +using System.Reflection; +using log4net; using OpenMetaverse; using OpenSim.Framework; using Mono.Data.SqliteClient; @@ -37,6 +39,8 @@ namespace OpenSim.Data.SQLiteLegacy { public class SQLiteAuthenticationData : SQLiteFramework, IAuthenticationData { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private string m_Realm; private List m_ColumnNames; private int m_LastExpire; @@ -162,7 +166,7 @@ namespace OpenSim.Data.SQLiteLegacy } catch (Exception e) { - Console.WriteLine(e.ToString()); + m_log.Error("[SQLITE]: Exception storing authentication data", e); CloseCommand(cmd); return false; } From 648999dd3bb59fa6cd784338223d2f693ea09f08 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 30 Apr 2010 22:36:26 +0100 Subject: [PATCH 051/260] add operation to "nant distbin" to copy StandaloneCommon.ini.example -> StandaloneCommon.ini --- .nant/local.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nant/local.include b/.nant/local.include index 2591aba963..b78b3efcdc 100644 --- a/.nant/local.include +++ b/.nant/local.include @@ -7,8 +7,8 @@ + - From bd49985afa0a30cf9338730807a42eff3d508bee Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 2 May 2010 10:31:35 -0700 Subject: [PATCH 052/260] Switched everything to XInventory by default. The old Inventory is still there for now, in case bugs pop up with XInventory. --- .../SQLite/Resources/001_XInventoryStore.sql | 38 ++++++ .../SQLite/Resources/002_XInventoryStore.sql | 9 ++ OpenSim/Data/SQLite/SQLiteXInventoryData.cs | 2 +- .../LocalInventoryServiceConnector.cs | 113 +++++------------- bin/Robust.HG.ini.example | 9 +- bin/Robust.ini.example | 9 +- bin/config-include/Grid.ini | 2 +- bin/config-include/GridHypergrid.ini | 3 +- bin/config-include/Standalone.ini | 6 +- bin/config-include/StandaloneHypergrid.ini | 9 +- .../storage/SQLiteStandalone.ini | 6 + 11 files changed, 100 insertions(+), 106 deletions(-) create mode 100644 OpenSim/Data/SQLite/Resources/001_XInventoryStore.sql create mode 100644 OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql diff --git a/OpenSim/Data/SQLite/Resources/001_XInventoryStore.sql b/OpenSim/Data/SQLite/Resources/001_XInventoryStore.sql new file mode 100644 index 0000000000..7e21996ed9 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/001_XInventoryStore.sql @@ -0,0 +1,38 @@ +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders( + folderName varchar(255), + type integer, + version integer, + folderID varchar(255) primary key, + agentID varchar(255) not null default '00000000-0000-0000-0000-000000000000', + parentFolderID varchar(255) not null default '00000000-0000-0000-0000-000000000000'); + +CREATE TABLE inventoryitems( + assetID varchar(255), + assetType integer, + inventoryName varchar(255), + inventoryDescription varchar(255), + inventoryNextPermissions integer, + inventoryCurrentPermissions integer, + invType integer, + creatorID varchar(255), + inventoryBasePermissions integer, + inventoryEveryOnePermissions integer, + salePrice integer default 99, + saleType integer default 0, + creationDate integer default 2000, + groupID varchar(255) default '00000000-0000-0000-0000-000000000000', + groupOwned integer default 0, + flags integer default 0, + inventoryID varchar(255) primary key, + parentFolderID varchar(255) not null default '00000000-0000-0000-0000-000000000000', + avatarID varchar(255) not null default '00000000-0000-0000-0000-000000000000', + inventoryGroupPermissions integer not null default 0); + +create index inventoryfolders_agentid on inventoryfolders(agentID); +create index inventoryfolders_parentid on inventoryfolders(parentFolderID); +create index inventoryitems_parentfolderid on inventoryitems(parentFolderID); +create index inventoryitems_avatarid on inventoryitems(avatarID); + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql b/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql new file mode 100644 index 0000000000..545d233466 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql @@ -0,0 +1,9 @@ +BEGIN TRANSACTION; + +ATTACH 'inventoryStore.db' AS old; + +INSERT INTO inventoryfolders (folderName, type, version, folderID, agentID, parentFolderID) SELECT `name` AS folderName, `type` AS type, `version` AS version, `UUID` AS folderID, `agentID` AS agentID, `parentID` AS parentFolderID from old.inventoryfolders; + +INSERT INTO inventoryitems (assetID, assetType, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryID, parentFolderID, avatarID, inventoryGroupPermissions) SELECT `assetID`, `assetType` AS assetType, `inventoryName` AS inventoryName, `inventoryDescription` AS inventoryDescription, `inventoryNextPermissions` AS inventoryNextPermissions, `inventoryCurrentPermissions` AS inventoryCurrentPermissions, `invType` AS invType, `creatorsID` AS creatorID, `inventoryBasePermissions` AS inventoryBasePermissions, `inventoryEveryOnePermissions` AS inventoryEveryOnePermissions, `salePrice` AS salePrice, `saleType` AS saleType, `creationDate` AS creationDate, `groupID` AS groupID, `groupOwned` AS groupOwned, `flags` AS flags, `UUID` AS inventoryID, `parentFolderID` AS parentFolderID, `avatarID` AS avatarID, `inventoryGroupPermissions` AS inventoryGroupPermissions FROM old.inventoryitems; + +COMMIT; diff --git a/OpenSim/Data/SQLite/SQLiteXInventoryData.cs b/OpenSim/Data/SQLite/SQLiteXInventoryData.cs index be1d0412fd..6064538990 100644 --- a/OpenSim/Data/SQLite/SQLiteXInventoryData.cs +++ b/OpenSim/Data/SQLite/SQLiteXInventoryData.cs @@ -49,7 +49,7 @@ namespace OpenSim.Data.SQLite public SQLiteXInventoryData(string conn, string realm) { m_Folders = new SQLiteGenericTableHandler( - conn, "inventoryfolders", "InventoryStore"); + conn, "inventoryfolders", "XInventoryStore"); m_Items = new SqliteItemHandler( conn, "inventoryitems", String.Empty); } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/LocalInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/LocalInventoryServiceConnector.cs index a2f26d5a2c..22bd04cd65 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/LocalInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/LocalInventoryServiceConnector.cs @@ -41,7 +41,7 @@ using OpenMetaverse; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { - public class LocalInventoryServicesConnector : BaseInventoryConnector, ISharedRegionModule, IInventoryService + public class LocalInventoryServicesConnector : ISharedRegionModule, IInventoryService { private static readonly ILog m_log = LogManager.GetLogger( @@ -50,7 +50,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory private IInventoryService m_InventoryService; private bool m_Enabled = false; - private bool m_Initialized = false; public Type ReplaceableInterface { @@ -93,23 +92,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (m_InventoryService == null) { m_log.Error("[LOCAL INVENTORY SERVICES CONNECTOR]: Can't load inventory service"); - //return; throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); } - //List plugins - // = DataPluginFactory.LoadDataPlugins( - // configSettings.StandaloneInventoryPlugin, - // configSettings.StandaloneInventorySource); - - //foreach (IInventoryDataPlugin plugin in plugins) - //{ - // // Using the OSP wrapper plugin for database plugins should be made configurable at some point - // m_InventoryService.AddPlugin(new OspInventoryWrapperPlugin(plugin, this)); - //} - - Init(source); - m_Enabled = true; m_log.Info("[LOCAL INVENTORY SERVICES CONNECTOR]: Local inventory connector enabled"); } @@ -128,99 +113,60 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { if (!m_Enabled) return; - - if (!m_Initialized) - { - m_Initialized = true; - } - -// m_log.DebugFormat( -// "[LOCAL INVENTORY SERVICES CONNECTOR]: Registering IInventoryService to scene {0}", scene.RegionInfo.RegionName); scene.RegisterModuleInterface(this); - m_cache.AddRegion(scene); } public void RemoveRegion(Scene scene) { if (!m_Enabled) return; - - m_cache.RemoveRegion(scene); } public void RegionLoaded(Scene scene) { if (!m_Enabled) return; - - m_log.InfoFormat( - "[LOCAL INVENTORY SERVICES CONNECTOR]: Enabled local inventory for region {0}", scene.RegionInfo.RegionName); } #region IInventoryService - public override bool CreateUserInventory(UUID user) + public bool CreateUserInventory(UUID user) { return m_InventoryService.CreateUserInventory(user); } - public override List GetInventorySkeleton(UUID userId) + public List GetInventorySkeleton(UUID userId) { return m_InventoryService.GetInventorySkeleton(userId); } - public override InventoryCollection GetUserInventory(UUID id) + public InventoryCollection GetUserInventory(UUID id) { return m_InventoryService.GetUserInventory(id); } - public override void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) { m_InventoryService.GetUserInventory(userID, callback); } - // Inherited. See base - //public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) - //{ - // return m_InventoryService.GetFolderForType(userID, type); - //} - - public override Dictionary GetSystemFolders(UUID userID) + public InventoryFolderBase GetRootFolder(UUID userID) { - InventoryFolderBase root = m_InventoryService.GetRootFolder(userID); - if (root != null) - { - InventoryCollection content = GetFolderContent(userID, root.ID); - if (content != null) - { - Dictionary folders = new Dictionary(); - foreach (InventoryFolderBase folder in content.Folders) - { - if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) - { - //m_log.InfoFormat("[INVENTORY CONNECTOR]: folder type {0} ", folder.Type); - folders[(AssetType)folder.Type] = folder; - } - } - // Put the root folder there, as type Folder - folders[AssetType.Folder] = root; - //m_log.InfoFormat("[INVENTORY CONNECTOR]: root folder is type {0} ", root.Type); - - return folders; - } - } - m_log.WarnFormat("[LOCAL INVENTORY SERVICES CONNECTOR]: System folders for {0} not found", userID); - return new Dictionary(); + return m_InventoryService.GetRootFolder(userID); } - public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) + public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + { + return m_InventoryService.GetFolderForType(userID, type); + } + + public InventoryCollection GetFolderContent(UUID userID, UUID folderID) { return m_InventoryService.GetFolderContent(userID, folderID); } - - public override List GetFolderItems(UUID userID, UUID folderID) + public List GetFolderItems(UUID userID, UUID folderID) { return m_InventoryService.GetFolderItems(userID, folderID); } @@ -230,7 +176,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory /// /// /// true if the folder was successfully added - public override bool AddFolder(InventoryFolderBase folder) + public bool AddFolder(InventoryFolderBase folder) { return m_InventoryService.AddFolder(folder); } @@ -240,7 +186,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory /// /// /// true if the folder was successfully updated - public override bool UpdateFolder(InventoryFolderBase folder) + public bool UpdateFolder(InventoryFolderBase folder) { return m_InventoryService.UpdateFolder(folder); } @@ -250,12 +196,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory /// /// A folder containing the details of the new location /// true if the folder was successfully moved - public override bool MoveFolder(InventoryFolderBase folder) + public bool MoveFolder(InventoryFolderBase folder) { return m_InventoryService.MoveFolder(folder); } - public override bool DeleteFolders(UUID ownerID, List folderIDs) + public bool DeleteFolders(UUID ownerID, List folderIDs) { return m_InventoryService.DeleteFolders(ownerID, folderIDs); } @@ -265,18 +211,17 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory /// /// /// true if the folder was successfully purged - public override bool PurgeFolder(InventoryFolderBase folder) + public bool PurgeFolder(InventoryFolderBase folder) { return m_InventoryService.PurgeFolder(folder); } /// - /// Add a new item to the user's inventory, plain - /// Called by base class AddItem + /// Add a new item to the user's inventory /// /// /// true if the item was successfully added - protected override bool AddItemPlain(InventoryItemBase item) + public bool AddItem(InventoryItemBase item) { return m_InventoryService.AddItem(item); } @@ -286,13 +231,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory ///
/// Remove an item from this entity's inventory diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 3b1b567557..44c49c58e4 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -609,30 +609,37 @@ namespace OpenSim.Region.Framework.Scenes /// false if the item did not exist, true if the update occurred successfully public bool UpdateInventoryItem(TaskInventoryItem item) { - lock (m_items) + return UpdateInventoryItem(item, true); + } + + public bool UpdateInventoryItem(TaskInventoryItem item, bool fireScriptEvents) + { + lock(m_items) { if (m_items.ContainsKey(item.ItemID)) { - item.ParentID = m_part.UUID; - item.ParentPartID = m_part.UUID; - item.Flags = m_items[item.ItemID].Flags; - - // If group permissions have been set on, check that the groupID is up to date in case it has - // changed since permissions were last set. - if (item.GroupPermissions != (uint)PermissionMask.None) - item.GroupID = m_part.GroupID; - - if (item.AssetID == UUID.Zero) + if (m_items.ContainsKey(item.ItemID)) { - item.AssetID = m_items[item.ItemID].AssetID; - } + item.ParentID = m_part.UUID; + item.ParentPartID = m_part.UUID; + item.Flags = m_items[item.ItemID].Flags; + // If group permissions have been set on, check that the groupID is up to date in case it has + // changed since permissions were last set. + if (item.GroupPermissions != (uint)PermissionMask.None) + item.GroupID = m_part.GroupID; + + if (item.AssetID == UUID.Zero) + { + item.AssetID = m_items[item.ItemID].AssetID; + } m_items[item.ItemID] = item; m_inventorySerial++; - m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + if (fireScriptEvents) + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); HasInventoryChanged = true; m_part.ParentGroup.HasGroupChanged = true; - + m_items.LockItemsForWrite(false); return true; } else @@ -643,9 +650,9 @@ namespace OpenSim.Region.Framework.Scenes item.ItemID, m_part.Name, m_part.UUID, m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName); } - } - return false; + return false; + } } /// From edde0be0a0902a5f9cf51df7ceb3325d50c95c6e Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 27 Apr 2010 01:11:14 +0100 Subject: [PATCH 020/260] Fix build break. --- .../Scenes/SceneObjectPartInventory.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 44c49c58e4..4da63c0187 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -633,24 +633,24 @@ namespace OpenSim.Region.Framework.Scenes { item.AssetID = m_items[item.ItemID].AssetID; } - m_items[item.ItemID] = item; - m_inventorySerial++; - if (fireScriptEvents) - m_part.TriggerScriptChangedEvent(Changed.INVENTORY); - HasInventoryChanged = true; - m_part.ParentGroup.HasGroupChanged = true; - m_items.LockItemsForWrite(false); - return true; - } - else - { - m_log.ErrorFormat( - "[PRIM INVENTORY]: " + - "Tried to retrieve item ID {0} from prim {1}, {2} at {3} in {4} but the item does not exist in this inventory", - item.ItemID, m_part.Name, m_part.UUID, - m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName); - } + m_items[item.ItemID] = item; + m_inventorySerial++; + if (fireScriptEvents) + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + HasInventoryChanged = true; + m_part.ParentGroup.HasGroupChanged = true; + return true; + } + else + { + m_log.ErrorFormat( + "[PRIM INVENTORY]: " + + "Tried to retrieve item ID {0} from prim {1}, {2} at {3} in {4} but the item does not exist in this inventory", + item.ItemID, m_part.Name, m_part.UUID, + m_part.AbsolutePosition, m_part.ParentGroup.Scene.RegionInfo.RegionName); + } + } return false; } } From 1e743eab6d620a14876a1ec9b41b3e136af889d3 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 27 Apr 2010 03:48:49 +0100 Subject: [PATCH 021/260] Allow a client to pass a scope id to log into in the login XML / LLSD --- .../Server/Handlers/Login/LLLoginHandlers.cs | 12 ++++- OpenSim/Services/Interfaces/ILoginService.cs | 3 +- .../Services/LLLoginService/LLLoginService.cs | 44 +++++++++++++------ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs index aaa958b218..daf27043df 100644 --- a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs +++ b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs @@ -72,6 +72,9 @@ namespace OpenSim.Server.Handlers.Login string last = requestData["last"].ToString(); string passwd = requestData["passwd"].ToString(); string startLocation = string.Empty; + UUID scopeID = UUID.Zero; + if (requestData["scope_id"] != null) + scopeID = new UUID(requestData["scope_id"].ToString()); if (requestData.ContainsKey("start")) startLocation = requestData["start"].ToString(); @@ -83,7 +86,7 @@ namespace OpenSim.Server.Handlers.Login m_log.InfoFormat("[LOGIN]: XMLRPC Login Requested for {0} {1}, starting in {2}, using {3}", first, last, startLocation, clientVersion); LoginResponse reply = null; - reply = m_LocalService.Login(first, last, passwd, startLocation, remoteClient); + reply = m_LocalService.Login(first, last, passwd, startLocation, scopeID, remoteClient); XmlRpcResponse response = new XmlRpcResponse(); response.Value = reply.ToHashtable(); @@ -109,10 +112,15 @@ namespace OpenSim.Server.Handlers.Login if (map.ContainsKey("start")) startLocation = map["start"].AsString(); + UUID scopeID = UUID.Zero; + + if (map.ContainsKey("scope_id")) + scopeID = new UUID(map["scope_id"].AsString()); + m_log.Info("[LOGIN]: LLSD Login Requested for: '" + map["first"].AsString() + "' '" + map["last"].AsString() + "' / " + startLocation); LoginResponse reply = null; - reply = m_LocalService.Login(map["first"].AsString(), map["last"].AsString(), map["passwd"].AsString(), startLocation, remoteClient); + reply = m_LocalService.Login(map["first"].AsString(), map["last"].AsString(), map["passwd"].AsString(), startLocation, scopeID, remoteClient); return reply.ToOSDMap(); } diff --git a/OpenSim/Services/Interfaces/ILoginService.cs b/OpenSim/Services/Interfaces/ILoginService.cs index 24bf342742..49efbe2572 100644 --- a/OpenSim/Services/Interfaces/ILoginService.cs +++ b/OpenSim/Services/Interfaces/ILoginService.cs @@ -31,6 +31,7 @@ using System.Collections.Generic; using System.Net; using OpenMetaverse.StructuredData; +using OpenMetaverse; namespace OpenSim.Services.Interfaces { @@ -46,7 +47,7 @@ namespace OpenSim.Services.Interfaces public interface ILoginService { - LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, IPEndPoint clientIP); + LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, IPEndPoint clientIP); } diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index c333b5cec0..4d7dfd1eda 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -147,7 +147,7 @@ namespace OpenSim.Services.LLLoginService { } - public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, IPEndPoint clientIP) + public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, IPEndPoint clientIP) { bool success = false; UUID session = UUID.Random(); @@ -157,7 +157,7 @@ namespace OpenSim.Services.LLLoginService // // Get the account and check that it exists // - UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, firstName, lastName); + UserAccount account = m_UserAccountService.GetUserAccount(scopeID, firstName, lastName); if (account == null) { m_log.InfoFormat("[LLOGIN SERVICE]: Login failed, reason: user not found"); @@ -170,6 +170,22 @@ namespace OpenSim.Services.LLLoginService return LLFailedLoginResponse.LoginBlockedProblem; } + // If a scope id is requested, check that the account is in + // that scope, or unscoped. + // + if (scopeID != UUID.Zero) + { + if (account.ScopeID != scopeID && account.ScopeID != UUID.Zero) + { + m_log.InfoFormat("[LLOGIN SERVICE]: Login failed, reason: user not found"); + return LLFailedLoginResponse.UserProblem; + } + } + else + { + scopeID = account.ScopeID; + } + // // Authenticate this user // @@ -219,7 +235,7 @@ namespace OpenSim.Services.LLLoginService // Get the home region if ((presence.HomeRegionID != UUID.Zero) && m_GridService != null) { - home = m_GridService.GetRegionByUUID(account.ScopeID, presence.HomeRegionID); + home = m_GridService.GetRegionByUUID(scopeID, presence.HomeRegionID); } } @@ -230,7 +246,7 @@ namespace OpenSim.Services.LLLoginService Vector3 position = Vector3.Zero; Vector3 lookAt = Vector3.Zero; GridRegion gatekeeper = null; - GridRegion destination = FindDestination(account, presence, session, startLocation, out gatekeeper, out where, out position, out lookAt); + GridRegion destination = FindDestination(account, scopeID, presence, session, startLocation, out gatekeeper, out where, out position, out lookAt); if (destination == null) { m_PresenceService.LogoutAgent(session, presence.Position, presence.LookAt); @@ -286,7 +302,7 @@ namespace OpenSim.Services.LLLoginService } } - protected GridRegion FindDestination(UserAccount account, PresenceInfo pinfo, UUID sessionID, string startLocation, out GridRegion gatekeeper, out string where, out Vector3 position, out Vector3 lookAt) + protected GridRegion FindDestination(UserAccount account, UUID scopeID, PresenceInfo pinfo, UUID sessionID, string startLocation, out GridRegion gatekeeper, out string where, out Vector3 position, out Vector3 lookAt) { m_log.DebugFormat("[LLOGIN SERVICE]: FindDestination for start location {0}", startLocation); @@ -318,7 +334,7 @@ namespace OpenSim.Services.LLLoginService } else { - region = m_GridService.GetRegionByUUID(account.ScopeID, pinfo.HomeRegionID); + region = m_GridService.GetRegionByUUID(scopeID, pinfo.HomeRegionID); if (null == region) { @@ -332,7 +348,7 @@ namespace OpenSim.Services.LLLoginService if (tryDefaults) { - List defaults = m_GridService.GetDefaultRegions(account.ScopeID); + List defaults = m_GridService.GetDefaultRegions(scopeID); if (defaults != null && defaults.Count > 0) { region = defaults[0]; @@ -342,7 +358,7 @@ namespace OpenSim.Services.LLLoginService { m_log.WarnFormat("[LLOGIN SERVICE]: User {0} {1} does not have a valid home and this grid does not have default locations. Attempting to find random region", account.FirstName, account.LastName); - defaults = m_GridService.GetRegionsByName(account.ScopeID, "", 1); + defaults = m_GridService.GetRegionsByName(scopeID, "", 1); if (defaults != null && defaults.Count > 0) { region = defaults[0]; @@ -363,9 +379,9 @@ namespace OpenSim.Services.LLLoginService GridRegion region = null; - if (pinfo.RegionID.Equals(UUID.Zero) || (region = m_GridService.GetRegionByUUID(account.ScopeID, pinfo.RegionID)) == null) + if (pinfo.RegionID.Equals(UUID.Zero) || (region = m_GridService.GetRegionByUUID(scopeID, pinfo.RegionID)) == null) { - List defaults = m_GridService.GetDefaultRegions(account.ScopeID); + List defaults = m_GridService.GetDefaultRegions(scopeID); if (defaults != null && defaults.Count > 0) { region = defaults[0]; @@ -374,7 +390,7 @@ namespace OpenSim.Services.LLLoginService else { m_log.Info("[LLOGIN SERVICE]: Last Region Not Found Attempting to find random region"); - defaults = m_GridService.GetRegionsByName(account.ScopeID, "", 1); + defaults = m_GridService.GetRegionsByName(scopeID, "", 1); if (defaults != null && defaults.Count > 0) { region = defaults[0]; @@ -414,11 +430,11 @@ namespace OpenSim.Services.LLLoginService { if (!regionName.Contains("@")) { - List regions = m_GridService.GetRegionsByName(account.ScopeID, regionName, 1); + List regions = m_GridService.GetRegionsByName(scopeID, regionName, 1); if ((regions == null) || (regions != null && regions.Count == 0)) { m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, can't locate region {1}. Trying defaults.", startLocation, regionName); - regions = m_GridService.GetDefaultRegions(UUID.Zero); + regions = m_GridService.GetDefaultRegions(scopeID); if (regions != null && regions.Count > 0) { where = "safe"; @@ -461,7 +477,7 @@ namespace OpenSim.Services.LLLoginService } else { - List defaults = m_GridService.GetDefaultRegions(account.ScopeID); + List defaults = m_GridService.GetDefaultRegions(scopeID); if (defaults != null && defaults.Count > 0) { where = "safe"; From 76e87181b2cfd7a9dd7a73367fd4fe5c26ccd6ce Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 26 Apr 2010 20:28:37 -0700 Subject: [PATCH 022/260] RemoteXInventoryServiceConnector, the plugin region module. Not active in default configs yet. --- .../RemoteXInventoryServiceConnector.cs | 324 ++++++++++++++++++ .../Inventory/XInventoryInConnector.cs | 48 ++- .../Inventory/XInventoryConnector.cs | 29 ++ 3 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs new file mode 100644 index 0000000000..fb353c2893 --- /dev/null +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs @@ -0,0 +1,324 @@ +/* + * 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 OpenSimulator 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 log4net; +using System; +using System.Collections.Generic; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Statistics; + +using OpenSim.Services.Connectors; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory +{ + public class RemoteXInventoryServicesConnector : BaseInventoryConnector, ISharedRegionModule, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_Enabled = false; + private bool m_Initialized = false; + private Scene m_Scene; + private XInventoryServicesConnector m_RemoteConnector; + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "RemoteXInventoryServicesConnector"; } + } + + public RemoteXInventoryServicesConnector() + { + } + + public RemoteXInventoryServicesConnector(IConfigSource source) + { + Init(source); + } + + protected override void Init(IConfigSource source) + { + m_RemoteConnector = new XInventoryServicesConnector(source); + base.Init(source); + } + + + #region ISharedRegionModule + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("InventoryServices", ""); + if (name == Name) + { + Init(source); + m_Enabled = true; + + m_log.Info("[XINVENTORY CONNECTOR]: Remote XInventory enabled"); + } + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + m_Scene = scene; + //m_log.Debug("[XXXX] Adding scene " + m_Scene.RegionInfo.RegionName); + + if (!m_Enabled) + return; + + if (!m_Initialized) + { + m_Initialized = true; + } + + scene.RegisterModuleInterface(this); + m_cache.AddRegion(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_cache.RemoveRegion(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + m_log.InfoFormat("[XINVENTORY CONNECTOR]: Enabled remote XInventory for region {0}", scene.RegionInfo.RegionName); + + } + + #endregion ISharedRegionModule + + #region IInventoryService + + public override bool CreateUserInventory(UUID user) + { + return false; + } + + public override List GetInventorySkeleton(UUID userId) + { + return new List(); + } + + public override InventoryCollection GetUserInventory(UUID userID) + { + return null; + } + + public override void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + { + try + { + m_RemoteConnector.GetUserInventory(userID, callback); + } + catch (Exception e) + { + if (StatsManager.SimExtraStats != null) + StatsManager.SimExtraStats.AddInventoryServiceRetrievalFailure(); + + m_log.ErrorFormat("[XINVENTORY CONNECTOR]: Request inventory operation failed, {0} {1}", + e.Source, e.Message); + } + + } + + // inherited. See base class + // public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + + public override Dictionary GetSystemFolders(UUID userID) + { + return m_RemoteConnector.GetSystemFolders(userID); + } + + public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) + { + try + { + return m_RemoteConnector.GetFolderContent(userID, folderID); + } + catch (Exception e) + { + m_log.ErrorFormat("[XINVENTORY CONNECTOR]: GetFolderContent operation failed, {0} {1}", + e.Source, e.Message); + } + InventoryCollection nullCollection = new InventoryCollection(); + nullCollection.Folders = new List(); + nullCollection.Items = new List(); + nullCollection.UserID = userID; + return nullCollection; + } + + public override List GetFolderItems(UUID userID, UUID folderID) + { + return m_RemoteConnector.GetFolderItems(userID, folderID); + } + + public override bool AddFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + return m_RemoteConnector.AddFolder(folder); + } + + public override bool UpdateFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + return m_RemoteConnector.UpdateFolder(folder); + } + + public override bool MoveFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + return m_RemoteConnector.MoveFolder(folder); + } + + public override bool DeleteFolders(UUID ownerID, List folderIDs) + { + if (folderIDs == null) + return false; + if (folderIDs.Count == 0) + return false; + + return m_RemoteConnector.DeleteFolders(ownerID, folderIDs); + } + + + public override bool PurgeFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + return m_RemoteConnector.PurgeFolder(folder); + } + + // public bool AddItem(InventoryItemBase item) inherited + // Uses AddItemPlain + + protected override bool AddItemPlain(InventoryItemBase item) + { + if (item == null) + return false; + + return m_RemoteConnector.AddItem(item); + } + + public override bool UpdateItem(InventoryItemBase item) + { + if (item == null) + return false; + + return m_RemoteConnector.UpdateItem(item); + } + + public override bool MoveItems(UUID ownerID, List items) + { + if (items == null) + return false; + + return m_RemoteConnector.MoveItems(ownerID, items); + } + + + public override bool DeleteItems(UUID ownerID, List itemIDs) + { + if (itemIDs == null) + return false; + if (itemIDs.Count == 0) + return true; + + return m_RemoteConnector.DeleteItems(ownerID, itemIDs); + } + + public override InventoryItemBase GetItem(InventoryItemBase item) + { + if (item == null) + return null; + + return m_RemoteConnector.GetItem(item); + } + + public override InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + if (folder == null) + return null; + + return m_RemoteConnector.GetFolder(folder); + } + + public override bool HasInventoryForUser(UUID userID) + { + return false; + } + + public override List GetActiveGestures(UUID userId) + { + return new List(); + } + + public override int GetAssetPermissions(UUID userID, UUID assetID) + { + return m_RemoteConnector.GetAssetPermissions(userID, assetID); + } + + + #endregion + + + } +} diff --git a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs index 34f7dccc4e..7164520f14 100644 --- a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs +++ b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs @@ -144,6 +144,8 @@ namespace OpenSim.Server.Handlers.Asset return HandleGetActiveGestures(request); case "GETASSETPERMISSIONS": return HandleGetAssetPermissions(request); + case "GETSYSTEMFOLDERS": + return HandleGetSystemFolders(request); } m_log.DebugFormat("[XINVENTORY HANDLER]: unknown method request: {0}", method); } @@ -197,7 +199,7 @@ namespace OpenSim.Server.Handlers.Asset return ms.ToArray(); } - + byte[] HandleCreateUserInventory(Dictionary request) { Dictionary result = new Dictionary(); @@ -540,6 +542,24 @@ namespace OpenSim.Server.Handlers.Asset return encoding.GetBytes(xmlString); } + byte[] HandleGetSystemFolders(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + + Dictionary sfolders = GetSystemFolders(principal); + + if (sfolders != null) + foreach (KeyValuePair kvp in sfolders) + result[kvp.Key.ToString()] = EncodeFolder(kvp.Value); + + string xmlString = ServerUtils.BuildXmlResponse(result); + m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + UTF8Encoding encoding = new UTF8Encoding(); + return encoding.GetBytes(xmlString); + } + private Dictionary EncodeFolder(InventoryFolderBase f) { Dictionary ret = new Dictionary(); @@ -623,5 +643,31 @@ namespace OpenSim.Server.Handlers.Asset return item; } + + #region Extra + private Dictionary GetSystemFolders(UUID userID) + { + InventoryFolderBase root = m_InventoryService.GetRootFolder(userID); + if (root != null) + { + InventoryCollection content = m_InventoryService.GetFolderContent(userID, root.ID); + if (content != null) + { + Dictionary folders = new Dictionary(); + foreach (InventoryFolderBase folder in content.Folders) + { + if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) + folders[(AssetType)folder.Type] = folder; + } + // Put the root folder there, as type Folder + folders[AssetType.Folder] = root; + return folders; + } + } + m_log.WarnFormat("[INVENTORY SERVICE]: System folders for {0} not found", userID); + return new Dictionary(); + } + #endregion + } } diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs index 0cc1978a0b..edf224f741 100644 --- a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs @@ -455,6 +455,35 @@ namespace OpenSim.Services.Connectors return int.Parse(ret["RESULT"].ToString()); } + public Dictionary GetSystemFolders(UUID userID) + { + Dictionary ret = MakeRequest("GETSYSTEMFOLDERS", + new Dictionary { + { "PRINCIPAL", userID.ToString() }, + }); + + if (ret == null) + return new Dictionary(); + + Dictionary sfolders = new Dictionary(); + + try + { + foreach (KeyValuePair kvp in ret) + { + InventoryFolderBase folder = BuildFolder((Dictionary)(kvp.Value)); + short type = 0; + if (Int16.TryParse(kvp.Key, out type)) + sfolders.Add((AssetType)type, folder); + } + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: exception {0}", e.Message); + } + + return sfolders; + } // These are either obsolete or unused // From 3f9d38538e4d7f238eb901aced60de7e6673a92e Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 27 Apr 2010 11:01:56 -0700 Subject: [PATCH 023/260] XInventory connector/handler showing signs of life. Tested, but needs more testing. --- .../Resources/CoreModulePlugin.addin.xml | 1 + .../RemoteXInventoryServiceConnector.cs | 2 + .../Inventory/XInventoryInConnector.cs | 69 ++++++++-- .../Inventory/XInventoryConnector.cs | 128 +++++++++++------- .../InventoryService/XInventoryService.cs | 8 +- 5 files changed, 139 insertions(+), 69 deletions(-) diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml index aaa318cabb..0a5ff3f982 100644 --- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml +++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml @@ -45,6 +45,7 @@ + diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs index fb353c2893..8504b677c2 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs @@ -183,6 +183,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) { + m_log.DebugFormat("[XINVENTORY CONNECTOR]: GetFolderContent {0}", folderID); try { return m_RemoteConnector.GetFolderContent(userID, folderID); @@ -295,6 +296,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public override InventoryFolderBase GetFolder(InventoryFolderBase folder) { + m_log.DebugFormat("[XINVENTORY CONNECTOR]: GetFolder {0}", folder.ID); if (folder == null) return null; diff --git a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs index 7164520f14..c95b4d44e3 100644 --- a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs +++ b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs @@ -228,9 +228,17 @@ namespace OpenSim.Server.Handlers.Asset List folders = m_InventoryService.GetInventorySkeleton(new UUID(request["PRINCIPAL"].ToString())); + Dictionary sfolders = new Dictionary(); if (folders != null) + { + int i = 0; foreach (InventoryFolderBase f in folders) - result[f.ID.ToString()] = EncodeFolder(f); + { + sfolders["folder_" + i.ToString()] = EncodeFolder(f); + i++; + } + } + result["FOLDERS"] = sfolders; string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -246,7 +254,7 @@ namespace OpenSim.Server.Handlers.Asset UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); InventoryFolderBase rfolder = m_InventoryService.GetRootFolder(principal); if (rfolder != null) - result[rfolder.ID.ToString()] = EncodeFolder(rfolder); + result["folder"] = EncodeFolder(rfolder); string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -263,7 +271,7 @@ namespace OpenSim.Server.Handlers.Asset Int32.TryParse(request["TYPE"].ToString(), out type); InventoryFolderBase folder = m_InventoryService.GetFolderForType(principal, (AssetType)type); if (folder != null) - result[folder.ID.ToString()] = EncodeFolder(folder); + result["folder"] = EncodeFolder(folder); string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -283,13 +291,21 @@ namespace OpenSim.Server.Handlers.Asset if (icoll != null) { Dictionary folders = new Dictionary(); + int i = 0; foreach (InventoryFolderBase f in icoll.Folders) - folders[f.ID.ToString()] = EncodeFolder(f); + { + folders["folder_" + i.ToString()] = EncodeFolder(f); + i++; + } result["FOLDERS"] = folders; + i = 0; Dictionary items = new Dictionary(); - foreach (InventoryItemBase i in icoll.Items) - items[i.ID.ToString()] = EncodeItem(i); + foreach (InventoryItemBase it in icoll.Items) + { + items["item_" + i.ToString()] = EncodeItem(it); + i++; + } result["ITEMS"] = items; } @@ -308,9 +324,18 @@ namespace OpenSim.Server.Handlers.Asset UUID.TryParse(request["FOLDER"].ToString(), out folderID); List items = m_InventoryService.GetFolderItems(principal, folderID); + Dictionary sitems = new Dictionary(); + if (items != null) + { + int i = 0; foreach (InventoryItemBase item in items) - result[item.ID.ToString()] = EncodeItem(item); + { + sitems["item_" + i.ToString()] = EncodeItem(item); + i++; + } + } + result["ITEMS"] = sitems; string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -483,7 +508,7 @@ namespace OpenSim.Server.Handlers.Asset InventoryItemBase item = new InventoryItemBase(id); item = m_InventoryService.GetItem(item); if (item != null) - result[item.ID.ToString()] = EncodeItem(item); + result["item"] = EncodeItem(item); string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -500,7 +525,7 @@ namespace OpenSim.Server.Handlers.Asset InventoryFolderBase folder = new InventoryFolderBase(id); folder = m_InventoryService.GetFolder(folder); if (folder != null) - result[folder.ID.ToString()] = EncodeFolder(folder); + result["folder"] = EncodeFolder(folder); string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -515,9 +540,17 @@ namespace OpenSim.Server.Handlers.Asset UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); List gestures = m_InventoryService.GetActiveGestures(principal); + Dictionary items = new Dictionary(); if (gestures != null) + { + int i = 0; foreach (InventoryItemBase item in gestures) - result[item.ID.ToString()] = EncodeItem(item); + { + items["item_" + i.ToString()] = EncodeItem(item); + i++; + } + } + result["ITEMS"] = items; string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -549,10 +582,16 @@ namespace OpenSim.Server.Handlers.Asset UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); Dictionary sfolders = GetSystemFolders(principal); + m_log.DebugFormat("[XXX]: SystemFolders got {0} folders", sfolders.Count); - if (sfolders != null) - foreach (KeyValuePair kvp in sfolders) - result[kvp.Key.ToString()] = EncodeFolder(kvp.Value); + Dictionary folders = new Dictionary(); + int i = 0; + foreach (KeyValuePair kvp in sfolders) + { + folders["folder_" + i.ToString()] = EncodeFolder(kvp.Value); + i++; + } + result["FOLDERS"] = folders; string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); @@ -589,7 +628,7 @@ namespace OpenSim.Server.Handlers.Asset ret["Flags"] = item.Flags.ToString(); ret["Folder"] = item.Folder.ToString(); ret["GroupID"] = item.GroupID.ToString(); - ret["GroupedOwned"] = item.GroupOwned.ToString(); + ret["GroupOwned"] = item.GroupOwned.ToString(); ret["GroupPermissions"] = item.GroupPermissions.ToString(); ret["ID"] = item.ID.ToString(); ret["InvType"] = item.InvType.ToString(); @@ -664,7 +703,7 @@ namespace OpenSim.Server.Handlers.Asset return folders; } } - m_log.WarnFormat("[INVENTORY SERVICE]: System folders for {0} not found", userID); + m_log.WarnFormat("[XINVENTORY SERVICE]: System folders for {0} not found", userID); return new Dictionary(); } #endregion diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs index edf224f741..52294dab71 100644 --- a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs @@ -112,8 +112,15 @@ namespace OpenSim.Services.Connectors List folders = new List(); - foreach (Object o in ret.Values) - folders.Add(BuildFolder((Dictionary)o)); + try + { + foreach (Object o in ret.Values) + folders.Add(BuildFolder((Dictionary)o)); + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception unwrapping folder list: {0}", e.Message); + } return folders; } @@ -130,7 +137,7 @@ namespace OpenSim.Services.Connectors if (ret.Count == 0) return null; - return BuildFolder(ret); + return BuildFolder((Dictionary)ret["folder"]); } public InventoryFolderBase GetFolderForType(UUID principalID, AssetType type) @@ -146,7 +153,7 @@ namespace OpenSim.Services.Connectors if (ret.Count == 0) return null; - return BuildFolder(ret); + return BuildFolder((Dictionary)ret["folder"]); } public InventoryCollection GetFolderContent(UUID principalID, UUID folderID) @@ -173,10 +180,17 @@ namespace OpenSim.Services.Connectors Dictionary items = (Dictionary)ret["ITEMS"]; - foreach (Object o in folders.Values) - inventory.Folders.Add(BuildFolder((Dictionary)o)); - foreach (Object o in items.Values) - inventory.Items.Add(BuildItem((Dictionary)o)); + try + { + foreach (Object o in folders.Values) // getting the values directly, we don't care about the keys folder_i + inventory.Folders.Add(BuildFolder((Dictionary)o)); + foreach (Object o in items.Values) // getting the values directly, we don't care about the keys item_i + inventory.Items.Add(BuildItem((Dictionary)o)); + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception unwrapping content list: {0}", e.Message); + } return inventory; } @@ -194,13 +208,12 @@ namespace OpenSim.Services.Connectors if (ret.Count == 0) return null; - - List items = new List(); - - foreach (Object o in ret.Values) - items.Add(BuildItem((Dictionary)o)); + Dictionary items = (Dictionary)ret["ITEMS"]; + List fitems = new List(); + foreach (Object o in items.Values) // getting the values directly, we don't care about the keys item_i + fitems.Add(BuildItem((Dictionary)o)); - return items; + return fitems; } public bool AddFolder(InventoryFolderBase folder) @@ -405,7 +418,7 @@ namespace OpenSim.Services.Connectors if (ret.Count == 0) return null; - return BuildItem(ret); + return BuildItem((Dictionary)ret["item"]); } public InventoryFolderBase GetFolder(InventoryFolderBase folder) @@ -420,7 +433,7 @@ namespace OpenSim.Services.Connectors if (ret.Count == 0) return null; - return BuildFolder(ret); + return BuildFolder((Dictionary)ret["folder"]); } public List GetActiveGestures(UUID principalID) @@ -435,8 +448,8 @@ namespace OpenSim.Services.Connectors List items = new List(); - foreach (Object o in ret.Values) - items.Add(BuildItem((Dictionary)o)); + foreach (Object o in ret.Values) // getting the values directly, we don't care about the keys item_i + items.Add(BuildItem((Dictionary)o)); return items; } @@ -469,13 +482,14 @@ namespace OpenSim.Services.Connectors try { - foreach (KeyValuePair kvp in ret) + Dictionary folders = (Dictionary)ret["FOLDERS"]; + + foreach (Object o in folders.Values) // getting the values directly, we don't care about the keys folder_i { - InventoryFolderBase folder = BuildFolder((Dictionary)(kvp.Value)); - short type = 0; - if (Int16.TryParse(kvp.Key, out type)) - sfolders.Add((AssetType)type, folder); + InventoryFolderBase folder = BuildFolder((Dictionary)o); + sfolders.Add((AssetType)folder.Type, folder); } + } catch (Exception e) { @@ -522,13 +536,20 @@ namespace OpenSim.Services.Connectors { InventoryFolderBase folder = new InventoryFolderBase(); - folder.ParentID = new UUID(data["ParentID"].ToString()); - folder.Type = short.Parse(data["Type"].ToString()); - folder.Version = ushort.Parse(data["Version"].ToString()); - folder.Name = data["Name"].ToString(); - folder.Owner = new UUID(data["Owner"].ToString()); - folder.ID = new UUID(data["ID"].ToString()); - + try + { + folder.ParentID = new UUID(data["ParentID"].ToString()); + folder.Type = short.Parse(data["Type"].ToString()); + folder.Version = ushort.Parse(data["Version"].ToString()); + folder.Name = data["Name"].ToString(); + folder.Owner = new UUID(data["Owner"].ToString()); + folder.ID = new UUID(data["ID"].ToString()); + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception building folder: {0}", e.Message); + } + return folder; } @@ -536,26 +557,33 @@ namespace OpenSim.Services.Connectors { InventoryItemBase item = new InventoryItemBase(); - item.AssetID = new UUID(data["AssetID"].ToString()); - item.AssetType = int.Parse(data["AssetType"].ToString()); - item.Name = data["Name"].ToString(); - item.Owner = new UUID(data["Owner"].ToString()); - item.ID = new UUID(data["ID"].ToString()); - item.InvType = int.Parse(data["InvType"].ToString()); - item.Folder = new UUID(data["Folder"].ToString()); - item.CreatorId = data["CreatorId"].ToString(); - item.Description = data["Description"].ToString(); - item.NextPermissions = uint.Parse(data["NextPermissions"].ToString()); - item.CurrentPermissions = uint.Parse(data["CurrentPermissions"].ToString()); - item.BasePermissions = uint.Parse(data["BasePermissions"].ToString()); - item.EveryOnePermissions = uint.Parse(data["EveryOnePermissions"].ToString()); - item.GroupPermissions = uint.Parse(data["GroupPermissions"].ToString()); - item.GroupID = new UUID(data["GroupID"].ToString()); - item.GroupOwned = bool.Parse(data["GroupOwned"].ToString()); - item.SalePrice = int.Parse(data["SalePrice"].ToString()); - item.SaleType = byte.Parse(data["SaleType"].ToString()); - item.Flags = uint.Parse(data["Flags"].ToString()); - item.CreationDate = int.Parse(data["CreationDate"].ToString()); + try + { + item.AssetID = new UUID(data["AssetID"].ToString()); + item.AssetType = int.Parse(data["AssetType"].ToString()); + item.Name = data["Name"].ToString(); + item.Owner = new UUID(data["Owner"].ToString()); + item.ID = new UUID(data["ID"].ToString()); + item.InvType = int.Parse(data["InvType"].ToString()); + item.Folder = new UUID(data["Folder"].ToString()); + item.CreatorId = data["CreatorId"].ToString(); + item.Description = data["Description"].ToString(); + item.NextPermissions = uint.Parse(data["NextPermissions"].ToString()); + item.CurrentPermissions = uint.Parse(data["CurrentPermissions"].ToString()); + item.BasePermissions = uint.Parse(data["BasePermissions"].ToString()); + item.EveryOnePermissions = uint.Parse(data["EveryOnePermissions"].ToString()); + item.GroupPermissions = uint.Parse(data["GroupPermissions"].ToString()); + item.GroupID = new UUID(data["GroupID"].ToString()); + item.GroupOwned = bool.Parse(data["GroupOwned"].ToString()); + item.SalePrice = int.Parse(data["SalePrice"].ToString()); + item.SaleType = byte.Parse(data["SaleType"].ToString()); + item.Flags = uint.Parse(data["Flags"].ToString()); + item.CreationDate = int.Parse(data["CreationDate"].ToString()); + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception building item: {0}", e.Message); + } return item; } diff --git a/OpenSim/Services/InventoryService/XInventoryService.cs b/OpenSim/Services/InventoryService/XInventoryService.cs index bbd37d123a..1409a0192d 100644 --- a/OpenSim/Services/InventoryService/XInventoryService.cs +++ b/OpenSim/Services/InventoryService/XInventoryService.cs @@ -184,7 +184,7 @@ namespace OpenSim.Services.InventoryService foreach (XInventoryFolder x in allFolders) { - m_log.DebugFormat("[INVENTORY]: Adding folder {0} to skeleton", x.folderName); + m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to skeleton", x.folderName); folders.Add(ConvertToOpenSim(x)); } @@ -221,7 +221,7 @@ namespace OpenSim.Services.InventoryService // connector. So we disregard the principal and look // by ID. // - m_log.DebugFormat("[INVENTORY]: Fetch contents for folder {0}", folderID.ToString()); + m_log.DebugFormat("[XINVENTORY]: Fetch contents for folder {0}", folderID.ToString()); InventoryCollection inventory = new InventoryCollection(); inventory.UserID = principalID; inventory.Folders = new List(); @@ -233,7 +233,7 @@ namespace OpenSim.Services.InventoryService foreach (XInventoryFolder x in folders) { - m_log.DebugFormat("[INVENTORY]: Adding folder {0} to response", x.folderName); + m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to response", x.folderName); inventory.Folders.Add(ConvertToOpenSim(x)); } @@ -243,7 +243,7 @@ namespace OpenSim.Services.InventoryService foreach (XInventoryItem i in items) { - m_log.DebugFormat("[INVENTORY]: Adding item {0} to response", i.inventoryName); + m_log.DebugFormat("[XINVENTORY]: Adding item {0} to response", i.inventoryName); inventory.Items.Add(ConvertToOpenSim(i)); } From 6d05ea2a755a5acb4a54b01f2c02ba66a82bc413 Mon Sep 17 00:00:00 2001 From: justincc Date: Tue, 27 Apr 2010 20:44:04 +0100 Subject: [PATCH 024/260] update Mono.Data.Sqlite.dll to the one that shipped with Mono 2.6.3, which is the one known to work in Windows Thanks for the initial library inclusion, Diva. I had it all queued up on my windows test machine but forgot to add it! --- bin/Mono.Data.Sqlite.dll | Bin 169472 -> 169984 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/Mono.Data.Sqlite.dll b/bin/Mono.Data.Sqlite.dll index 2b81a44957dcce036ee794bca4bcfa37c8e543f7..4f69e0d4af194f53d9460b56f5c492b6b24cc452 100644 GIT binary patch literal 169984 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

TJoGQ2oh&)XCBoIg2M020u^S~H-cM`XO z=*b*59f-C!xpHz8Ett$XylyhTSSrV2i-^(j9O;0!B2JCqIbgIM_{Kh!QD$3)ZG4SQgXm zsV-~O3@U&dQ5x7yGy~R_S&^`r%#cDU*gxnZ%c_L^fX-7~N_Aj<5|@$;C7#?qZw)q+_sC)AYCpg+yizKHPcPP9u;<(Wru{V0^@goN5ZWI3n0BegH6|0 z(pE{~Yp^TTRdlnkPTdiw+CcY8xD+ryZJ}3`%7~|a%=FC|>-hafvrmjV?;&SI=Znur z!5pM_ad&=)r|C<1_r-AALeJ9QB-}~~_bi?26Ur1; z0CuVRk}5RESqkQ-(^M~RRbYkCw_tdVrv>8XsFj*)GXz$w{c5dVt-8`War3Jy#pgD# z=_*S7PS^vMy^NTMehu~t++G*(Vf!p^nqg!mj}*(burTlTrI znU;Nt)QJ}K;ChHsUum99TnRQ^2h`WXws>sj>N|UMqnWGk6MS6o*Mct#zAgBX;Fp3< zPxc=#*k5plV58s~!7YOK2>w>^b;09;-wJj$Dy~9 z_5hD3Jp|3?NsjBKoIUl;*K1FAA0_tU{A7t-)T=G23(d~U?->d{NF); zQT|K7P(FuRp8r;go7TR*!_#K2eopWp(A6LAI~w}$Iy3If7@d|wZ)S9(9D2DgPW>jOCCGdAqEY>hq&Za5kIQo^P*H8aV{ol49G;#)t%al09r|Hm z3FM816M#Q1oC4fgSP9%$I1Bg(!H)~)K>kYnbQm}fa?(K7aD)gSKL13vE<#CQ-g)?!XFa3|t% z=!rqxmL_E#`!@azH2hGgYY8Yb&!Fa)Lf=Pnu zg1Lgf%wwN@MII_RO0Z1uxxVarzR0r#hY)M#iM&v-L2!lO6@nWCuNTY~|64@{&G@$D`YxpL(&K0Z!Vs?VM*vF{Hvc}HZNi9DuKm0bjDN{uY|Y@@9?50= zi{QW!b8;Li&F0v+RiLg%%*z=}Kzoo8d$Y{ifrbqb}NGihUlojFTrvVWx zibh7T4m2@>1*j^5MbkWwDa_fMJhlq#X^*|ry(4TeQ97*&Y)}58dnX!e*~4J5G}p4{ zlfUjBN6RhyBiK2##j;b$Dlwk+SQee45)9}QuU|qbr;ugb=mIqtY^1QQ^mxj%I*Be6ww7K=>5-UB+l8&Ck5jS}Q|PeA`X{E*w;mgk zm`zCCNYE3h3%)FsbyfRg&m+isjDcHuC^>mW9+0--j}4UG%>X@u@}v=Y)0zr z#4K7Q>_A{%Y7u2olVw-NtpdAS*m`P9y*M$Ob_?52Yg3mx*|gXC+>CJ9w9m4i3fnJi zGd++xAK|9ubLyMvP-?8orQMdj4VFiN0^@c(^|Hi#+9~V+eV2MuVgYsSZQPR6ZcXe> zMZyly&@@H;=-2>uTOSyiR;>Ebam%KrZG_t?VFv>9(~77+buDC{TLZ0WPbc=LWMKya z>(lmwWeHnPo6=UnXO+j2odLAcvU}lHNV`3j>~3!pGX`^PY*UMQ`o#A%+}DT^v%vtN-s9-qVzxy_#A84 zRba#ENQGgygN-7ZY}g^Nv9#xW!`?}6=utwg(+xY7{`sK+ISU6i4FPNRvItR=|bwWm{T}_cDB^Hkcuq3Epv9#g*4K# zhcc_dN-f);SqC=RvOi^(BV3hbr!p^r+gxF5VfiitTO@41^v2n=T-bBOz2hP}YT49Y z%#IrtnAxic>@#7TrS)7yRL}W*E-=4WvAT%58ip3RCg~!2-?E9Rm;uu%%l7oz2$qLk zQG}xdy|yM*(`n0&^xBa$kM?1wl->S-^lIp^Wgqr>B&mk(xYW3H&w4j$0Ufn0FYC`q zAqq4bw?SE7B-PVM%SLB?o770_ESs3sF}axzTQ)nZdvY6fYBAyJv(l57(^$)vX6;D& zAyrwnJ}WQzGHSByrmO+Um($gjJpiApXuD-IJLDy=rpGLMB5PRk6?E9L-)4=)z?t+@PGdk{_Xcmd(ofDEV>P@3GI5pQOW0 zQx4M>%c660QhrA}E$f=wH{}J|YgtC_pp+Nspk;k?i&I{vqm~utj!Aiij#*ZgJ2L83 zI&RrCgnNy;t}ywWlY3su8J$n?kiu@Hp{q${DXE{#_jr_wBIsr zOG+KHjN7iGzO{_ou3z=I+@!#5w}UFOjB6}fm0HI25Th=%j7ztZY7%DNC{C>uW-R78 zierRjh-FI@W^8*r=I8edGqz{;xPqSZv+W5pwl7`XIQ?v;!i=p=n6Z_@l-H#0XDfBC z$Jj5)+g6WfydY`_4OFrrT!jcyEMRKY?lUl zjO|j9$GH9mdyMVUP>-=)D)t!LrI8-vx*hE?uG_I5{o*<);_W_XOP zRF%irO3m~bx2y|2#&+oYMaN{O5Nfywo*U#7~7>=J;rwF zHjl9dyF*uF>%ex{6b|@*lJ`#cohp76`!qAFT`JjQt7wQ(*(<%3><*PYPGbegb1=cWIOWX1l{X*@t?C1IMslQOit}t$o=BI&;UCZnMu`PH= zEjKK%KfefUwPi>1SAiX~?EU;=wO1Xt?C<#-X|FnESxiBn)Q8n+%W?_^f(6!b3j1kD z!6iwLsCZ%P=~QNM>LaR$VS$MSW5Cian^o{m_ea##CR|`aL3PrjYLjKl3QAKSRd-mQ zR~MX@`k2~j-EJX~Ndf zmoQ(yRG%3)itfG9`K9_+*m}A>_L9^mRNi`yg;`7LK2;=aE#=_Ud!HIB>;MhvU99%0 zyDcm4-5YMct2o>N`VeKkPX!DMRQ0|JvBX;z>U|v;CRgk;@T1;S=}DDk*_PgO!HR^L zmFch4NMWWe{Yp*r*n!kvspZ1?sNcbo?%oTdvW4#Rg=d~rv6r~ zwrm#Mo>!YZb~5#Ob%$k(;C5K;@z}}K!|G{a2Lcy#s&sy*4q6uKG#l)wWtVpNCq8h1(12Tg!d}wC`I`+Alc%}bG)%Cv&o0$5n{-fGs*$#bA z+B<6G_3U;7W%he2?N4gEFizo(wD;B1!c2SlP#y8uD*8|z6SjsL`>mpn)U-_;%X+Mx ziq&zo+_H83Hqvpm&9Y7XPNbbs$1M9vzms66EW4-QRQj{(af6BFseW_8`dD@pZhukJ zEIS6bzoT1irf!oJwyJfsT@riocvY7s>z>ZkfvwsntRHrPQ5(r2p9vZDTT!IoP#25z6L?Uqf4+vjSJWwmhot2$~~E8PC7 zj$3vm+`dqqHk;IMgxeP?-Ll(}&r_<@vfaq%DOF|J z)MJ*t54XRm!}ktp4e+IZtpYchSS}q91C}i80Ntx&eBY>}!q(8{0gmqALiQ&bo`ktwp$7)1!5fu&vbJe^+`({q)`Jwv|HhpZH_- zF^_#far*Gj*ljaCHt<0DIr?MEelze$dS~7B=f>@gfv=@^(eo_(Xy8ZbU3I->Uk^N) z-c2vJtjnO2>D~1@%kl<&nVzU$v#fZ~x9L6f`<9gt@@MqaeRh}>E*KP>ajxDi%=FYG zy;m5QZbn9u-XGzXq7QrQcuI1%g;#H$vZg(Q!AOeNZ9&-D(at+sqeNd z0j!sP%(ASa^E0yaVao=AW$R_)it)~{K1H{5#b_bqz_ZhiDA%MQY= zkEVM#FI-A@W%SjH43oJ{f88qVfb0|X*Sqg!pKItsEG+u#tlfsy2^%Tw0Pdybz^%%# zK=a^Y)nCuEY^AVmmR%!kr?542=iuEL{q<}2ahwNe_uyA`q3(0PVUG^pmoZRZYgw%C znT$dDlw}_d9;Jrqst1hE-)BFcQLOh__VwV0XtXZcW8Ch{ej{UyzSgpsA@634)u$}G zGW&SOI6e76U$Sl+ETXubRr_6Fa z_95f8EjuA|g5GXfap#9V+=K1=RVS&qsOr@zh@U(;rY!KGfFe*rRRZrD*mTiUGG(F&1 z_KA`oGAnbMJ}T@04N91bv-xSyvD<;b14D|{bX{-R2`U0x>9PJaLtiay4LvtRQKdd8 zjN8)U%u4OsZ&DaG^quY(=saN`ss}P!GH2>LESog6C3BYEY1xOpewcZoe$BFlaGR}* zel4+Z`}AF;#|q;fzCLrVzR+W<=m)yaV+wZz>pga#uUan`W_odrUU`7yTp!p!^xDiC zy-wH-^wQ89!QK+K4k_UDSbrug;X>ZSt<}p9ns6z_N3v@5YGDUdxqby~on;4ly_r>~ zuXf#v&(#a{wU!mC_p=u1O~TgFMa6&4x!ZJAwwA6bJ}0|DpR(-7 z#oede~w1Y1U(_^k>4%dTh1+R@hehOL1xT zYF+g^_PLq9ExsUojXwQ*X2yPAp#v{OvUp+Z=<=M^*;nW+<3{{OSLn$e`*HR<-5L>Y zy}rY+K;bz>v|jJD?DpZS!1h|Ub9k}3O7FAmk>MNZDt*wh{lkBneYHMf*^9$>g1se- z+u0M@8?^6L&ga@dzd*6NR;L?A9Y$=VYxP8rDY{Odw(JO26W8f^uW>k2F4yV9!qyU7 zjq7yY>k=oiE%+~ewPoCkuh-ix<6gW;zi%1atQ+*$H%vITSvTtKma)y+tm#eT#x`r4 zo@*J~tXuTcma&z(O((x)e6p3gT`#wct<)X*sAX)I?$iU`Ha^)d{ZwCT8QZ11bow8S z8{4J3^9HQWK4+h<^4Qj#Cw0BYZp(Q}ulCr_ zbDq(gJoaGDetoycewp(d{g}u0=RB_iA93ouv+!ch5nUyWbM{)!i~4GheVp^A-sdsO z{Xie_SRnT!{l3Q%a{sJP2|Ez@#mK32Qh#gNGb87M1&(ui2LeY%rsRIAFSP8N7!zgEzqQB~G)@|UZV)a*jhh-y1 zZG_uy%SsW;7kaN{7a*1|^wX9t7&Vnn>4TPCF={T@QOj-{H6Zs({g!2WMhyWwZrQd# zZ~B}5%(7=kjmrI-Et&e?Ok`cfDHJfk4UVsdQSevuviYt1Vj~>{`p( zM)!u#O@_%F<+Q$A*m|`0`MIa{L60@$eyfjrY(?%rb>PpO7xuX}S2=Tq9RS;ytDSo5 zcE#xHa~*GSEkNaX$0d#@w!s?_-nC?>b+mx;g2V?NFm~6P=Nk9qqh|dN`9k zwvl=|b;9=3y`x{sJ=baV*c-XY&L&}N=`n0Hr#jm$dusHba#NjoCpkT?>7IFM&ah8~ z(M4nW=Jj%36J~5kw$t@9;}#mDDBGDUYz-|RvoAN>8TMCk3tT&9QeLhz)-bwb%qq%t z;=d3#q%b!x&*^%K*#UY0TlEFb-NM$(-dcgPTiAZtO)GHr8YU;G1x}AIC0yXp7+-pC zC*3ewh)wq1PL;6D^wyZl#NN(MVQXpLh}OJ5&g8$jsVnO1R0-Q0NEmxXUSDUMW!Yng z!R-#q3djBz+;&l!r1%s`g?BC<96i@_1L-Np2-{Lu@U2*&l~BniQ`_*8||@K2shSa!EtZojq}(V zuo92m23F>=C&4Co>=m#Ik9`U@$z#s=H}a-N{=lDyTD^tfz9&R ztzfe~_Tczqd2>AW^!N|*=6dYI@t@_@cx-sdvAjz>HU(_4$L5##^Xom8`vhJvOIwQ2u(4Er;9H9=i@~gU5bcIy`@)$MztFYd!W{>Dc`L^4M#oZ{%(A*vIgB zqsLTPdHxoUbt{{aztv;?%68@5>ap^&V|m*>HXCj~@z|2G+4;A5>O9d+dXe?|6Y&vEMJpdasZY{q#|5qM+u>AJ?XFawb?AIQ9seDKNL63b{ zz9;{Ak9}SKc>eD_7C+%w-isc~obYV^OCBqla4`R<<2%hgp64hdqh5Eq3S--<=nW@J z*nXNbVJg@>VFv;sVXeZp29`~DIsXl3rN`dLf74kf%*6SYvqhMR^KIvDVJ6PEonyjG zoNqg)gmIkj=D+QH>#^hcf0SH1GOwHGyki-!t8~wIJ$CDaPxC+Y*z*&<%m1WZxRah+ zx$fBWGmqUm!C&yX$DW@MUvR2jxGz1oay>HY?;hiDr#;5$ebX-7x1L+MPAvG&W4BI7 zEs&2%$e{y@!})w%3-0^*E#vp=UC_Z}w@w&X5bZGz7uzmeoaa`q&&iJW*sT-B6m;YU!lC|n1(jNdQWV;nB6UAT15tz37{&h*%=6DAkrdW_4npRe^Rjx|bJ zR)4rDa*`N305x?KIsQMu)yW*{2EhmZ-@p`(f4|^+g8u+&>XgcQgRD_xaD?b1^Ybfc zG#wEAYY}L29-qcxvw)fkfDZK&jd_>bMK<`T$Oc~(`C|_?eIs%xo#SZ*I&?WuQKV+8 zXdVzhkBXls&+_wXgyv&UK06*?yZG7Xze;gxhU5XLsS(KckoikdPTfzJi|dUM*p$is zxeX{P?ZvnesA((Eq3u8)?G*hJg3m|bNywW13UufzppSHxMk<}MI3D(?DHF0orfmQB zH8anOx%Lc=NjEZeSN@sj)75a^{K?#==F zC<*ANbYK+a0~M73wdft1CYmLp;TRmc6ta(6&(bdw{SQUYacZ05&qU_8aO5p~@)myk z7Eg%(??X;e+cx(9L^f|78H1wwTzw!)z^OYH2_-JJa2X+8dvb`@?R?z{Z=7A)-Fp=z|le`i(xy`T2! zo^3b&6Kli0&$Si*Q#->g*0pThhP7DB|G(|be_~;{e*cv{iL4`If6Um;_Q!3*MmEU( zPSKV5E^0e}Gqa`}&d}qlT$$TEAhRsfTK@By9zHBVS_UISeJA=}eYpJl0v##@`e+PL z(OkhxB5;?;hk%-nh-_keN#sb3OeIqOPJH(2%kdk1k;pS6(DWcPUVi{RW?3>jitWeg zZtul2f$oqMvBsdLRLHKrulO7!uIy9O9Ffgz^M@iw=IZ|&`I-S5IWcMN8o)U>_IdO` zmMa8j0X5Z~C9e_9MnMz99U_~)bC1Z}W;EUJ$ygCcD>5zf*U&p;TIOrw$H@N)E*Qi) z+z&*(i~J8kZoO{1HL@w;?<>#gMI6ufxklh^K zYs~1{5F_V~jBY$eHle~YzBY}BryR0FRsT}sYnNi!r_uXu zJjNE8d`9|dL!5qE1&pGNzz(zp7?3!lC2Wkf7~dC`Lq$ob4iYLLp`z)>(7RzdMQ!sR zmff;CQ{$GEDgXZ|8ryhwZX@d@Tmo+0u?F8t7UHizaB7#TQBcCC|U?v6R#qb(kt_j+YK?`AxY8Ihmff=0>v zLRZ4B9f3aTQo^oDK>S`6u!HP)1Z2k}ng+s;vUOx?Dx8WN+ivj_E&;Rp^U*WV`{_6T z(lxU5{*%?>+5TZYN;&>P!1lGnEg@198TNlq=682n8n-c@v@w*Pv|%$^nNs@i(TsH5 zh8-y-pM8t4-re8$x{Up(a(<(;T}>}A>4sg6|MqddBDH^Z9YvO*iNW-v$P~jGH%?Oy zW+(PZ$=mm7xMUnDCAYQw_ZrTj>oZa|JC@@naG6h@AiHyrjnDRILPg?r@TqBw_zz(W z{r+9n|IRgvo<$5Dh+|Xqir|?sV+UB`XP?gh0iTg8sj%!wPJG`Rf7qv@u>9{rnc6>d z_T}y<^6bth>H5Dn-N<(M?@Is|WDzHRp9f{-&JvGFshTqU*RE&QLJW=49cc-ExvY>9 zAZOxMd^Y61_}?G@3!xhXj3z#vOTaG*#8FQx55ccCW{6x!>mZGj8!W~23iYN6I)U)f z_y)9yrc<&ShOmWdl*CX@HMp&y@LOXStEm){7%mlDLAR?)T7j=5D)G$&f3I z1^IRT7`;oM>0iN54|)zc?CcDq&#BD$12i1=E1Ki{5kF~W{I766plEfy(@pe$rk?5< z=Tpj8uQ{ioe;@i{@Qp&)&A#!#dw>Zjhcd{wt4YB9h;z2$yEh5+wr`!}O;Mq|Q6YWc zo2?4T@2^%hYKIO&#>cOSxxc>=@?if`RY4>Dm#KV|$r@;kecd1pt2h|SX@nB0ROt@u)ri}#sv04E>yo%)9K>C0rjq088{5hx&Z(7 z2FH*kG4z4F6*9+@CGljTo#R^=+7|c>WqShOc!s`3k?W_l;VEc1v1m zj;*J9GZ4@{Rj2599j&`XpR0R{zF1!tovDk}FQWSZV||7CQhgwL1n~Lj(RzjcF1i%h zA?7@|c8aOgUs3m%Ir??f;C#rbF$?t?Jv^oXnmIA8zy&c^056T%0Q_Ohb^1>JGDBx* z#@8X=tVhb*;Z0+HhTj}|L6@nK@^&L7=OgJ4zTMC-(?!VhwwS&87X4(*lfdVIm+CTS zw7yewU*4tPiFsB(sK1Q)jecC-v~DYZ=he^5i3)Dc}MLqb`Xqp@Y`3uF3v5|N*R-#uV_QZOlO7O(y@rqQ z)$s%T$|~RTKOw0n(DTuq)#v)Pj#KF?{YA$o_2>GVj=iZ=-m6qdd*4&TSG zQ734d!`Ah7;7I?^ov%ijq^0eFJW)hnFGAS(`<3BDxwmgr9i{!KLh z6get^{U-qP9lqh5@9e%@=zMj6GoqrE3?tBzDuJaqd3?G+pPv7j$@A~q6KLQ?duI_vsV+h+Z zwkMzaqUp^*cVB{Ud*>pIaouCS#yh6QC_neafPba5tCeVPTWKZzIBu_6i53_OJ@=QD zXkA_X*P?Z$`?pBzx>iQ^mD0wx+Z_IeD*Sg#3moTrvh!U3c1iti@wr`E>2|R;JdP{; zQZwUTL2if3{ktXI-O{#qOX?$~&+eAg_etveCH0ZgyZ6)K&W|`F>1b!36>x4V=D~qG9B^o4fy=7 z^D*|1vOO%hJ&ck+=07T>cT{pa-51m4Bjl|h;S*q7m%sXNQ9ZkS139}3MZG9?o^5P& z)NILBjYXbQ@r(rVYxml669e6FGw1z#6@SMY@3=Yn4eDnD+uQnX+K5G4jYQBVk6Ma7~YCvpW) z(R9Jtf+67izL$!;2DrBG4d|zu)8D2|O|Kv_YaO9{YFhV&FU1BkZ}EC#j?iz6S6i;BZ8 z$uwXmx(pak&jP#B7r-8*JH%lQk^oGne!wi61k9s1fqm$D{I>7_nuOmjE~0OM!|0}% zIJDEkj&U@WUIv!ZhE8$#ZS#!SIE*I`1E*1CTpaBEm%t0@zH{P`%jxlPG>@EwII5$2 zfs1H*=Qye-r%N1W&$k0xX=>Lv^y5!}tEp4BILzL2y9LOnCIbU%IeIxK%&3W6T zIjt?4)7qwQ25#5efp_RTfp_bl19$3sfxGn{;2zEIvR6M0d`$lmxKBR?d|K}Z?$`W| z2lXM~Vf}mH5&bgosD2IjntmJD$>IFRJDmTn4(C5vS?2AmN638d0>P_$oC_jA~MAl^kZ6&5$g zk`BAlTPYx=H;*u$e>J@j%(2}UAfB2GQIpn!BQ&hklpncZvRxMGpI3)XQ)!>F_4n zK&{|f!S@860PA}RmIOHd6M=`2?umf3qG*1vL(wcBihd&Q)R2mfm%)EXFe8rrQ#otW z1?Qc|t?2FxxJLxAhda8rLp7?nCw}*|!~2;E_LHatwmyJoDSgnrOJZu``H2mQS0?UA zyf^W!#FL3%C8{3rJqmhM_L$eBt;f0^xAgd3kCQ#(dk*NiG3mynpCsL%^y?&l^5o>o zX+KD7N!yn8 zP}+gC57H9SlhS{fz9s#h^e58aO&^glKO>ZJS;oT|uVheWO6Ii8XEHy}@@1!G=VbTI zo|OHA?B8UM$tlli%sG*plQ%eTMBcQ#+PqL+L*CVSx97#+P15CEdL{E!=Yh%m`N+8B zaljeL*es=;8LWRGgY}POu)e7Xn~=1!XgEeV{`F9tDgfi~>+}h94t2(_X!b;$>G&fc z88no7;dkP*F=F+>eo$YOSbr*`LMo?$G!;H(Vsx8DgYgS~!|;pl#TcoGvXVA;C;Tmm zZas;f%3%43;G2T)XCy)Yg~%$CUC+r(gJwV;%SJOSkNp^pk?-lvu8;I){ka8Oa3l8D zy?+dxNVfxz^u8N-Pse+J#m*k!q%MyD-|W2)_;K%Nfy?^x9oe!@MZmRvIkqi=cL?ql z93bgFG9Wu0IUm5GjtIVHWXbb;130XCi;qNqQt(^BU*>VB=tB0>xsY?4B$y{Su&@9z zk1OmCoKjc>ysBt@2GLD|cL_cy_`(nl`-!hu1g{ZXJAvb544@5a%(UWxdl|`+xYLZd0A1xgDr6m+JzoMVh#Qf^;+m5x1sC5EJfSAg8$Ntka+6sxRc8K_{V#p(bipHoBkVgaYTkqJ*)if4} z-&0p(AeRCa%~azcUjS5ep(=qq3y9zERArE71F^@TCP1D8#4mDUUs7X5QvrFNngqET zsA#^L0=WjLFsqyjxfZBs0e0w6Za`RO>=J8Q2vl^5x&U$rhS8czX2-xPF)H4A3%jen5!WF6MvTitzK_{CRJYpIR%K{y4Kf09t>18 zOkWRqC=fSO^bL@Ufr>`z&5%a`QM39c$fJR%S^NbH)GQG9o%Jn{#{*Hz`c}xLKt&Vu zPau~Io~LhzJW1aPoUHExPSHOD&e1#I^J2XV@?0SQ_QAc7e;|0Nz7KMnegL>sKL}io z{drB7>xbcUm3|cRj|5li$04uLPXMpbPXdqVr+|OZ&!BhSkNWw|*z0Kr-Kkzte^P1s zHa*l?;N0&#;k@F!=T!S1_WjD&$3MhB)?ewb^*8!2_iywc^uOpQ-O>N}ST4y}{|oXw z#~(e8d4m63d3N!Sz|*I@`6mib^v{xKPk&IJN&YqRO!40)&ouv&^33qRBF|p_Pvx2I zcgAzPx&EH=%=Z_`v$ub`Jp1|=%d@}#DtQ+AZMd{h1M!-;yj zf3)yQ{~UQ<;9oA!S^n$fIotnZdCu|gk>|z!=j2)Ke@&h>{*UEZ>&Nj0;tl${$#bE< zpFBhUa(OQH&z9#B{}OpN`md1ZrT#7QZ1L}uXPf^Cc`oz6AkP*459E27KdPMLzuezb zo~!)*J1<{y00=lGj3VGZm3Xx04B^~ExFQuxtM zSA$XGOx^GB9kD0@9LqD4iE7pw`S+0LtTXa4$QN8`$M;Mfov+BU((pnVE#yWq(i@w#wAM{8;Cv4XyiZiY5yd+Xynhb(!yYCHQZXL zqO759NwB#_l9qfRxElb@4XwfEhMM~Jf0BHBO-qm_1Y0A*a)c#q&CU3$l5WD1NCm%H zRik~YlD7KRw&o!JRc|-P6Yz)bYU)Ejtl__PZh|x{t!WO`G_+dZg!gHxX%13_DU@l= zjY~sy!Dgx`4Yf2iw(#GXB_jrDO5@Vt`N8EVx#opoD-T_38O z+QhGn1j8<+!TR9B8Wc2An;&Wj(v08|#6@LG5y`}whPryBSlJ36C5vhr76#ktIfso6 z{O+bMxeAP8N@HEHp2|b@K@>r-W{K;eVyY;q1WF`o9ZTvPQL^D`mGsU^6|crMv$Sz> z(0k4Bi=5xsTCt?5KFBX0tPAU_8Yed{!#k9kKLB6Tykg!!H#ZSg-fu#%A&5T`Pvh$9 zsH%Ads(EHjeH*ga*o^olhgw=WTr2-E_*tRWMO4`~zXgr7VIfMaslKLGUIx_GTC>#Y z)aJSnno-5V2K=4Y5^g>fuKT<~@nIW}Tj#bMtD0*XT58N+tVeDYfK0A6yt}nA})XC%KnQR5qcC>HOd_*ei)vOv<=g?$V|!woq9^ofpWI(~MvZn}`dS z)U(9BkhtKjnjdh237rybtqG^k1>eFJ0A?lB8VvunVy|5A#%vKK&y|gB&9y<8qq?@* zRt|c$b|(IQFIOHX(ayVdIG@cttY5H>CNwv;HH}{}KDel6X$S*BIB_@4uts_i`c$xa z{0dXFEfo!wuwWQX&0&+&i<5|<+f$CQ@ehrw>825gfsDd zddZwvE;d1Dm7J-c$td3D8g4*RoL+A|uQAkMtaBsUCAT83Zqvjjw1w)pe+?<3%GyQ2 zB{edZnfFJfabuENz+XgX3pNdHIwYlU6_wH)jYn&cA|skALqtuzjj%Mhpa#R0F@B}v z*>sR0TJcAuCax#GTG$QnFOhg1<}+^D?fj6(*{-S*$bo1r4D~VOzzj z7V^6l4xv(H8;ycWV4jTiENO%dVk1|JDFGg>==9QUf>p3g(q1t>%BU)R+KiXNt(7(J z#-m1EaQV~)Fr}d-ge=JmO{)nt!=`gk^MagtDO<@qvxw4_flr2@()sS&m%~J}ae-No zf+!CK>+2#%`5D0l?j&S>H<>-{J=QE541L9 zj{TTwfNEN6GhGyHZsbv@%pHT!-C7o*+U&^X<^kT}v&qW>n;uD!4JOBIhaMTakXBWr z`_h;faP^uF>{Tw$U}SbA8rT)_RN1{UF@}k zaSbbEkSk&Ph`ewyi#(+fCB?H*GbOBA)QoKNAXdh&LuJchl1rLVnCMcrD$p)5dIo70 z)WOL-gDPuS8ftEAU@IVvi<35GI0N&kM$8K(S~C`yxuey{4313!O(<~(T`NdS;2tP5 zl;)`mriCyGuCr6=3R})@!;}IxwT}`B2BpT9V??Rt7g)p-If)Q8>P}M8CHehYTG7Mi zdxj7lz-_=h?_RP5Z$PtxHH*=*gP8SV+QX%b9$p{9tdS!v4Yt%ahnlz;;i);;Vn*Dm znr8Tv*}KFtzAaSGLsjW~9wf%q)iiMlb7Ya6Eny@tf#3SZg*?Q16X!E^9Og_N$7^+- zWfn7gmXdvn$%h?X5LzytBDv>Y*|wmayXEX|EMWVD&eTOtq%jx7D1Sjy9bT$oVc60{ zC{1%{=8EMhJNHUc8Bj>TNJT|M3#Nsx56lwGl+_fqoeNl3Z`>_g9<0TJ%eq!@p<5X# zM2tH$y)D=lM2<@_4TTiBl!CUb!DuIxz|IDnOtXkwvYCGG*1qX^GK)p+BPBbf1+fx$ zXw$}A3*fG%A|}YT4jZVIMFL$CR;n4c*mgMDLgqTrxk{d`2j58H+v@F|2y(f00^5l$@@NL@e+^ufAr5$IYzA ztzv$y_8Jb;z8SEe_8RunzU{D|_8LjMU4vsk?VDg^Q|2LEs!NnDFaf-rbXzYLSFSh( zlMI;A7HQ710-X?S<<(uS^lXX8ZU0!$@p9CTz7flByROC53o8b9v0K&Xu5lpoNLSS; zGip;sGJarYgxa#B2P~i&1Z6D3OhhIzu%5x@@I=7a9pT(Hq#cOW!z=-_vzD^uO)zyb z?W$-&zTFvcMa#rcU0tvNUM4rnQj2@l)P{O#YOYV+F%%cfw;dGi6!FYZ0u{(b#yE!v@BSh4))(gEL^{=hxIOwm$Ga7*1rq zAknv*(3n+WzjhvYjfOQoV&%7wW;pzxW<62tKOYr8XX)AM|1S_ejuSamtgDXgR z!`*#)qfTA>@LV0-*4)_^b6y#R(_V;HYkYBXZ7r>hOT2E_A`Ny5s;_1t58vL-&^WBX zm)7t|X7(%4eOhbWF^{c!*va;CPmPF;YB1GZkg7G63(a!^A^sB>?0WE>25_T=j zfvcvLPW6gh0+lw_$}37MLxT>sclP!G+N)(|iA|NW156Gr31O!S3(xi;BDY1tF6K3m zPj4?#-pifSS5D`(+90w|-nM{gFpLcZRT$X}jb2_c8JB=q%r(edU-n4cw9Js*j=3XY z<%)*dMVL0)EkRzow~(>LV*JG7L~t>NZ2v>8VdLn=?7q`9Z1#}AnJ(>zVb2vd58FxT zJ>xLB!CV+yukt$FSj~*weZlk4tWXP_PxFf6PjYF3u*m%u0iHwg%ZGqj3utHp@OHr*ej4*>ZCIN%nY%l~%$`^6qKe6od|A zRL-zUE{L;w^##~fT!31ESqmaJiyG_P+6ngqGeFx|yrf*M)Uur-z-Y^`Vd}=TMYiM- zN_S{ELv8%JQvnk`qHVW#I3sL1@2H-k^D2-=PHSe99rX5srKU@<-4&{D8BJMYX1U_C z3J2gTi!dD~CdnPP*YvY9QB z%(coDg~QG#mrM!OHaE63F2F8Cs0AClm91@cp~ipXTsA{Si`W(IwPskv;9NbUX8!z8>y+u9dx<&DabLtNnmt!TW%YKJ zPBM9xHoO~a_buATTwU5+vkVzIE1XedQp&G>rmL6#Gab%)U7KM{ZFtuNHIA*5mNP>| zmb(ow3p+bFQ>teBc$R;Zk=bJ4Y0;9Fg|uKvD`6?zLbb5n?iK;}2bSGu1x`d7g0g<8 zu0u!mwxg+f3Ff-N=DPV*jSa%4FstBa9jTCA{_587L4w)nudXwz5^w*Xs)OOV zVRg%;^(4s~J3`e>H7za68k_5&H34~}2D|lCjiV!+tjJDf^-{a}TiwPBS{!>=i+~*Q zXcgwocHSA*m=JFFw*frr`gaxbqGmUPMvh)kUEQ)Q)LOeJYe`MhfHM_^C~tREw9CG* zf@`N$d}6L4|XiEd^yuiyqmXiy}KUUYsKT4*>=62sb|lTuVnp2s@RLMlVn{j zV+U?L*xs=Yma)SOl=FC=-&-e-^K|v>-ArkFR40dw+}+IFm+csFTbz%{k9%4}QyU_) z*AQGL)SPET4of(>i9BIK^9kb66sH%+l^lMwpVFF2<&Moks-UChL3aYHsCb3OQY^Q8IylccB|Y|V5N()^jd&fUP$Yh;BFM5 zuesZXQe{{fADLF?rH z+Q3J*3*Eg%-S%)mh=4m$W)tlA#^KnyN78Z7d04keBdaXuuX+#xU_ zV8J+3RM%>p#^XK>%?EJt8_?S9Anh*pN&i8^Ij+AycMU8#sGY7cAf^o{x!jEAc>&y@3XMRE|)* zsfzioyYe!Fb9Wa_*x|!y z&SnfN{}7+}+cgM^(=NG8HHA5rVD_2|H+XvqvKnI}CXQG@%Be{;rui_f7$i(iA}5-W zSu`^@bKim+0A_V9Bn83e&{o5h7%s+zJi z6A-zRZbExQvI*c(c8xr4lnC1?*oSmmzAR*x4H3~t6f1j-oUn6Bk=!H|sbL@O7c$|S z7w8>!chH`+wmXfT?_PhBH4Piz_MXuW%}$~X%y)Y1Eg929>cVY=b8(hZGJ95glMJ7w zb?bw-EL_=ET|}3OD8sW-Gg~o{ExV=;8&FM6^((w!=2*#XxiXx2jhpAIu=ci9i`%kA zxNlHWUxVohw@EJ=885wn?U{SqDboKmzN@*YQSKg8ish2rqqn$A`5Jo{0VV1>$qu{g z-V($-r5Rt*AS=eSwV-m_WvB(Gwpinjd{Wz zr#rm2$lAx)GxuJV@no_lb8~MxigtxHP55F$I%HEupkd}QZ<`Ez?|3wiV4rFOGuQC&U11dO)KiZP$T)q(|$ z&Ex9i{tX&GI6Jw+0uIr5IcB; zT`p(|$|&QW2uRSglCE6^bM9zDNSI|ch-%OVHR?CKGAndWW=u!~2G%fg@( zv2n44UAP`<7NR5YT|IVfr(hp4N_v6AjNRM`XGDI_P_eIwvnEL*l1nUSY-5_;P0oy6 zka`=O#swQJvZ;eR&~jKYq1_!3+lW@kVCprBv+CZh>xhcQ7LYxz$Mx#Ss%lpUUcqv? zNx8HuvTy;NQ4*1F{jcAPH*15u$}+{-g84-20(|k>UX5vqJ=|+;#Ow~U4#w8Ty7`8g zXKQ1v*-znXomi$DKXNu6s>PDd-Z4GXr3C|%=bx=HGGPK&HCB0v;cB>h!pJuOBm>6t z8T+^caXQXdBQas*OUE*y;kS{YMRsT#1MU)$#7-24dGkaG$79+hZT0xhq7=8c zs~XSqWOg=NZXB}3y%CNPkY}$p_aVFBbgo!LWZ+Qg`CY+HF`@?*OQmLguq=s@5U#-X5e?0<%X;q>`lnZsl$d0D5UWF zPh(4rg`O9;6o%AUtD6<9BqC)LnYjsL=B=m!84IRObSJXr-VS)}k~-OBj~G+KyV2e- z+s+HFp?Le%#s@UKQR;5DEUB%-{&QrEVFs^}4z4U$TX@>+4yoo^hRe)6#%1jl13h|4TQ_T#$+0qi`i`PxaoKo^Qz5GJTGl! z6T2GLOXf~8XXJg{Anup)`UV?(ETi5+JRP*V#!dE<2f06N(qx-ymmCpV42+=#E2P3< z5ZyaeEb=8P$+Ej6fUA#~0nL|(U2~z+@W&PK&iew!yLE!D)V369p3Lnn7l@JrH`Vg` zMhsif%25)YSOQ~_55b+wHSn(0GJGaw?vP7KFtrxZ^kDcGp@o*RkubA-OrKCHMy_bF zCB{n~%sX%yjT14uS(2HBD5Duce6dy+t~b6?ggq5WvlQQ9EJAzqD)Q_CbrbXwJ2Sot ztlFp=pT)5e_Nv*PC6RI96+uH=6Sg$myI9&-dV%Um%Clq&0W0XD9^hrVMKEh zioyu-3OwwJ65tuFEX>_4b=YmW48@m>o5Qy-xKL!h?%tfP#4xc~G&Xh#yr!FQT z$UBx@ytxB=vxH=ph(%}13cf0|7)|*6M%>dgzM9*baIN1Rz!tdl@am8~R1n6qMX^rY#<4?bgRnZx*p1)A3*nKlA5VpC8ElhR zhgwhpIDFyp07jE1l)Mr~mouv@vxsV)&v)jyIM5Ewr*MQt5bh4iK!SjFIwn@V!p6zD zb7lAG9X6R6i2djR-9BP3u7xDk7Ba_GjIM7?9lbLq?=TEgI8$U8#xP8H-*%z1xU{-) z{w1cRRlBQJo;UHO0lQ-*rEBgJ$egkPt4VXkfhC-(Ht`hBrqnKuh*Z7UBtIULd6skl z-qkVJCHQEtPVSVj6p|a7l3sZBYG$jEY4H@v%?j7Hx!5VeOkuMJq3mj=X0j%+qG-KD zIY>A&?B?ZBN`u|oYvfcVA}(*;Bm&1!DKlYHU6OJ;#hE@L--lC+^c?9H4(C>A*mtDH zRB=S9gkP`yi%5PWQ^S|EJcTzIk4)O=-9*Ce#n{}ohWY3gb$pl7Hhu0v=B6Zu3(j{1 zE?F%P)_Em_3)t=wS6&PLXJE6MKkenuOU)~Q80%jn-*HZe*ivHC>AeiLY35_n$*2CF z4n|fscEhv1ik5b{ip&u^Nb-E984h7jIIWRrYhYVd7mTTR@DB;R(iBUx-7bL4o&g1Vxm zr7e7oycM;x%#8NU;p@jRfGws4n6Kd_6KL+DbFU|JeYxL4a|3i`*1K~^Gg@LE#%p*( z+>$`j7iV=Y`(N#y4{%*qedo`6($jmAtS7&xIF@ROJUek?gRMky947&nD3+awShlg` z1UDE(vSeG0EE!4uN1=UE(`GZf?h*(s&@vOcw3L~o8#-hcI;50owgWSvakf)tX_qCP zVP^sZ3*BvJnr^e~=X-wlynEl%lbvJ&WW!p1_s{wJJHPY$cYf!0?-j|$oMehHBQh|fo2eaUZ+pfz)G(2v{p?W6`i#(WlJL_BhzsTBX#-{N&t&^E z)dQ8v+S&ybYg;thx~=qlMr#?u{t^(!NeO^?$HtjRNe6JSgBgbG>5y{tgq|sgX=@xC zd{n+8#q$w_6v}x@(#ASFz#`Te@6(-9v|?3}343O>DoxgRZ33H^RY^1G)9MsIz!^DV zPMtwM5UGKjud3t4p%i^&L9UPq4-iifGNZXLn=>SOz6>`}iJFl_smZa#Nr}-!i5^uo zEo$E~#%NTqntx1+kY!ZPm6XJIEUgMkWDG)e zw5r!At)`186`Ero7?FrJ@Sy{haYY4^K*UK2D5AtFDv8pn7$J!#zgnvFQzynpVMRS+ z6Y9>u;bs?vlt~`Wgc~XgtW#&ag4beu2+?$q?HDAvZBRufQS@k>e`5&mMe$Gm4$fCn z?G-!-CY}MX2uxety21W=87$@!@z<4JS)o~MKTniKr43_%#)1*eb5}v8m&y?WHyKvVDa9@(i2;3fEtrQHkAOo`qbS zVB>u5{x}N*EAr|j()JE(Ei4amvVFZ9fjQA*_S5Hi4GMyV@Bpllx%s%R1&s6XToh=} z@Z@qOSf%ocqwxH^vb_I7@3PU8xmv^Q!jxdHr^d#oFiCg?20>m}qHzcP(GzyE9wLK! zc_x0%vGc=39xTtoLQ>T@BLYVw#?Nlu#<_>&ci=HZt%u|higP@9dWUA5R=KiA!3iA- z#BfNyrplf7H2}q&ew3FbFE(Yq`qF1&j}5A(|6i-r8sH-xZVPge1y$E{S_AojmIf?s;0+h&&dfGCt5LW&&6pr zj!LKIQLaghOGl2-2t|ozq6~WsHc>u)vN%o*dsLy75kyj)8@1$}{fX2#lrwswRVHv~ zAugv>NEgyUgU88Q?rE(eVv17-CdW_v$lKW&S<=;17V*=-)ELi#&+|Ajc02DsfbjUif$o+nHs4q*F5d+h_)G^X0NuT7pI^OT!(=b}EC_+9aHYJ#q2B7l z;@a=_L&df4(dHWX&hF@bKNj^j?B2=LPf>f4yXvxffwFp-=Qs~?PVpzis;v4p^e*s1 zYAd6u6>?qn5TziX^27BhNE9Ko&Cr~XIqo|*%3c5sy`+`gAvzUMxB5m;`mWD=O0fx< z1sqL^0MSvX*jiP#1enH40Plzz}K*u~Y7S>M`2~AM)|%s|L6`MmbTLMm<6a^(@X> z;DwCaRBCH&sX^dgkyE>dsV`KEjMQHsTKCRDU`%q;D`{8LE!-ZZP4Tv-+x6I7652zG z;NAmVfy-R`AS2$-dx&ph6n(rjTHjQYlo+IiA+A{DaP;`>I7n>~e2E$&==094x!vcA zLwN}Rb3ceobAaC3b68(R@X_29;d`n&&*XAdTVuilAodE zsK>HWbQNQ@(r#wI+Xp4_hD83{T78jD6nFw`NwNwK#6{b_N?oK_^Bz*YJlk5`^L*Xf zmA=+U|ETS19Nz$rJsrQLx;1gKm9B&3u6KCYSm04gVsMCiLcxGl^r#U_n7H<8>U7st z_wh?rt&DnJRVx*k+r*JUIJ^6U)CpW)qJ}UO%bFlNOpr(g$CJH0%oJ_Am1c^0t9hyq3v2p6$Eb*Qu@2<{qTj!v;p9g>X70Q&79{8 zDsJ7%x$fE_4*~B?x|hG*SQ{g7rl@h)xwnLTalW+m zE=mlNw}<6|5Whg|fQd;LC3CWnI<`YghjCZLbK- zz0A?88=%!p4))#5^D+Lm@iUaY8{jnbO?>TQCm_!Q^7{?o@~7YP{|2B}Z329ofqNND zJX4z)*k;u7TVbASDPyIs^<}PQiDUVE%>j`jktVj1Rqocs%s+~FO0us@h?`LuRtqT^ zhW!{L$y&UQfz;iIi=>#~`HvOSE5^&Z>he-)`(Y_>*sM1(A)(a|M~srb9~cHJ;?-SU zUHeYT4SNx|hpR)>837m7Wh5XIxn0${d#EGR2(h(R#+pW;c9eQMM@Co?(xbF)V%l|t zt5NDkW?7$b)z5ge{2Za?L0S{$-o*%pyx1P~E$*Yv2xZ;YdNuY(wMVF}{wt-g7G%P? zg#0d#9SPc9+zChC7+P0bMl&c}GLDU=Y^1~l4?!Q`cUcQ>sRxr5&w{6hRyJR%Y!&Tn zyi~~w%|x>++y@o8tk3l}f7NBG`ikbixbY&N946Nl^y^3jO+}|8M$Af+ z{xuC7%4pR^APfDT<0o+!TS3bvJYgZ#io0a33L$jt0vm@Q1!HhcvumzQGB(m6`4U|c zIT8cHklA4-#!Rq@VSf>fy4E0W5I{Y$7!Ois6w$Mvzq=yBx{LnY+CV^~^nZYU#U!-Q z6I#&N%p#51#{uuMG(@(@bpRMNF3qk+;93uGC)<(U*4s#e7*i{HvFQz-WnV5T;_l7WsmT+gYRM_1xR$MLc(7>$zl^L0 zcXhRdk&8x4s<`D*9Ih)PIayBEGKp&tz=B0?8se0Ih;2_8rFsdIuqQUw=F}yw@?Td| zS5l720hvUA-SfCM^D~-2jHSzmAU|C2Y#iZxRH|=C33n=WmLW`dguZq%Hi;Idg4J4( zx*(~_5Ua=@kQq#tQTPpB>`A6b`tOHd1CP7)$=v$LeC~~b%z!!&*=~z4i6hxR4p*TT zvAQ#D9Z;wxv8gT)epOf!kAhZNlQkj@n^LNiH~7+~tl40_Nbt+1j0JY3eN>wXO^IzO z-HHgsSrKyNkd@F?k6zVGR?o<5sn`>$x;bo)I7TK|q$ISsPw*q3T86}GT#FNssR>te zEXSfxRH4R=NSu-*!YQ5(Ga$pwMfLKa0VF30Fr5sMUcpX!5B?F@zK5D(j{fey^ z=h(76xzQmAYN2x1%C+1d$+|(r!cpIbWTxBzftIvdAaamY33j3g>rJp z5v>)Mb`qw%!$Zc%Jo7o_jgtxRsuH|4M|Iy)ohMwFyIC#BaW+gJav4q{5>!@R1MfUW z8PB6;&9zy95$a>(jnZ;hm;=vDH@A7EoO?X1Cun)f-LHN&T0gcIqJ16STu(pMyB-b~ zZ?c3zE+_dp%&+P^)z%{2=XtobucQ(3I2t`pz?Idmpv0)jkuC{tphV8qf>ZRN8Plem zaHne?T8pSm+!&N{l9HeU)+gq^la}fC{9R|g8Z`H zB2f0A0#sHzwgcR2x^F{8a9?PVp0J)Ce5^fH`yv-;%>|MD$LV9(G;JtW)^Az&K- zzE)_M01+Da4$eo}vz!10H+m(}M=EG2NN|e)v;$?)7IL87$<&`D9d2%o?_EJT+P^-@ z(Q@Myw?4kM++Mgd`R5AfA%LwY^&yTEQb=o*vMhB+z0{J2E7|+9_OE?;_1yZ)m2-6= zFicz$g1Rjy>$$oxCQC$^?FldnTf$Y$cYB#Pg>aOU?&-yM9JgBlP0hWn%9` z?&^pOEgPK)?2wua?nYPG2OC$VG*|NQy~Nj2zWXUjTg_&U^J&Jc6fY_4#G0d3jy}>c z8_h%yu2k17&9EQ+pty+GnI|LJoP5EE$CpqTW*nwB0@RYTs;7mBN+j!f-%LTRn=4rR zeZW*nsf=N5C{sJ)kSFM@_s+>OXb>i zR4>xZK+z#scGL6i?j4+ENV(>Ud%jujR>9?(H5!fHdBaL0G8#JXRfKCej~j$&jf^C( z%SigV2ClNRUM9IV#hz5+z6#RpvH=ujrX`>&FCLBxC}bq|xUBEoWd@aKGAw8L|sY$DU!;{>H17UJFRzayEKukeg$&{lV+G}g_K#S zgM_Jqa|Dlc=A5skm@Y*%X|UzFH@R{@%fskibuTN7MxH8-q&lXpg!AWB zj^^x2El$x#iJmM7OmrY}Q_zvEa78C_Icdch2Voqo!uGYk4W-p$#tqlUx7Kwsl0(E@ z67v?cVSO2N)xEhx3b9!!pz-C5pl++11Eg`lN;Pgm5QuBI7X~GOv-!5*nC-aDJMJ`R zCqr6$HSf8#^+SH#dIiOp9e2zJw%Ix|5!lwQh(CEEWF{TQJQ(*^7N&I^?veMxt)1{Q zrYOWJfpL{+JQ?yPn^XP;36%welKNBIGI-oNl@z}y0O6!hmGtoau9E}^;Zh-_QyfRV zjxd{X$c-z=nJIdT1IL1UQ!qxMc|B@Dk|EC1d`CfGYvh5DbdWTP>)2Su`8Jkh0EFwR z4pma%Q55jBwH6l|wJ@tOn2gjIHFMLb)?MV)QuHt-V1n-X-zgIA%!9cSbA7&e3|XiBqlTWPdWN&6ri2 z!Lt;XRjNV=HKxGx+U7AHi+vlCvgUwR@SDwv=5B;vN@gPKfBXcOXeLjbCcNTZnsW?^1oWJkG<%Uh^wb?mL{>!*~h+%$jM zkTlGQio;BahU63SbIEtq)XOv&5S`v1;mBrIT2VNgL@S@>s9CUCm28(j3Ulptj6^gZ zxHy(V@?D84gO<{G)RWPYLM&sw8Af%vfVQeOuF@=O3~F~1aU9}plLEevyZY%tkSpk2 zRA8aMXBnA!;Y_mz39yxRLeO39Xxu&fBdo?8r}hG#V+stEUv1IY2AY+h1E+#V5c9Pk zdKGP#z>Mffydwij;m6`~&CDQy^7}a_sX$|nJ$P{E;L0-*wgd{+qxa4e@RFPCPSrE&HT3iQuIYvnzg4!?7 zXW-Q^i$SYhs+~ZA?v^0$V(JW(ByN#(5N&9L;=CBo?&{?=4R3*}FoG|^1M{>g9Uv?x zgF`)@nK9`{3otcHGTIDK}rwwRPQ+fL0*DdvURX)?<3Bl5lTVNj(oCn;<|N@Eg-XpnSDc zHS>pWpl5NT^t)L|7H}+nFiBF&<7VGXM6$^2lrUpzLAnxmSy|UnlqEzbGQ&~JKo(>?FzT;`P!ftd(6jzUh28SwUE09nKS@Y zJNdmGt8xog2?M6*1gq;jo=h8vK^m43<5&{Ki&H_%vAwe&QzT(xA z+*y@?;D!=#Y~XhvPV3uQb=iVd(o^mbxta&VGP*U+cWrVisDyW690~Tratlh@;_|Bu zn>q7s)~PDKiDluUX0Q+l#wkafu;QuU@p`BR4ckOq|DTi(2YS`h-gm#Yx~-4T%no zCJ4bGAl!A;H3ldrwHBVkF#?y^j6ALlu){kq?>kPmS`Tm4@H_p3z*D*Z_`Y}F-F4&h z4>h@TKHt-wZpv4Z8cVLYMu#3sb~lz%-8nAujm{M}=UihhpHJ(eAz$1|O|sHCmoDzq zAB9utN<$f!Dh}ndrF5!rxNx|Mb9eEGisUn;RKB=bm${}j#i?{Yr&1lRBb6(q(;aSg zM~d{5fJT|L4*6WBl+HIa2#WVMQ*ll4G!-g+WnDU5JYPJ|cya*N=v0ky=5qj1zECJX z48+}B<J53pE%8~{_P;^s_=)<4@^N*Bvd=IO7Ae)2sH!skY6rE=ZA+{RofRVaVCSpHAC z?IAIp%6B(570cgb;=W1;;0d=CkQ#obMgp?|*( zeCpQ%Y@|aTd7b<~^4#Tm8a!3>Fa{>Eus8^~jV_hy)*;{E9Qdd4F;JAtX=c(nATKOV zb{7_>Xt-0*H?+DYLmHFGT!;jy-$~t6dy0XiI@=+-#+17AIM?_ax>e4TQC@@1GguE; zvHXl?syi+GFaA0WrZ{m^SbRt~oP2&K#TPG-_plXt*q1-eO>yybx(VjtA0$v*{77N( zV==({{5~uF0XM^H>nnkBZKS`bQt4of{-%t687TdJNxB23IIiJo2laT8! zZWRwKewGm#Fi+a)3m`e2Uo8$#Lw}I%1rOzyvp~|uohW4SYZ@yf?zT#&eUaywv*J#Y zL`pkJ>M8E@HwL2y{tZ8Z#ZNHUbQ%~ydM3xAA=l~}3gz!WaNqH%tJ+9y%C(9Nzh7MZ z`@-TsLXx!bQei2jM)Jj_W=1}hZYh>O8m8fiCUv^hCKBKX^h*UxvQzm2lB7qoh;$N} zGJ`AtySNR6>mc^38|zy^V*ZViQkQWNAk_K`Ji>Bl%M&IWCX;X7AaII&OLEa=_(muW|nIK zA*1#*XPLIP2sq}=W$9xrb!Dc=8N+7nCT_G!*Q2Gf-en=nw1}HeTKDtLLu}i~+C$CT zR%+N;?hbpb+sWI(e&`Oy)yL?z@z)*7*2^sa<{$-c#w?Jbb2p&Hdks5jDD?1JZQKex zZ3ItZY}|tRpzYclxSJqd``P6`R=kcPsIVE+k6c8eNSU!$nk3_BGXMaL4kAqY_ zAhey|4i9r5@Cut_q>s7spS%*7ds#7P^!4x^X6m(!7q|@Ue^eiKJtHG;SBSBid%eK> z(b%B86G~9HZ9Po>6e#!OF=TptUAQ~eY5cB za4$4rZ_n1 zbpqX^wa~@V?Gky-slZ#HC;q zt-kK4;SZ`IUM@3VCz$k?Q>Zgt(pI7PO=cmu&4Ms!RyDX@w&22`Qy+&myuL| zR12GUeF81X7ss&X_jr9N&H0nU<)ve0%Qc!w3p*{+>OuFDkLZ2VrQcNYbIF~T>L-4$ zsm{_8R@=YTg&O5gZA|Hq){kf4+F@Z{Gji_w_Ek@Z7(D=EE=ide_27-txKfC$~I&;~hhdP1oG< z=f9Fa@n_}X*=OJO_CLMu#jl?K%a1*9`oA~5@Rv*9ytjRO+n4hn{!;J%c=o}&t~tBv zz~bM2cj1ng{^p;r{pAh+?T)Yi=(_{IweYDI|K-^So;xs=`*3;F&{Kaky60;@Dt_{* zbKm}fo4k1Cw;FOT-_TSzoNmHJStvgwKjpt_Xe!>^(A1MjH>K0KL`Zr;hwtg|LmmD} zhku}0Kfm4C$dQZ_BvsthmTk%xyNVl&J;i}ypLboA|H)4O;7j!Tg$(CX6oe9eEo zDx1O6XFhJ+len$r)y}sA9}XN{;~kcBn-UivzC$B*QD$!2{bzIoMc%{J=RU*mlB zCGy*{MJ4*~yk{(zU)E{PSHT+{6)0uftz8=1Sb@-k$6&gOT^-q+FJQpm8TFXK0o>V% zcP*9c)C|50uNZUy9p+$0Pzz;UNH=vuSX*+S3t#YJX6a3DWB*e=9^1_h5i!zt+KJSr znC6n9z=oq6(?hV%K zPcr`XxrVzG4u#!4ey;RlOJP>_pKajHv9lXTKy4siCSd%!x)`^)w=!OqEJ^|t{N>|1 zE^QPoJ)ddnDV87cBIcBK8}fzQa_MZgp)*%p>W9^Hh#AQ2ndWRJS6n>O+^mEg36z0n z%FpCW8IHwGx^7;bUzcs~#G}ooj1p9XeGeLY8+Z71kyX#++QLOHT(pFXd?w32Pp%~@ ziFdd;y6nlw<*VwP)wa$8gx!jCVOj~)@)@fO(@j9v*^_U~;%i=A${~;PtM!-GUrQDq z14jPQ1{(^`|CgHd#QS=wI=5QwvcCilJ9F$GfZFFd(dM^F>Q)lh&+EDq9eEofX%i&1cpgo; zv|GgWm1f&CqeI&vj$leQax z-x()r^>L&UVp?h$ILoWxC3vj3v9L6h_AoyUZLkTm z=y%-8Ww1~jNM|{AvGXQuEPg_Ifpip;6j%94nF;A)pGO$F)F)XgiGrl8eit7C{d@Tx z@C=+U9B9$zH7dM#U$X<3&*z!WE3%m=t#}`kL@NKUsU+HtPw~F0?K&%0$Trs0WJlVQ zL#{QOu4FYi*z>+LBSBE8ea*h@3W_w@@_{(M0Hd+5$3rU`RAH|!PwBAW?>Rbb7mxh% zLo~(K+f&8GuX^-6C1Q~l+^fSW9TqhHGUJEUp|DSYgi~IkUEo+)Tth~VAKTYGh4p2!(HZis#h%$8`HSL@iXyotL?O7?`Egg=r*CCMU_2Ymt16u8@c_V$RVE=#%DP ze^ndeZMv55;d+&&e@*;XnD)X8Ya1n_1C%T~EuG$yg*W`)Mr3(jT@&t+W;(E(7C+(* zYPbd5^?8QLu`TH(2Vw*Wu$~B%&;fezK@`}<1ZDW)iwwZRi!$*)=xG(v2VH$o8|=Bf zr?C%;nr)|7nD`eCi%y5eijaF^17>OY32pwOJPSD#>jm)56}M|piQT!};&*>mOtlaK zs27sGaZI#VVs=R(oq+nfnRrwj$JHno!eA6c!d>So#7MXH|D}ROcb(k`63L9EW=t6B zuo%b@7#r_(KubE9I_*&VPma-{gFjsBGw=ogn)IqvDlTfqL^xwFMrV*!my$NR*s11 z&x_&p=h^*>=(%RfshV$}NQ;eV4My=r9Uj2?0#eCrEY0GbD5b*EAx)12t1L3RAkh!g zgf3a#622-TOHSuRHN>4&%jH*5fv1r4^PAr!xdJEgkAkrdd)t^x%zR zddPWb`~@|HdP#oFjKdzXrNjQlz+5_ntW0%#YhaCzU=`p@{0CPyn#VaQ+3O-p&BxBhWV2Kv3L-A(k!$#~n{k-+WLCqgSqH;#)xzh@areJ@f06TMScES+RonqP41ZCS4IF@GN zKoYA2VwtF;dT1!xYok4_9xUXDWD2u>$U0;UrTm>8TXMe3yzi04MUJo@ofwmrR48}X z(8-uzpe=v#y>RhE8>bDiC)3H85Jwc%vVmiC_icao?aq8DTUU&I*0c6>Q)wIoa0{0&uF_%k+LSAXuV3(SiC+6~ zF=6*9cTNAm{@#7NM@l#JUSHCeCrW&RXHI&p4(_SnK%K4aT^boL~qlyYqcPLJ~iq0+$YNxrDdGsJRgI{1xc%Z0+y!>dwG zzE^oZdEPJOa(%tm_uj<8xz#Dx+S_~G$=R9N-czT?oqLn&j-;==XKw7&zS)_|w}nUa z(ZKoC(uSWE%c9n0^~SL-mOFrk{X9&4-e`$!1lTDqPo=jPRr*fW5A<6PfDV}0C%I^&y;^`gsAM}eq*fPA}Kc($I z8nh+ilS=6=rRaM~J4M^uZtA^$ODX!oSXVLm5IBBoAr13UdONB zc5M5x9mo3mCbnNcwrz|p36HO@f)4vPl5+d`m`-VShTpN$!imY!`~sh`8Jn9Z-Oi_* z?Gu0c#Odrz$vzP_zlCB`^S;X2sp;v`_+)8r^5iU^<}1z5^7$P1t10)sl`!l-k`wU$ zV?nKRi+SewLqk(X=Vm3pdhZ?%9rj8HNsrCXbCFpi3jsH%7f-v3K(L*wOPPK01DKjHd?S;bwiixug%E+3f*7 z;#Klr9ubfBScT@yHJ|UoSm-Y~rIEHmA;>eyRO$8b18P2mj}9Jn-0qPd#zwzh=uv zMj!o+XW#bRi+}Q^_x#>p_57Fr^?@IJZ16p)7k>AKyS{YdNXylaKmXuAe|*)Ek2hVs z@(cgF?cd%0Tub_Qc6?>u4dZY9#nP`F|N3v=^0_O@pZU!Cp@ZLgtl>|;^{L0V{Q0fh zuKM6@e{pm6n}7U7`O^zm|MoqSkoEtioWvFpHs$JkGuc@i;unFa=u?e9_&-`L*l%a$%yXdrpOJfX6P*S)r)X7=!Ni_m zd-=DDT;rK!gj_veGw(OO!`XPuW;l>GGCdz&=Rv$VT|PkzjIgz@sRH zwBUEv11EZ2fS%E@HB<;(aD*+`V=#K%qOSDs-PnGq=#^D^fO{tSY%TdGXh#nx*tZ>% z;Y_`xp5W1d@OX=!3RbvTFMaEYBF#a^GMFmQ?UG*|m=T`St$wc~#oTQ!Pdj^QTd(|? z_RyXJPGRY|htM(d_;r(d=2sBd%Mlzk*HB;2_1g2qGrpA=k~m~P2}At)vR*LiUt0kz z2mF}AyjVc39`l(38_D^6xnrAf8!Pw^k7>y^>Sql5!KL2vHsVUrBTVtr%zDA5=31|P z(8FGOg@L{Jpyov~v~{DWjR12kRn(`85s$O|5spQT!hC2$cY@Xtu%YM1F7=kq7dEt2tKwW)+rb_y7O<8OA`#ImKY6O+@_+BjRUs=>HddxJ;}7 From 59ac792e44fef4de1da9fdf5c3be3ed164cae6cf Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 27 Apr 2010 17:11:24 -0700 Subject: [PATCH 025/260] * Thanks cmickeyb, for a patch that corrects an invalid construction of Primitive.TextureEntry (a "blank" texture should be initialized with UUID.Zero, not null) --- OpenSim/Framework/PrimitiveBaseShape.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/PrimitiveBaseShape.cs b/OpenSim/Framework/PrimitiveBaseShape.cs index 1208b97300..4d1de22eeb 100644 --- a/OpenSim/Framework/PrimitiveBaseShape.cs +++ b/OpenSim/Framework/PrimitiveBaseShape.cs @@ -236,7 +236,7 @@ namespace OpenSim.Framework catch { } m_log.Warn("[SHAPE]: Failed to decode texture, length=" + ((m_textureEntry != null) ? m_textureEntry.Length : 0)); - return new Primitive.TextureEntry(null); + return new Primitive.TextureEntry(UUID.Zero); } set { m_textureEntry = value.GetBytes(); } From e83877692046068735af97eb9f40cea1a09ae66c Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 27 Apr 2010 17:13:54 -0700 Subject: [PATCH 026/260] * Thanks cmickeyb, for a patch that sanity checks if the response from m_Database.GetAsset(assetID) is null in AssetService.cs --- OpenSim/Services/AssetService/AssetService.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index ed87f3f6fc..4e512e729c 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -106,7 +106,10 @@ namespace OpenSim.Services.AssetService return null; AssetBase asset = m_Database.GetAsset(assetID); - return asset.Metadata; + if (asset != null) + return asset.Metadata; + + return null; } public byte[] GetData(string id) From 044c1cf5f6e38799526be15cc2c7f4a7d0d9350f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 27 Apr 2010 18:37:34 -0700 Subject: [PATCH 027/260] Changed GetToken from protected to public. This is not exposed at the interface. --- .../Services/AuthenticationService/AuthenticationServiceBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs index 9af61a922a..242aba3670 100644 --- a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs +++ b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs @@ -127,7 +127,7 @@ namespace OpenSim.Services.AuthenticationService return true; } - protected string GetToken(UUID principalID, int lifetime) + public string GetToken(UUID principalID, int lifetime) { UUID token = UUID.Random(); From c54bc5094deedecbde2ca2283efefe6df70cc591 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 27 Apr 2010 18:52:15 -0700 Subject: [PATCH 028/260] Putting it back to protected; Melanie doesn't like it to be public. --- .../Services/AuthenticationService/AuthenticationServiceBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs index 242aba3670..9af61a922a 100644 --- a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs +++ b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs @@ -127,7 +127,7 @@ namespace OpenSim.Services.AuthenticationService return true; } - public string GetToken(UUID principalID, int lifetime) + protected string GetToken(UUID principalID, int lifetime) { UUID token = UUID.Random(); From 0718748ea4dba1f0de409c0e099a803d8765ae63 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 27 Apr 2010 13:44:19 +0300 Subject: [PATCH 029/260] Removed from git .../obj directories (created by VStudio) --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index b6b5582b0d..4eca1cff57 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,13 @@ *.pdb *.pidb *.dll.build +*.dll +*/*/obj +*/*/*/obj +*/*/*/*/obj +*/*/*/*/*/obj +*/*/*/*/*/*/obj +*/*/*/*/*/*/*/obj */*/bin */*/*/bin */*/*/*/bin @@ -45,6 +52,7 @@ bin/OpenSim.Grid.InventoryServer.log bin/OpenSim.Grid.MessagingServer.log bin/OpenSim.Grid.UserServer.log bin/OpenSim.log +bin/*.manifest bin/crashes/ Examples/*.dll OpenSim.build From b9c6ee6eb262d74aa42a2b4a76fecf867ed5c414 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 27 Apr 2010 10:52:22 +0300 Subject: [PATCH 030/260] removed from git *.VisualState.xml (automatically created by NUnit) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4eca1cff57..8d3f2b266f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.pidb *.dll.build *.dll +*.VisualState.xml */*/obj */*/*/obj */*/*/*/obj From 423ff3b94bbbace7092f110b51d8723f7fc25713 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 27 Apr 2010 10:53:54 +0300 Subject: [PATCH 031/260] Removed unused var from InventoryService.cs (-1 warning) --- OpenSim/Services/InventoryService/InventoryService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/InventoryService/InventoryService.cs b/OpenSim/Services/InventoryService/InventoryService.cs index 0d6577e776..fbcd6634e7 100644 --- a/OpenSim/Services/InventoryService/InventoryService.cs +++ b/OpenSim/Services/InventoryService/InventoryService.cs @@ -109,7 +109,7 @@ namespace OpenSim.Services.InventoryService { existingRootFolder = GetRootFolder(user); } - catch (Exception e) + catch /*(Exception e)*/ { // Munch the exception, it has already been reported // From fb7458be3185802c4511306c0cda7648d702de59 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 25 Apr 2010 10:13:13 +0300 Subject: [PATCH 032/260] Minor spelling corrections in MiniModule: "RetreiveAsset" changed to "RetrieveAsset" and 'm_rootSceene' to m_rootScene'. --- .../Scripting/Minimodule/Interfaces/IInventoryItem.cs | 2 +- .../OptionalModules/Scripting/Minimodule/InventoryItem.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IInventoryItem.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IInventoryItem.cs index 5fac189d09..16cd7e454e 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IInventoryItem.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IInventoryItem.cs @@ -39,6 +39,6 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule { int Type { get; } UUID AssetID { get; } - T RetreiveAsset() where T : Asset, new(); + T RetrieveAsset() where T : Asset, new(); } } diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/InventoryItem.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/InventoryItem.cs index 5bf29d710c..bf85cbcff5 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/InventoryItem.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/InventoryItem.cs @@ -39,11 +39,11 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule public class InventoryItem : IInventoryItem { TaskInventoryItem m_privateItem; - Scene m_rootSceene; + Scene m_rootScene; public InventoryItem(Scene rootScene, TaskInventoryItem internalItem) { - m_rootSceene = rootScene; + m_rootScene = rootScene; m_privateItem = internalItem; } @@ -82,9 +82,9 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule public UUID AssetID { get { return m_privateItem.AssetID; } } // This method exposes OpenSim/OpenMetaverse internals and needs to be replaced with a IAsset specific to MRM. - public T RetreiveAsset() where T : OpenMetaverse.Assets.Asset, new() + public T RetrieveAsset() where T : OpenMetaverse.Assets.Asset, new() { - AssetBase a = m_rootSceene.AssetService.Get(AssetID.ToString()); + AssetBase a = m_rootScene.AssetService.Get(AssetID.ToString()); T result = new T(); if ((sbyte)result.AssetType != a.Type) From 806a2555f557e7a05002f67731b1e3105c9aab8b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 25 Apr 2010 17:21:05 +0300 Subject: [PATCH 033/260] Spelling in FriendsModule.cs: "filed to load" -> "failed to load" --- OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 312db38ed0..23d5b3c2ec 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -149,7 +149,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends if (m_FriendsService == null) { - m_log.Error("[FRIENDS]: No Connector defined in section Friends, or filed to load, cannot continue"); + m_log.Error("[FRIENDS]: No Connector defined in section Friends, or failed to load, cannot continue"); throw new Exception("Connector load error"); } From 8b75302a1e33f1f5cee166dd22d1de44172da509 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 27 Apr 2010 10:05:17 +0300 Subject: [PATCH 034/260] Just a bit of spellchecking in the comments --- OpenSim/Data/MySQL/MySQLInventoryData.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLInventoryData.cs b/OpenSim/Data/MySQL/MySQLInventoryData.cs index 192deb298a..e0e9b9cc99 100644 --- a/OpenSim/Data/MySQL/MySQLInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLInventoryData.cs @@ -145,7 +145,7 @@ namespace OpenSim.Data.MySQL ///

/// Returns a list of the root folders within a users inventory /// - /// The user whos inventory is to be searched + /// The user whose inventory is to be searched /// A list of folder objects public List getUserRootFolders(UUID user) { @@ -284,7 +284,7 @@ namespace OpenSim.Data.MySQL { InventoryItemBase item = new InventoryItemBase(); - // TODO: this is to handle a case where NULLs creep in there, which we are not sure is indemic to the system, or legacy. It would be nice to live fix these. + // TODO: this is to handle a case where NULLs creep in there, which we are not sure is endemic to the system, or legacy. It would be nice to live fix these. if (reader["creatorID"] == null) { item.CreatorId = UUID.Zero.ToString(); From aa56953411e04c260fe1938f5f184c775fea72b6 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 24 Apr 2010 18:52:23 +0300 Subject: [PATCH 035/260] Compiler.cs contained method GetCompilerOutput which, apparently, was not used, but exactly the same path was calculated inline. This patch does some minor refactoring by replacing inline path calculation with GetCompilerOutput. This doesn't actually affect anything, just minor prettifying of the code --- .../Region/ScriptEngine/Shared/CodeTools/Compiler.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs index d8c0ba57a5..f7196835bb 100644 --- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs +++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs @@ -261,13 +261,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools // } //} - public object GetCompilerOutput(UUID assetID) + public string GetCompilerOutput(string assetID) { return Path.Combine(ScriptEnginesPath, Path.Combine( m_scriptEngine.World.RegionInfo.RegionID.ToString(), FilePrefix + "_compiled_" + assetID + ".dll")); } + public string GetCompilerOutput(UUID assetID) + { + return GetCompilerOutput(assetID.ToString()); + } + /// /// Converts script from LSL to CS and calls CompileFromCSText /// @@ -279,9 +284,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools linemap = null; m_warnings.Clear(); - assembly = Path.Combine(ScriptEnginesPath, Path.Combine( - m_scriptEngine.World.RegionInfo.RegionID.ToString(), - FilePrefix + "_compiled_" + asset + ".dll")); + assembly = GetCompilerOutput(asset); if (!Directory.Exists(ScriptEnginesPath)) { From b49e9eff562076786f68088155c6a0c8b1192e4a Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 28 Apr 2010 13:11:42 +0300 Subject: [PATCH 036/260] Fixed comments in Migration.cs: wrong argument order (no change to code) --- OpenSim/Data/Migration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 0fb9e783bd..5ca8e4450a 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -53,8 +53,8 @@ namespace OpenSim.Data /// When a database driver starts up, it specifies a resource that /// needs to be brought up to the current revision. For instance: /// - /// Migration um = new Migration(Assembly, DbConnection, "Users"); - /// um.Upgrade(); + /// Migration um = new Migration(DbConnection, Assembly, "Users"); + /// um.Update(); /// /// This works out which version Users is at, and applies all the /// revisions past it to it. If there is no users table, all From be1141f0f7994cb068683e07fe99936025361001 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 28 Apr 2010 13:40:35 +0300 Subject: [PATCH 037/260] Refactoring in Migration.cs: "using()" instead of explicit Dispose() This ensures that 'cmd' gets disposed on errors --- OpenSim/Data/Migration.cs | 108 ++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 5ca8e4450a..06defe4e36 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -110,10 +110,11 @@ namespace OpenSim.Data return; // If not, create the migration tables - DbCommand cmd = _conn.CreateCommand(); - cmd.CommandText = _migrations_create; - cmd.ExecuteNonQuery(); - cmd.Dispose(); + using (DbCommand cmd = _conn.CreateCommand()) + { + cmd.CommandText = _migrations_create; + cmd.ExecuteNonQuery(); + } InsertVersion("migrations", 1); } @@ -131,37 +132,37 @@ namespace OpenSim.Data m_log.InfoFormat("[MIGRATIONS] Upgrading {0} to latest revision {1}.", _type, migrations.Keys[migrations.Count - 1]); m_log.Info("[MIGRATIONS] NOTE: this may take a while, don't interupt this process!"); - DbCommand cmd = _conn.CreateCommand(); - foreach (KeyValuePair kvp in migrations) + using (DbCommand cmd = _conn.CreateCommand()) { - int newversion = kvp.Key; - cmd.CommandText = kvp.Value; - // we need to up the command timeout to infinite as we might be doing long migrations. - cmd.CommandTimeout = 0; - try + foreach (KeyValuePair kvp in migrations) { - cmd.ExecuteNonQuery(); - } - catch (Exception e) - { - m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", cmd.CommandText); - m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); - cmd.CommandText = "ROLLBACK;"; - cmd.ExecuteNonQuery(); - } + int newversion = kvp.Key; + cmd.CommandText = kvp.Value; + // we need to up the command timeout to infinite as we might be doing long migrations. + cmd.CommandTimeout = 0; + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", cmd.CommandText); + m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); + cmd.CommandText = "ROLLBACK;"; + cmd.ExecuteNonQuery(); + } - if (version == 0) - { - InsertVersion(_type, newversion); + if (version == 0) + { + InsertVersion(_type, newversion); + } + else + { + UpdateVersion(_type, newversion); + } + version = newversion; } - else - { - UpdateVersion(_type, newversion); - } - version = newversion; } - - cmd.Dispose(); } // private int MaxVersion() @@ -200,43 +201,46 @@ namespace OpenSim.Data protected virtual int FindVersion(DbConnection conn, string type) { int version = 0; - DbCommand cmd = conn.CreateCommand(); - try + using (DbCommand cmd = conn.CreateCommand()) { - cmd.CommandText = "select version from migrations where name='" + type +"' order by version desc"; - using (IDataReader reader = cmd.ExecuteReader()) + try { - if (reader.Read()) + cmd.CommandText = "select version from migrations where name='" + type + "' order by version desc"; + using (IDataReader reader = cmd.ExecuteReader()) { - version = Convert.ToInt32(reader["version"]); + if (reader.Read()) + { + version = Convert.ToInt32(reader["version"]); + } + reader.Close(); } - reader.Close(); + } + catch + { + // Something went wrong, so we're version 0 } } - catch - { - // Something went wrong, so we're version 0 - } - cmd.Dispose(); return version; } private void InsertVersion(string type, int version) { - DbCommand cmd = _conn.CreateCommand(); - cmd.CommandText = "insert into migrations(name, version) values('" + type + "', " + version + ")"; - m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); - cmd.ExecuteNonQuery(); - cmd.Dispose(); + using (DbCommand cmd = _conn.CreateCommand()) + { + cmd.CommandText = "insert into migrations(name, version) values('" + type + "', " + version + ")"; + m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); + cmd.ExecuteNonQuery(); + } } private void UpdateVersion(string type, int version) { - DbCommand cmd = _conn.CreateCommand(); - cmd.CommandText = "update migrations set version=" + version + " where name='" + type + "'"; - m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); - cmd.ExecuteNonQuery(); - cmd.Dispose(); + using (DbCommand cmd = _conn.CreateCommand()) + { + cmd.CommandText = "update migrations set version=" + version + " where name='" + type + "'"; + m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); + cmd.ExecuteNonQuery(); + } } // private SortedList GetAllMigrations() From 2c595227b030221a0fa2ca2765b3bd3f053deac7 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 28 Apr 2010 07:41:16 -0700 Subject: [PATCH 038/260] Commented verbose debug messages from XInventory handler. --- .../Inventory/XInventoryInConnector.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs index c95b4d44e3..16b05df9e2 100644 --- a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs +++ b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs @@ -91,7 +91,7 @@ namespace OpenSim.Server.Handlers.Asset sr.Close(); body = body.Trim(); - m_log.DebugFormat("[XXX]: query String: {0}", body); + //m_log.DebugFormat("[XXX]: query String: {0}", body); try { @@ -213,7 +213,7 @@ namespace OpenSim.Server.Handlers.Asset result["RESULT"] = "False"; string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -241,7 +241,7 @@ namespace OpenSim.Server.Handlers.Asset result["FOLDERS"] = sfolders; string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -257,7 +257,7 @@ namespace OpenSim.Server.Handlers.Asset result["folder"] = EncodeFolder(rfolder); string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -274,7 +274,7 @@ namespace OpenSim.Server.Handlers.Asset result["folder"] = EncodeFolder(folder); string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -310,7 +310,7 @@ namespace OpenSim.Server.Handlers.Asset } string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -338,7 +338,7 @@ namespace OpenSim.Server.Handlers.Asset result["ITEMS"] = sitems; string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -511,7 +511,7 @@ namespace OpenSim.Server.Handlers.Asset result["item"] = EncodeItem(item); string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -528,7 +528,7 @@ namespace OpenSim.Server.Handlers.Asset result["folder"] = EncodeFolder(folder); string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -553,7 +553,7 @@ namespace OpenSim.Server.Handlers.Asset result["ITEMS"] = items; string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -570,7 +570,7 @@ namespace OpenSim.Server.Handlers.Asset result["RESULT"] = perms.ToString(); string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } @@ -582,7 +582,7 @@ namespace OpenSim.Server.Handlers.Asset UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); Dictionary sfolders = GetSystemFolders(principal); - m_log.DebugFormat("[XXX]: SystemFolders got {0} folders", sfolders.Count); + //m_log.DebugFormat("[XXX]: SystemFolders got {0} folders", sfolders.Count); Dictionary folders = new Dictionary(); int i = 0; @@ -594,7 +594,7 @@ namespace OpenSim.Server.Handlers.Asset result["FOLDERS"] = folders; string xmlString = ServerUtils.BuildXmlResponse(result); - m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); UTF8Encoding encoding = new UTF8Encoding(); return encoding.GetBytes(xmlString); } From 6a4fae123ade98d53e7342309dfb52e254d4b5f2 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 29 Apr 2010 11:39:13 -0700 Subject: [PATCH 039/260] Started redoing HGInventoryBroker for XInventory and with multi-protocol in mind. Unfinished. --- .../Inventory/HGInventoryBroker2.cs | 589 ++++++++++++++++++ .../Inventory/InventoryCache.cs | 51 ++ .../RemoteXInventoryServiceConnector.cs | 5 + .../InventoryService/XInventoryService.cs | 6 +- 4 files changed, 648 insertions(+), 3 deletions(-) create mode 100644 OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs new file mode 100644 index 0000000000..22aac02c75 --- /dev/null +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs @@ -0,0 +1,589 @@ +/* + * 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 OpenSimulator 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 log4net; +using Nini.Config; +using System; +using System.Collections.Generic; +using System.Reflection; +using OpenSim.Framework; + +using OpenSim.Server.Base; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors; +using OpenMetaverse; + +namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory +{ + public class HGInventoryBroker2 : BaseInventoryConnector, INonSharedRegionModule, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private static bool m_Initialized = false; + private static bool m_Enabled = false; + + private static IInventoryService m_LocalGridInventoryService; + private static ISessionAuthInventoryService m_HGService; // obsolete + private Dictionary m_connectors = new Dictionary(); + + + private Scene m_Scene; + private IUserAccountService m_UserAccountService; + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "HGInventoryBroker2"; } + } + + public void Initialise(IConfigSource source) + { + if (!m_Initialized) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("InventoryServices", ""); + if (name == Name) + { + IConfig inventoryConfig = source.Configs["InventoryService"]; + if (inventoryConfig == null) + { + m_log.Error("[HG INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); + return; + } + + string localDll = inventoryConfig.GetString("LocalGridInventoryService", + String.Empty); + string HGDll = inventoryConfig.GetString("HypergridInventoryService", + String.Empty); + + if (localDll == String.Empty) + { + m_log.Error("[HG INVENTORY CONNECTOR]: No LocalGridInventoryService named in section InventoryService"); + //return; + throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); + } + + if (HGDll == String.Empty) + { + m_log.Error("[HG INVENTORY CONNECTOR]: No HypergridInventoryService named in section InventoryService"); + //return; + throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); + } + + Object[] args = new Object[] { source }; + m_LocalGridInventoryService = + ServerUtils.LoadPlugin(localDll, + args); + + m_HGService = + ServerUtils.LoadPlugin(HGDll, + args); + + if (m_LocalGridInventoryService == null) + { + m_log.Error("[HG INVENTORY CONNECTOR]: Can't load local inventory service"); + return; + } + if (m_HGService == null) + { + m_log.Error("[HG INVENTORY CONNECTOR]: Can't load hypergrid inventory service"); + return; + } + + Init(source); + + m_Enabled = true; + m_log.Info("[HG INVENTORY CONNECTOR]: HG inventory broker enabled"); + } + } + m_Initialized = true; + } + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_Scene = scene; + m_UserAccountService = m_Scene.UserAccountService; + + scene.RegisterModuleInterface(this); + m_cache.AddRegion(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_cache.RemoveRegion(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + m_log.InfoFormat("[HG INVENTORY CONNECTOR]: Enabled HG inventory for region {0}", scene.RegionInfo.RegionName); + + } + + + #region IInventoryService + + public override bool CreateUserInventory(UUID userID) + { + return m_LocalGridInventoryService.CreateUserInventory(userID); + } + + public override List GetInventorySkeleton(UUID userId) + { + return m_LocalGridInventoryService.GetInventorySkeleton(userId); + } + + public override InventoryCollection GetUserInventory(UUID userID) + { + return null; + } + + public override void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + { + } + + // Inherited. See base + //public override InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + //{ + // if (IsLocalGridUser(userID)) + // return m_GridService.GetFolderForType(userID, type); + // else + // { + // UUID sessionID = GetSessionID(userID); + // string uri = GetUserInventoryURI(userID) + "/" + userID.ToString(); + // // !!!!!! + // return null; + // //return m_HGService.GetFolderForType(uri, sessionID, type); + // } + //} + + public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) + { + m_log.Debug("[HGInventory]: GetFolderContent " + folderID); + string url = string.Empty; + + string invURL = m_cache.GetInventoryServiceURL(userID); + + if (invURL == null) // not there, forward to local inventory connector to resolve + return m_LocalGridInventoryService.GetFolderContent(userID, folderID); + + IInventoryService connector = GetConnector(url); + return connector.GetFolderContent(userID, folderID); + + //if (StringToUrlAndUserID(id, out url, out userID)) + //{ + // ISessionAuthInventoryService connector = GetConnector(url); + // return connector.GetFolderContent(userID, folderID, sessionID); + //} + + //return null; + + ////////// + + //string uri = string.Empty; + //if (!IsForeignUser(userID, out uri)) + // return m_GridService.GetFolderContent(userID, folderID); + //else + //{ + // UUID sessionID = GetSessionID(userID); + // uri = uri + "/" + userID.ToString(); + // return m_HGService.GetFolderContent(uri, folderID, sessionID); + //} + } + + public override Dictionary GetSystemFolders(UUID userID) + { + string uri = string.Empty; + if (!IsForeignUser(userID, out uri)) + { + // This is not pretty, but it will have to do for now + if (m_LocalGridInventoryService is BaseInventoryConnector) + { + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetSystemsFolders redirected to RemoteInventoryServiceConnector module"); + return ((BaseInventoryConnector)m_LocalGridInventoryService).GetSystemFolders(userID); + } + else + { + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetSystemsFolders redirected to GetSystemFoldersLocal"); + return GetSystemFoldersLocal(userID); + } + } + else + { + UUID sessionID = GetSessionID(userID); + uri = uri + "/" + userID.ToString(); + return m_HGService.GetSystemFolders(uri, sessionID); + } + } + + private Dictionary GetSystemFoldersLocal(UUID userID) + { + InventoryFolderBase root = m_LocalGridInventoryService.GetRootFolder(userID); + if (root != null) + { + InventoryCollection content = m_LocalGridInventoryService.GetFolderContent(userID, root.ID); + if (content != null) + { + Dictionary folders = new Dictionary(); + foreach (InventoryFolderBase folder in content.Folders) + { + //m_log.DebugFormat("[HG INVENTORY CONNECTOR]: scanning folder type {0}", (AssetType)folder.Type); + if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) + folders[(AssetType)folder.Type] = folder; + } + // Put the root folder there, as type Folder + folders[AssetType.Folder] = root; + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: System folders count for {0}: {1}", userID, folders.Count); + return folders; + } + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Root folder content not found for {0}", userID); + + } + + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Root folder not found for {0}", userID); + + return new Dictionary(); + } + + public override List GetFolderItems(UUID userID, UUID folderID) + { + string uri = string.Empty; + if (!IsForeignUser(userID, out uri)) + return m_LocalGridInventoryService.GetFolderItems(userID, folderID); + else + { + UUID sessionID = GetSessionID(userID); + uri = uri + "/" + userID.ToString(); + return m_HGService.GetFolderItems(uri, folderID, sessionID); + } + } + + public override bool AddFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + string uri = string.Empty; + if (!IsForeignUser(folder.Owner, out uri)) + return m_LocalGridInventoryService.AddFolder(folder); + else + { + UUID sessionID = GetSessionID(folder.Owner); + uri = uri + "/" + folder.Owner.ToString(); + return m_HGService.AddFolder(uri, folder, sessionID); + } + } + + public override bool UpdateFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + string uri = string.Empty; + if (!IsForeignUser(folder.Owner, out uri)) + return m_LocalGridInventoryService.UpdateFolder(folder); + else + { + UUID sessionID = GetSessionID(folder.Owner); + uri = uri + "/" + folder.Owner.ToString(); + return m_HGService.UpdateFolder(uri, folder, sessionID); + } + } + + public override bool DeleteFolders(UUID ownerID, List folderIDs) + { + if (folderIDs == null) + return false; + if (folderIDs.Count == 0) + return false; + + string uri = string.Empty; + if (!IsForeignUser(ownerID, out uri)) + return m_LocalGridInventoryService.DeleteFolders(ownerID, folderIDs); + else + { + UUID sessionID = GetSessionID(ownerID); + uri = uri + "/" + ownerID.ToString(); + return m_HGService.DeleteFolders(uri, folderIDs, sessionID); + } + } + + public override bool MoveFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + string uri = string.Empty; + if (!IsForeignUser(folder.Owner, out uri)) + return m_LocalGridInventoryService.MoveFolder(folder); + else + { + UUID sessionID = GetSessionID(folder.Owner); + uri = uri + "/" + folder.Owner.ToString(); + return m_HGService.MoveFolder(uri, folder, sessionID); + } + } + + public override bool PurgeFolder(InventoryFolderBase folder) + { + if (folder == null) + return false; + + string uri = string.Empty; + if (!IsForeignUser(folder.Owner, out uri)) + return m_LocalGridInventoryService.PurgeFolder(folder); + else + { + UUID sessionID = GetSessionID(folder.Owner); + uri = uri + "/" + folder.Owner.ToString(); + return m_HGService.PurgeFolder(uri, folder, sessionID); + } + } + + // public bool AddItem(InventoryItemBase item) inherited + // Uses AddItemPlain + + protected override bool AddItemPlain(InventoryItemBase item) + { + if (item == null) + return false; + + string uri = string.Empty; + if (!IsForeignUser(item.Owner, out uri)) + { + return m_LocalGridInventoryService.AddItem(item); + } + else + { + UUID sessionID = GetSessionID(item.Owner); + uri = uri + "/" + item.Owner.ToString(); + return m_HGService.AddItem(uri, item, sessionID); + } + } + + public override bool UpdateItem(InventoryItemBase item) + { + if (item == null) + return false; + + string uri = string.Empty; + if (!IsForeignUser(item.Owner, out uri)) + return m_LocalGridInventoryService.UpdateItem(item); + else + { + UUID sessionID = GetSessionID(item.Owner); + uri = uri + "/" + item.Owner.ToString(); + return m_HGService.UpdateItem(uri, item, sessionID); + } + } + + public override bool MoveItems(UUID ownerID, List items) + { + if (items == null) + return false; + if (items.Count == 0) + return true; + + string uri = string.Empty; + if (!IsForeignUser(ownerID, out uri)) + return m_LocalGridInventoryService.MoveItems(ownerID, items); + else + { + UUID sessionID = GetSessionID(ownerID); + uri = uri + "/" + ownerID.ToString(); + return m_HGService.MoveItems(uri, items, sessionID); + } + } + + public override bool DeleteItems(UUID ownerID, List itemIDs) + { + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Delete {0} items for user {1}", itemIDs.Count, ownerID); + + if (itemIDs == null) + return false; + if (itemIDs.Count == 0) + return true; + + string uri = string.Empty; + if (!IsForeignUser(ownerID, out uri)) + return m_LocalGridInventoryService.DeleteItems(ownerID, itemIDs); + else + { + UUID sessionID = GetSessionID(ownerID); + uri = uri + "/" + ownerID.ToString(); + return m_HGService.DeleteItems(uri, itemIDs, sessionID); + } + } + + public override InventoryItemBase GetItem(InventoryItemBase item) + { + if (item == null) + return null; + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetItem {0} for user {1}", item.ID, item.Owner); + string uri = string.Empty; + if (!IsForeignUser(item.Owner, out uri)) + return m_LocalGridInventoryService.GetItem(item); + else + { + UUID sessionID = GetSessionID(item.Owner); + uri = uri + "/" + item.Owner.ToString(); + return m_HGService.QueryItem(uri, item, sessionID); + } + } + + public override InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + if (folder == null) + return null; + + string uri = string.Empty; + if (!IsForeignUser(folder.Owner, out uri)) + return m_LocalGridInventoryService.GetFolder(folder); + else + { + UUID sessionID = GetSessionID(folder.Owner); + uri = uri + "/" + folder.Owner.ToString(); + return m_HGService.QueryFolder(uri, folder, sessionID); + } + } + + public override bool HasInventoryForUser(UUID userID) + { + return false; + } + + public override List GetActiveGestures(UUID userId) + { + return new List(); + } + + public override int GetAssetPermissions(UUID userID, UUID assetID) + { + string uri = string.Empty; + if (!IsForeignUser(userID, out uri)) + return m_LocalGridInventoryService.GetAssetPermissions(userID, assetID); + else + { + UUID sessionID = GetSessionID(userID); + uri = uri + "/" + userID.ToString(); + return m_HGService.GetAssetPermissions(uri, assetID, sessionID); + } + } + + #endregion + + private IInventoryService GetConnector(string url) + { + IInventoryService connector = null; + lock (m_connectors) + { + if (m_connectors.ContainsKey(url)) + { + connector = m_connectors[url]; + } + else + { + // We're instantiating this class explicitly, but this won't + // work in general, because the remote grid may be running + // an inventory server that has a different protocol. + // Eventually we will want a piece of protocol asking + // the remote server about its kind. Definitely cool thing to do! + connector = new RemoteXInventoryServicesConnector(url); + m_connectors.Add(url, connector); + } + } + return connector; + } + + + private UUID GetSessionID(UUID userID) + { + ScenePresence sp = null; + if (m_Scene.TryGetScenePresence(userID, out sp)) + { + return sp.ControllingClient.SessionId; + } + + m_log.DebugFormat("[HG INVENTORY CONNECTOR]: scene presence for {0} not found", userID); + return UUID.Zero; + } + + private bool IsForeignUser(UUID userID, out string inventoryURL) + { + inventoryURL = string.Empty; + UserAccount account = null; + if (m_Scene.UserAccountService != null) + account = m_Scene.UserAccountService.GetUserAccount(m_Scene.RegionInfo.ScopeID, userID); + + if (account == null) // foreign user + { + ScenePresence sp = null; + m_Scene.TryGetScenePresence(userID, out sp); + if (sp != null) + { + AgentCircuitData aCircuit = m_Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); + if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) + { + inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); + inventoryURL = inventoryURL.Trim(new char[] { '/' }); + return true; + } + } + } + return false; + } + + + } +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs index 5e06580cce..9c6e1cdd3c 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/InventoryCache.cs @@ -51,6 +51,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory // The cache proper protected Dictionary> m_InventoryCache; + // A cache of userIDs --> ServiceURLs, for HGBroker only + protected Dictionary m_InventoryURLs; + public virtual void Init(IConfigSource source, BaseInventoryConnector connector) { m_Scenes = new List(); @@ -89,8 +92,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory // If not, go get them and place them in the cache Dictionary folders = CacheSystemFolders(presence.UUID); + CacheInventoryServiceURL(presence.Scene, presence.UUID); + m_log.DebugFormat("[INVENTORY CACHE]: OnMakeRootAgent in {0}, fetched system folders for {1} {2}: count {3}", presence.Scene.RegionInfo.RegionName, presence.Firstname, presence.Lastname, folders.Count); + } void OnClientClosed(UUID clientID, Scene scene) @@ -113,6 +119,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory "[INVENTORY CACHE]: OnClientClosed in {0}, user {1} out of sim. Dropping system folders", scene.RegionInfo.RegionName, clientID); DropCachedSystemFolders(clientID); + DropInventoryServiceURL(clientID); } } @@ -174,5 +181,49 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return null; } + + /// + /// Gets the user's inventory URL from its serviceURLs, if the user is foreign, + /// and sticks it in the cache + /// + /// + private void CacheInventoryServiceURL(Scene scene, UUID userID) + { + if (scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, userID) == null) + { + // The user does not have a local account; let's cache its service URL + string inventoryURL = string.Empty; + ScenePresence sp = null; + scene.TryGetScenePresence(userID, out sp); + if (sp != null) + { + AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); + if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) + { + inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); + if (inventoryURL != null && inventoryURL != string.Empty) + { + inventoryURL = inventoryURL.Trim(new char[] { '/' }); + m_InventoryURLs.Add(userID, inventoryURL); + } + } + } + } + } + + private void DropInventoryServiceURL(UUID userID) + { + lock (m_InventoryURLs) + if (m_InventoryURLs.ContainsKey(userID)) + m_InventoryURLs.Remove(userID); + } + + public string GetInventoryServiceURL(UUID userID) + { + if (m_InventoryURLs.ContainsKey(userID)) + return m_InventoryURLs[userID]; + + return null; + } } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs index 8504b677c2..96d0c1c2fd 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs @@ -65,6 +65,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { } + public RemoteXInventoryServicesConnector(string url) + { + m_RemoteConnector = new XInventoryServicesConnector(url); + } + public RemoteXInventoryServicesConnector(IConfigSource source) { Init(source); diff --git a/OpenSim/Services/InventoryService/XInventoryService.cs b/OpenSim/Services/InventoryService/XInventoryService.cs index 1409a0192d..4d7103bf73 100644 --- a/OpenSim/Services/InventoryService/XInventoryService.cs +++ b/OpenSim/Services/InventoryService/XInventoryService.cs @@ -184,7 +184,7 @@ namespace OpenSim.Services.InventoryService foreach (XInventoryFolder x in allFolders) { - m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to skeleton", x.folderName); + //m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to skeleton", x.folderName); folders.Add(ConvertToOpenSim(x)); } @@ -233,7 +233,7 @@ namespace OpenSim.Services.InventoryService foreach (XInventoryFolder x in folders) { - m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to response", x.folderName); + //m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to response", x.folderName); inventory.Folders.Add(ConvertToOpenSim(x)); } @@ -243,7 +243,7 @@ namespace OpenSim.Services.InventoryService foreach (XInventoryItem i in items) { - m_log.DebugFormat("[XINVENTORY]: Adding item {0} to response", i.inventoryName); + //m_log.DebugFormat("[XINVENTORY]: Adding item {0} to response", i.inventoryName); inventory.Items.Add(ConvertToOpenSim(i)); } From 638dc8d3c25f68e6782d4cdbb202ad2c433bd676 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 29 Apr 2010 20:09:11 -0700 Subject: [PATCH 040/260] Simplified RemoteXInventoryServiceConnector and HGBroker (2) by a lot. --- .../Inventory/HGInventoryBroker2.cs | 477 ++++++++++-------- .../RemoteXInventoryServiceConnector.cs | 92 ++-- 2 files changed, 286 insertions(+), 283 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs index 22aac02c75..3509161daa 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs @@ -41,7 +41,7 @@ using OpenMetaverse; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { - public class HGInventoryBroker2 : BaseInventoryConnector, INonSharedRegionModule, IInventoryService + public class HGInventoryBroker2 : INonSharedRegionModule, IInventoryService { private static readonly ILog m_log = LogManager.GetLogger( @@ -54,8 +54,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory private static ISessionAuthInventoryService m_HGService; // obsolete private Dictionary m_connectors = new Dictionary(); + // A cache of userIDs --> ServiceURLs, for HGBroker only + protected Dictionary m_InventoryURLs; private Scene m_Scene; + private List m_Scenes = new List(); + private IUserAccountService m_UserAccountService; public Type ReplaceableInterface @@ -124,8 +128,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return; } - Init(source); - m_Enabled = true; m_log.Info("[HG INVENTORY CONNECTOR]: HG inventory broker enabled"); } @@ -148,18 +150,22 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return; m_Scene = scene; + m_Scenes.Add(scene); m_UserAccountService = m_Scene.UserAccountService; scene.RegisterModuleInterface(this); - m_cache.AddRegion(scene); + + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.EventManager.OnClientClosed += OnClientClosed; + } public void RemoveRegion(Scene scene) { if (!m_Enabled) return; - - m_cache.RemoveRegion(scene); + + m_Scenes.Remove(scene); } public void RegionLoaded(Scene scene) @@ -171,282 +177,302 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory } + #region Cache + + void OnMakeRootAgent(ScenePresence presence) + { + if (!m_InventoryURLs.ContainsKey(presence.UUID)) + CacheInventoryServiceURL(presence.Scene, presence.UUID); + } + + void OnClientClosed(UUID clientID, Scene scene) + { + if (m_InventoryURLs.ContainsKey(clientID)) // if it's in cache + { + ScenePresence sp = null; + foreach (Scene s in m_Scenes) + { + s.TryGetScenePresence(clientID, out sp); + if ((sp != null) && !sp.IsChildAgent && (s != scene)) + { + m_log.DebugFormat("[INVENTORY CACHE]: OnClientClosed in {0}, but user {1} still in sim. Keeping inventoryURL in cache", + scene.RegionInfo.RegionName, clientID); + return; + } + } + + m_log.DebugFormat( + "[INVENTORY CACHE]: OnClientClosed in {0}, user {1} out of sim. Dropping inventory URL", + scene.RegionInfo.RegionName, clientID); + DropInventoryServiceURL(clientID); + } + } + + /// + /// Gets the user's inventory URL from its serviceURLs, if the user is foreign, + /// and sticks it in the cache + /// + /// + private void CacheInventoryServiceURL(Scene scene, UUID userID) + { + if (scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, userID) == null) + { + // The user does not have a local account; let's cache its service URL + string inventoryURL = string.Empty; + ScenePresence sp = null; + scene.TryGetScenePresence(userID, out sp); + if (sp != null) + { + AgentCircuitData aCircuit = scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); + if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) + { + inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); + if (inventoryURL != null && inventoryURL != string.Empty) + { + inventoryURL = inventoryURL.Trim(new char[] { '/' }); + m_InventoryURLs.Add(userID, inventoryURL); + } + } + } + } + } + + private void DropInventoryServiceURL(UUID userID) + { + lock (m_InventoryURLs) + if (m_InventoryURLs.ContainsKey(userID)) + m_InventoryURLs.Remove(userID); + } + + public string GetInventoryServiceURL(UUID userID) + { + if (m_InventoryURLs.ContainsKey(userID)) + return m_InventoryURLs[userID]; + + return null; + } + #endregion #region IInventoryService - public override bool CreateUserInventory(UUID userID) + public bool CreateUserInventory(UUID userID) { return m_LocalGridInventoryService.CreateUserInventory(userID); } - public override List GetInventorySkeleton(UUID userId) + public List GetInventorySkeleton(UUID userId) { return m_LocalGridInventoryService.GetInventorySkeleton(userId); } - public override InventoryCollection GetUserInventory(UUID userID) + public InventoryCollection GetUserInventory(UUID userID) { return null; } - public override void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) { } - // Inherited. See base - //public override InventoryFolderBase GetFolderForType(UUID userID, AssetType type) - //{ - // if (IsLocalGridUser(userID)) - // return m_GridService.GetFolderForType(userID, type); - // else - // { - // UUID sessionID = GetSessionID(userID); - // string uri = GetUserInventoryURI(userID) + "/" + userID.ToString(); - // // !!!!!! - // return null; - // //return m_HGService.GetFolderForType(uri, sessionID, type); - // } - //} + public InventoryFolderBase GetRootFolder(UUID userID) + { + m_log.DebugFormat("[HGInventory]: GetRootFolder for {0}", userID); - public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) + string invURL = GetInventoryServiceURL(userID); + + if (invURL == null) // not there, forward to local inventory connector to resolve + return m_LocalGridInventoryService.GetRootFolder(userID); + + IInventoryService connector = GetConnector(invURL); + + return connector.GetRootFolder(userID); + } + + public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + { + m_log.DebugFormat("[HGInventory]: GetFolderForType {0} type {1}", userID, type); + + string invURL = GetInventoryServiceURL(userID); + + if (invURL == null) // not there, forward to local inventory connector to resolve + return m_LocalGridInventoryService.GetFolderForType(userID, type); + + IInventoryService connector = GetConnector(invURL); + + return connector.GetFolderForType(userID, type); + } + + public InventoryCollection GetFolderContent(UUID userID, UUID folderID) { m_log.Debug("[HGInventory]: GetFolderContent " + folderID); - string url = string.Empty; - string invURL = m_cache.GetInventoryServiceURL(userID); + string invURL = GetInventoryServiceURL(userID); if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.GetFolderContent(userID, folderID); - IInventoryService connector = GetConnector(url); + IInventoryService connector = GetConnector(invURL); + return connector.GetFolderContent(userID, folderID); - //if (StringToUrlAndUserID(id, out url, out userID)) - //{ - // ISessionAuthInventoryService connector = GetConnector(url); - // return connector.GetFolderContent(userID, folderID, sessionID); - //} - - //return null; - - ////////// - - //string uri = string.Empty; - //if (!IsForeignUser(userID, out uri)) - // return m_GridService.GetFolderContent(userID, folderID); - //else - //{ - // UUID sessionID = GetSessionID(userID); - // uri = uri + "/" + userID.ToString(); - // return m_HGService.GetFolderContent(uri, folderID, sessionID); - //} } - public override Dictionary GetSystemFolders(UUID userID) + public List GetFolderItems(UUID userID, UUID folderID) { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) - { - // This is not pretty, but it will have to do for now - if (m_LocalGridInventoryService is BaseInventoryConnector) - { - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetSystemsFolders redirected to RemoteInventoryServiceConnector module"); - return ((BaseInventoryConnector)m_LocalGridInventoryService).GetSystemFolders(userID); - } - else - { - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetSystemsFolders redirected to GetSystemFoldersLocal"); - return GetSystemFoldersLocal(userID); - } - } - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetSystemFolders(uri, sessionID); - } - } + m_log.Debug("[HGInventory]: GetFolderItems " + folderID); - private Dictionary GetSystemFoldersLocal(UUID userID) - { - InventoryFolderBase root = m_LocalGridInventoryService.GetRootFolder(userID); - if (root != null) - { - InventoryCollection content = m_LocalGridInventoryService.GetFolderContent(userID, root.ID); - if (content != null) - { - Dictionary folders = new Dictionary(); - foreach (InventoryFolderBase folder in content.Folders) - { - //m_log.DebugFormat("[HG INVENTORY CONNECTOR]: scanning folder type {0}", (AssetType)folder.Type); - if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) - folders[(AssetType)folder.Type] = folder; - } - // Put the root folder there, as type Folder - folders[AssetType.Folder] = root; - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: System folders count for {0}: {1}", userID, folders.Count); - return folders; - } - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Root folder content not found for {0}", userID); + string invURL = GetInventoryServiceURL(userID); - } - - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Root folder not found for {0}", userID); - - return new Dictionary(); - } - - public override List GetFolderItems(UUID userID, UUID folderID) - { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.GetFolderItems(userID, folderID); - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetFolderItems(uri, folderID, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.GetFolderItems(userID, folderID); + } - public override bool AddFolder(InventoryFolderBase folder) + public bool AddFolder(InventoryFolderBase folder) { if (folder == null) return false; - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) + m_log.Debug("[HGInventory]: AddFolder " + folder.ID); + + string invURL = GetInventoryServiceURL(folder.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.AddFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.AddFolder(uri, folder, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.AddFolder(folder); } - public override bool UpdateFolder(InventoryFolderBase folder) + public bool UpdateFolder(InventoryFolderBase folder) { if (folder == null) return false; - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) + m_log.Debug("[HGInventory]: UpdateFolder " + folder.ID); + + string invURL = GetInventoryServiceURL(folder.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.UpdateFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.UpdateFolder(uri, folder, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.UpdateFolder(folder); } - public override bool DeleteFolders(UUID ownerID, List folderIDs) + public bool DeleteFolders(UUID ownerID, List folderIDs) { if (folderIDs == null) return false; if (folderIDs.Count == 0) return false; - string uri = string.Empty; - if (!IsForeignUser(ownerID, out uri)) + m_log.Debug("[HGInventory]: DeleteFolders for " + ownerID); + + string invURL = GetInventoryServiceURL(ownerID); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.DeleteFolders(ownerID, folderIDs); - else - { - UUID sessionID = GetSessionID(ownerID); - uri = uri + "/" + ownerID.ToString(); - return m_HGService.DeleteFolders(uri, folderIDs, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.DeleteFolders(ownerID, folderIDs); } - public override bool MoveFolder(InventoryFolderBase folder) + public bool MoveFolder(InventoryFolderBase folder) { if (folder == null) return false; - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) + m_log.Debug("[HGInventory]: MoveFolder for " + folder.Owner); + + string invURL = GetInventoryServiceURL(folder.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.MoveFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.MoveFolder(uri, folder, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.MoveFolder(folder); } - public override bool PurgeFolder(InventoryFolderBase folder) + public bool PurgeFolder(InventoryFolderBase folder) { if (folder == null) return false; - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) + m_log.Debug("[HGInventory]: PurgeFolder for " + folder.Owner); + + string invURL = GetInventoryServiceURL(folder.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.PurgeFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.PurgeFolder(uri, folder, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.PurgeFolder(folder); } - // public bool AddItem(InventoryItemBase item) inherited - // Uses AddItemPlain - - protected override bool AddItemPlain(InventoryItemBase item) + public bool AddItem(InventoryItemBase item) { if (item == null) return false; - string uri = string.Empty; - if (!IsForeignUser(item.Owner, out uri)) - { + m_log.Debug("[HGInventory]: AddItem " + item.ID); + + string invURL = GetInventoryServiceURL(item.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.AddItem(item); - } - else - { - UUID sessionID = GetSessionID(item.Owner); - uri = uri + "/" + item.Owner.ToString(); - return m_HGService.AddItem(uri, item, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.AddItem(item); } - public override bool UpdateItem(InventoryItemBase item) + public bool UpdateItem(InventoryItemBase item) { if (item == null) return false; - string uri = string.Empty; - if (!IsForeignUser(item.Owner, out uri)) + m_log.Debug("[HGInventory]: UpdateItem " + item.ID); + + string invURL = GetInventoryServiceURL(item.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.UpdateItem(item); - else - { - UUID sessionID = GetSessionID(item.Owner); - uri = uri + "/" + item.Owner.ToString(); - return m_HGService.UpdateItem(uri, item, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.UpdateItem(item); } - public override bool MoveItems(UUID ownerID, List items) + public bool MoveItems(UUID ownerID, List items) { if (items == null) return false; if (items.Count == 0) return true; - string uri = string.Empty; - if (!IsForeignUser(ownerID, out uri)) + m_log.Debug("[HGInventory]: MoveItems for " + ownerID); + + string invURL = GetInventoryServiceURL(ownerID); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.MoveItems(ownerID, items); - else - { - UUID sessionID = GetSessionID(ownerID); - uri = uri + "/" + ownerID.ToString(); - return m_HGService.MoveItems(uri, items, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.MoveItems(ownerID, items); } - public override bool DeleteItems(UUID ownerID, List itemIDs) + public bool DeleteItems(UUID ownerID, List itemIDs) { m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Delete {0} items for user {1}", itemIDs.Count, ownerID); @@ -455,70 +481,73 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (itemIDs.Count == 0) return true; - string uri = string.Empty; - if (!IsForeignUser(ownerID, out uri)) + m_log.Debug("[HGInventory]: DeleteItems for " + ownerID); + + string invURL = GetInventoryServiceURL(ownerID); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.DeleteItems(ownerID, itemIDs); - else - { - UUID sessionID = GetSessionID(ownerID); - uri = uri + "/" + ownerID.ToString(); - return m_HGService.DeleteItems(uri, itemIDs, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.DeleteItems(ownerID, itemIDs); } - public override InventoryItemBase GetItem(InventoryItemBase item) + public InventoryItemBase GetItem(InventoryItemBase item) { if (item == null) return null; - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetItem {0} for user {1}", item.ID, item.Owner); - string uri = string.Empty; - if (!IsForeignUser(item.Owner, out uri)) + m_log.Debug("[HGInventory]: GetItem " + item.ID); + + string invURL = GetInventoryServiceURL(item.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.GetItem(item); - else - { - UUID sessionID = GetSessionID(item.Owner); - uri = uri + "/" + item.Owner.ToString(); - return m_HGService.QueryItem(uri, item, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.GetItem(item); } - public override InventoryFolderBase GetFolder(InventoryFolderBase folder) + public InventoryFolderBase GetFolder(InventoryFolderBase folder) { if (folder == null) return null; - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) + m_log.Debug("[HGInventory]: GetFolder " + folder.ID); + + string invURL = GetInventoryServiceURL(folder.Owner); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.GetFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.QueryFolder(uri, folder, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.GetFolder(folder); } - public override bool HasInventoryForUser(UUID userID) + public bool HasInventoryForUser(UUID userID) { return false; } - public override List GetActiveGestures(UUID userId) + public List GetActiveGestures(UUID userId) { return new List(); } - public override int GetAssetPermissions(UUID userID, UUID assetID) + public int GetAssetPermissions(UUID userID, UUID assetID) { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) + m_log.Debug("[HGInventory]: GetAssetPermissions " + assetID); + + string invURL = GetInventoryServiceURL(userID); + + if (invURL == null) // not there, forward to local inventory connector to resolve return m_LocalGridInventoryService.GetAssetPermissions(userID, assetID); - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetAssetPermissions(uri, assetID, sessionID); - } + + IInventoryService connector = GetConnector(invURL); + + return connector.GetAssetPermissions(userID, assetID); } #endregion diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs index 96d0c1c2fd..ac9e792317 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs @@ -41,7 +41,7 @@ using OpenMetaverse; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { - public class RemoteXInventoryServicesConnector : BaseInventoryConnector, ISharedRegionModule, IInventoryService + public class RemoteXInventoryServicesConnector : ISharedRegionModule, IInventoryService { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -75,10 +75,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory Init(source); } - protected override void Init(IConfigSource source) + protected void Init(IConfigSource source) { m_RemoteConnector = new XInventoryServicesConnector(source); - base.Init(source); } @@ -122,7 +121,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory } scene.RegisterModuleInterface(this); - m_cache.AddRegion(scene); } public void RemoveRegion(Scene scene) @@ -130,7 +128,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory if (!m_Enabled) return; - m_cache.RemoveRegion(scene); } public void RegionLoaded(Scene scene) @@ -146,71 +143,51 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory #region IInventoryService - public override bool CreateUserInventory(UUID user) + public bool CreateUserInventory(UUID user) { return false; } - public override List GetInventorySkeleton(UUID userId) + public List GetInventorySkeleton(UUID userId) { return new List(); } - public override InventoryCollection GetUserInventory(UUID userID) + public InventoryCollection GetUserInventory(UUID userID) { return null; } - public override void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) { - try - { - m_RemoteConnector.GetUserInventory(userID, callback); - } - catch (Exception e) - { - if (StatsManager.SimExtraStats != null) - StatsManager.SimExtraStats.AddInventoryServiceRetrievalFailure(); - - m_log.ErrorFormat("[XINVENTORY CONNECTOR]: Request inventory operation failed, {0} {1}", - e.Source, e.Message); - } - } - // inherited. See base class - // public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + public InventoryFolderBase GetRootFolder(UUID userID) + { + return m_RemoteConnector.GetRootFolder(userID); + } - public override Dictionary GetSystemFolders(UUID userID) + public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + { + return m_RemoteConnector.GetFolderForType(userID, type); + } + + public Dictionary GetSystemFolders(UUID userID) { return m_RemoteConnector.GetSystemFolders(userID); } - public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) + public InventoryCollection GetFolderContent(UUID userID, UUID folderID) { - m_log.DebugFormat("[XINVENTORY CONNECTOR]: GetFolderContent {0}", folderID); - try - { - return m_RemoteConnector.GetFolderContent(userID, folderID); - } - catch (Exception e) - { - m_log.ErrorFormat("[XINVENTORY CONNECTOR]: GetFolderContent operation failed, {0} {1}", - e.Source, e.Message); - } - InventoryCollection nullCollection = new InventoryCollection(); - nullCollection.Folders = new List(); - nullCollection.Items = new List(); - nullCollection.UserID = userID; - return nullCollection; + return m_RemoteConnector.GetFolderContent(userID, folderID); } - public override List GetFolderItems(UUID userID, UUID folderID) + public List GetFolderItems(UUID userID, UUID folderID) { return m_RemoteConnector.GetFolderItems(userID, folderID); } - public override bool AddFolder(InventoryFolderBase folder) + public bool AddFolder(InventoryFolderBase folder) { if (folder == null) return false; @@ -218,7 +195,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.AddFolder(folder); } - public override bool UpdateFolder(InventoryFolderBase folder) + public bool UpdateFolder(InventoryFolderBase folder) { if (folder == null) return false; @@ -226,7 +203,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.UpdateFolder(folder); } - public override bool MoveFolder(InventoryFolderBase folder) + public bool MoveFolder(InventoryFolderBase folder) { if (folder == null) return false; @@ -234,7 +211,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.MoveFolder(folder); } - public override bool DeleteFolders(UUID ownerID, List folderIDs) + public bool DeleteFolders(UUID ownerID, List folderIDs) { if (folderIDs == null) return false; @@ -245,7 +222,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory } - public override bool PurgeFolder(InventoryFolderBase folder) + public bool PurgeFolder(InventoryFolderBase folder) { if (folder == null) return false; @@ -253,10 +230,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.PurgeFolder(folder); } - // public bool AddItem(InventoryItemBase item) inherited - // Uses AddItemPlain - - protected override bool AddItemPlain(InventoryItemBase item) + public bool AddItem(InventoryItemBase item) { if (item == null) return false; @@ -264,7 +238,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.AddItem(item); } - public override bool UpdateItem(InventoryItemBase item) + public bool UpdateItem(InventoryItemBase item) { if (item == null) return false; @@ -272,7 +246,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.UpdateItem(item); } - public override bool MoveItems(UUID ownerID, List items) + public bool MoveItems(UUID ownerID, List items) { if (items == null) return false; @@ -281,7 +255,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory } - public override bool DeleteItems(UUID ownerID, List itemIDs) + public bool DeleteItems(UUID ownerID, List itemIDs) { if (itemIDs == null) return false; @@ -291,7 +265,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.DeleteItems(ownerID, itemIDs); } - public override InventoryItemBase GetItem(InventoryItemBase item) + public InventoryItemBase GetItem(InventoryItemBase item) { if (item == null) return null; @@ -299,7 +273,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.GetItem(item); } - public override InventoryFolderBase GetFolder(InventoryFolderBase folder) + public InventoryFolderBase GetFolder(InventoryFolderBase folder) { m_log.DebugFormat("[XINVENTORY CONNECTOR]: GetFolder {0}", folder.ID); if (folder == null) @@ -308,17 +282,17 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return m_RemoteConnector.GetFolder(folder); } - public override bool HasInventoryForUser(UUID userID) + public bool HasInventoryForUser(UUID userID) { return false; } - public override List GetActiveGestures(UUID userId) + public List GetActiveGestures(UUID userId) { return new List(); } - public override int GetAssetPermissions(UUID userID, UUID assetID) + public int GetAssetPermissions(UUID userID, UUID assetID) { return m_RemoteConnector.GetAssetPermissions(userID, assetID); } From 22b32171130b557dd83df218a38629589c7cc570 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 30 Apr 2010 11:46:50 +0100 Subject: [PATCH 041/260] Fix link security issue --- OpenSim/Framework/IClientAPI.cs | 2 +- .../ClientStack/LindenUDP/LLClientView.cs | 4 +- .../World/Permissions/PermissionsModule.cs | 6 +-- OpenSim/Region/DataSnapshot/ObjectSnapshot.cs | 2 +- .../Framework/Scenes/Scene.Inventory.cs | 50 +++++++++++++++++++ OpenSim/Region/Framework/Scenes/Scene.cs | 8 +-- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 41 ++++----------- 7 files changed, 70 insertions(+), 43 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index af88c4a632..01daeb1402 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -73,7 +73,7 @@ namespace OpenSim.Framework public delegate void LinkObjects(IClientAPI remoteClient, uint parent, List children); - public delegate void DelinkObjects(List primIds); + public delegate void DelinkObjects(List primIds, IClientAPI client); public delegate void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index d7120a5344..1f3582cfba 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -6151,7 +6151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP DelinkObjects handlerDelinkObjects = OnDelinkObjects; if (handlerDelinkObjects != null) { - handlerDelinkObjects(prims); + handlerDelinkObjects(prims, this); } return true; @@ -11820,4 +11820,4 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(dialog, ThrottleOutPacketType.Task); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index 01359f0db9..f6bb3fef29 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -1721,7 +1721,7 @@ namespace OpenSim.Region.CoreModules.World.Permissions DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); if (m_bypassPermissions) return m_bypassPermissionsValue; - return true; + return GenericObjectPermission(editorID, objectID, false); } private bool CanDelinkObject(UUID userID, UUID objectID) @@ -1729,7 +1729,7 @@ namespace OpenSim.Region.CoreModules.World.Permissions DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); if (m_bypassPermissions) return m_bypassPermissionsValue; - return true; + return GenericObjectPermission(editorID, objectID, false); } private bool CanBuyLand(UUID userID, ILandObject parcel, Scene scene) @@ -1894,4 +1894,4 @@ namespace OpenSim.Region.CoreModules.World.Permissions return(false); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs b/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs index f441aa9a84..6e699025b1 100644 --- a/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs +++ b/OpenSim/Region/DataSnapshot/ObjectSnapshot.cs @@ -69,7 +69,7 @@ namespace OpenSim.Region.DataSnapshot.Providers byte RayEndIsIntersection) { this.Stale = true; }; client.OnLinkObjects += delegate (IClientAPI remoteClient, uint parent, List children) { this.Stale = true; }; - client.OnDelinkObjects += delegate(List primIds) { this.Stale = true; }; + client.OnDelinkObjects += delegate(List primIds, IClientAPI clientApi) { this.Stale = true; }; client.OnGrabUpdate += delegate(UUID objectID, Vector3 offset, Vector3 grapPos, IClientAPI remoteClient, List surfaceArgs) { this.Stale = true; }; client.OnObjectAttach += delegate(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 15b523095f..6ebfd31896 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1941,5 +1941,55 @@ namespace OpenSim.Region.Framework.Scenes part.GetProperties(remoteClient); } } + + public void DelinkObjects(List primIds, IClientAPI client) + { + List parts = new List(); + + foreach (uint localID in primIds) + { + SceneObjectPart part = GetSceneObjectPart(localID); + + if (part == null) + continue; + + if (Permissions.CanDelinkObject(client.AgentId, part.ParentGroup.RootPart.UUID)) + parts.Add(part); + } + + m_sceneGraph.DelinkObjects(parts); + } + + public void LinkObjects(IClientAPI client, uint parentPrimId, List childPrimIds) + { + List owners = new List(); + + List children = new List(); + SceneObjectPart root = GetSceneObjectPart(parentPrimId); + + if (Permissions.CanLinkObject(client.AgentId, root.ParentGroup.RootPart.UUID)) + return; + + foreach (uint localID in childPrimIds) + { + SceneObjectPart part = GetSceneObjectPart(localID); + + if (part == null) + continue; + + if (!owners.Contains(part.OwnerID)) + owners.Add(part.OwnerID); + + if (Permissions.CanLinkObject(client.AgentId, part.ParentGroup.RootPart.UUID)) + children.Add(part); + } + + // Must be all one owner + // + if (owners.Count > 1) + return; + + m_sceneGraph.LinkObjects(root, children); + } } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 57587bedd4..61a2956ed8 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2721,8 +2721,8 @@ namespace OpenSim.Region.Framework.Scenes client.OnObjectName += m_sceneGraph.PrimName; client.OnObjectClickAction += m_sceneGraph.PrimClickAction; client.OnObjectMaterial += m_sceneGraph.PrimMaterial; - client.OnLinkObjects += m_sceneGraph.LinkObjects; - client.OnDelinkObjects += m_sceneGraph.DelinkObjects; + client.OnLinkObjects += LinkObjects; + client.OnDelinkObjects += DelinkObjects; client.OnObjectDuplicate += m_sceneGraph.DuplicateObject; client.OnObjectDuplicateOnRay += doObjectDuplicateOnRay; client.OnUpdatePrimFlags += m_sceneGraph.UpdatePrimFlags; @@ -2878,8 +2878,8 @@ namespace OpenSim.Region.Framework.Scenes client.OnObjectName -= m_sceneGraph.PrimName; client.OnObjectClickAction -= m_sceneGraph.PrimClickAction; client.OnObjectMaterial -= m_sceneGraph.PrimMaterial; - client.OnLinkObjects -= m_sceneGraph.LinkObjects; - client.OnDelinkObjects -= m_sceneGraph.DelinkObjects; + client.OnLinkObjects -= LinkObjects; + client.OnDelinkObjects -= DelinkObjects; client.OnObjectDuplicate -= m_sceneGraph.DuplicateObject; client.OnObjectDuplicateOnRay -= doObjectDuplicateOnRay; client.OnUpdatePrimFlags -= m_sceneGraph.UpdatePrimFlags; diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 1421d0e974..ce11267196 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -1463,20 +1463,21 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// - protected internal void LinkObjects(IClientAPI client, uint parentPrimId, List childPrimIds) + protected internal void LinkObjects(SceneObjectPart root, List children) { Monitor.Enter(m_updateLock); try { - SceneObjectGroup parentGroup = GetGroupByPrim(parentPrimId); + SceneObjectGroup parentGroup = root.ParentGroup; List childGroups = new List(); if (parentGroup != null) { // We do this in reverse to get the link order of the prims correct - for (int i = childPrimIds.Count - 1; i >= 0; i--) + for (int i = children.Count - 1; i >= 0; i--) { - SceneObjectGroup child = GetGroupByPrim(childPrimIds[i]); + SceneObjectGroup child = children[i].ParentGroup; + if (child != null) { // Make sure no child prim is set for sale @@ -1509,17 +1510,6 @@ namespace OpenSim.Region.Framework.Scenes parentGroup.HasGroupChanged = true; parentGroup.ScheduleGroupForFullUpdate(); -// if (client != null) -// { -// parentGroup.GetProperties(client); -// } -// else -// { -// foreach (ScenePresence p in GetScenePresences()) -// { -// parentGroup.GetProperties(p.ControllingClient); -// } -// } } finally { @@ -1531,12 +1521,7 @@ namespace OpenSim.Region.Framework.Scenes /// Delink a linkset ///
/// - protected internal void DelinkObjects(List primIds) - { - DelinkObjects(primIds, true); - } - - protected internal void DelinkObjects(List primIds, bool sendEvents) + protected internal void DelinkObjects(List prims) { Monitor.Enter(m_updateLock); try @@ -1546,9 +1531,8 @@ namespace OpenSim.Region.Framework.Scenes List affectedGroups = new List(); // Look them all up in one go, since that is comparatively expensive // - foreach (uint primID in primIds) + foreach (SceneObjectPart part in prims) { - SceneObjectPart part = m_parentScene.GetSceneObjectPart(primID); if (part != null) { if (part.ParentGroup.Children.Count != 1) // Skip single @@ -1563,17 +1547,13 @@ namespace OpenSim.Region.Framework.Scenes affectedGroups.Add(group); } } - else - { - m_log.ErrorFormat("Viewer requested unlink of nonexistent part {0}", primID); - } } foreach (SceneObjectPart child in childParts) { // Unlink all child parts from their groups // - child.ParentGroup.DelinkFromGroup(child, sendEvents); + child.ParentGroup.DelinkFromGroup(child, true); } foreach (SceneObjectPart root in rootParts) @@ -1628,12 +1608,9 @@ namespace OpenSim.Region.Framework.Scenes List linkIDs = new List(); foreach (SceneObjectPart newChild in newSet) - { newChild.UpdateFlag = 0; - linkIDs.Add(newChild.LocalId); - } - LinkObjects(null, newRoot.LocalId, linkIDs); + LinkObjects(newRoot, newSet); if (!affectedGroups.Contains(newRoot.ParentGroup)) affectedGroups.Add(newRoot.ParentGroup); } From 0ea908291d63125c4c436ec1056af767aac716e6 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 30 Apr 2010 12:06:58 +0100 Subject: [PATCH 042/260] Fix some symbol errors --- .../Region/CoreModules/World/Permissions/PermissionsModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index f6bb3fef29..69b247c350 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -1721,7 +1721,7 @@ namespace OpenSim.Region.CoreModules.World.Permissions DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); if (m_bypassPermissions) return m_bypassPermissionsValue; - return GenericObjectPermission(editorID, objectID, false); + return GenericObjectPermission(userID, objectID, false); } private bool CanDelinkObject(UUID userID, UUID objectID) @@ -1729,7 +1729,7 @@ namespace OpenSim.Region.CoreModules.World.Permissions DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); if (m_bypassPermissions) return m_bypassPermissionsValue; - return GenericObjectPermission(editorID, objectID, false); + return GenericObjectPermission(userID, objectID, false); } private bool CanBuyLand(UUID userID, ILandObject parcel, Scene scene) From 60dbc3c6ce5b9b78a29c0968951e96895c270241 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 30 Apr 2010 17:01:50 +0100 Subject: [PATCH 043/260] Make SQLiteNG the default since it actually does work with Mono 2.4 on Linux. I know this is tough on Mac OSX users (since SQLiteNG requires the export of the sqlite3_column_origin_name symbol and this isn't present for the Mac OSX sqlite3 system library) Unfortunately, I need to shaft somebody (as it were) --- bin/OpenSim.ini.example | 12 ++++++++---- bin/config-include/StandaloneCommon.ini.example | 10 +++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 59bce363cd..bbc6f78721 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -112,11 +112,15 @@ ;storage_plugin = "OpenSim.Data.Null.dll" ; --- To use sqlite as region storage: - ; PLEASE NOTE: If you use want to use SQLite with Mono 2.6 and above, you must use the SQLiteNG plugin rather than the existing SQLite one - ; do this by commenting out the OpenSim.Data.SQLite.dll line below and uncommenting the OpenSim.Data.SQLiteNG.dll one + ; + ; PLEASE NOTE: Unfortunately, the SQLiteNG database plugin, while necessary to use sqlite with Mono on Linux, is + ; not compatible with the sqlite3 library installed on Mac OSX. If you're using Mono 2.4 you can still use the old sqlite + ; library by uncommenting the SQLite.dll storage plugin (and commenting out SQLiteNG). Unfortunately, the older library + ; will not work with Mono 2.6 on Mac OSX so you will either need to replace the sqlite3 system library or use MySQL instead + ; ; You will also need to do the same thing in config-include/StandaloneCommon.ini if you are running in standalone mode - storage_plugin = "OpenSim.Data.SQLite.dll" - ; storage_plugin = "OpenSim.Data.SQLiteNG.dll" + storage_plugin = "OpenSim.Data.SQLiteNG.dll" + ;storage_plugin = "OpenSim.Data.SQLite.dll" storage_connection_string="URI=file:OpenSim.db,version=3"; ; --- To use MySQL storage, supply your own connection string (this is only an example): diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index 74bdbe2d23..58860d157f 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -6,12 +6,12 @@ ; ; SQLite - ; Uncomment this line if you want to use sqlite storage with Mono 2.4 - Include-Storage = "config-include/storage/SQLiteStandalone.ini"; + Include-Storage = "config-include/storage/SQLiteNGStandalone.ini"; - ; If you want to use sqlite with Mono 2.6 and above, uncomment this line instead. - ; Don't forget to do the same thing for the storage_plugin setting in OpenSim.ini - ; Include-Storage = "config-include/storage/SQLiteNGStandalone.ini"; + ; Unfortunately SQLiteNG is not compatible with Mac OSX. You can still use the older + ; sqlite library if you are using Mono 2.4. Please see the notes in OpenSim.ini for sqlite + ; for more details + ;Include-Storage = "config-include/storage/SQLiteStandalone.ini"; ; MySql ; Uncomment these lines if you want to use mysql storage From cc67de5b86ebcebadbe2ea46872a0dc63d99cae7 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 30 Apr 2010 17:45:00 +0100 Subject: [PATCH 044/260] rename SQLiteNG to SQLite and SQLite to SQLiteLegacy this seems the least evil way forward since mono 2.6 and later will see increasing usage, and this only works with what was SQLiteNG MAC USERS WILL NEED TO CHANGE REFERENCES TO "OpenSim.Data.SQLite.dll" to "OpenSim.Data.SQLiteLegacy.dll" in OpenSim.ini and config-include/StandaloneCommon.ini (if using standalone) See the OpenSim.ini.example and StandaloneCommon.ini.example files for more details This commit also temporarily changes unsigned ParentEstateID values in the OpenSim.Data.Tests to signed temporarily, since the new plugin enforces creation of signed fields in the database (which is what the SQL actually specifies). And change data columns in sqlite is a pita. --- OpenSim/Data/SQLite/SQLiteAssetData.cs | 6 +- .../Data/SQLite/SQLiteAuthenticationData.cs | 23 +- OpenSim/Data/SQLite/SQLiteAvatarData.cs | 8 +- OpenSim/Data/SQLite/SQLiteEstateData.cs | 48 +-- OpenSim/Data/SQLite/SQLiteFramework.cs | 14 +- OpenSim/Data/SQLite/SQLiteFriendsData.cs | 8 +- .../Data/SQLite/SQLiteGenericTableHandler.cs | 18 +- OpenSim/Data/SQLite/SQLiteInventoryStore.cs | 13 +- OpenSim/Data/SQLite/SQLiteRegionData.cs | 279 ++++++++++------- OpenSim/Data/SQLite/SQLiteUserAccountData.cs | 4 +- OpenSim/Data/SQLite/SQLiteUtils.cs | 2 +- OpenSim/Data/SQLite/SQLiteXInventoryData.cs | 4 +- .../Properties/AssemblyInfo.cs | 4 +- .../Resources/001_AssetStore.sql | 0 .../Resources/001_AuthStore.sql | 0 .../Resources/001_Avatar.sql | 0 .../Resources/001_FriendsStore.sql | 0 .../Resources/001_InventoryStore.sql | 0 .../Resources/001_RegionStore.sql | 0 .../Resources/001_UserAccount.sql | 0 .../Resources/001_UserStore.sql | 0 .../Resources/002_AssetStore.sql | 0 .../Resources/002_AuthStore.sql | 0 .../Resources/002_FriendsStore.sql | 0 .../Resources/002_InventoryStore.sql | 0 .../Resources/002_RegionStore.sql | 0 .../Resources/002_UserAccount.sql | 0 .../Resources/002_UserStore.sql | 0 .../Resources/003_AssetStore.sql | 0 .../Resources/003_InventoryStore.sql | 0 .../Resources/003_RegionStore.sql | 0 .../Resources/003_UserStore.sql | 0 .../Resources/004_AssetStore.sql | 0 .../Resources/004_InventoryStore.sql | 0 .../Resources/004_RegionStore.sql | 0 .../Resources/004_UserStore.sql | 0 .../Resources/005_RegionStore.sql | 0 .../Resources/005_UserStore.sql | 0 .../Resources/006_RegionStore.sql | 0 .../Resources/006_UserStore.sql | 0 .../Resources/007_RegionStore.sql | 0 .../Resources/007_UserStore.sql | 0 .../Resources/008_RegionStore.sql | 0 .../Resources/008_UserStore.sql | 0 .../Resources/009_RegionStore.sql | 0 .../Resources/009_UserStore.sql | 0 .../Resources/010_RegionStore.sql | 0 .../Resources/010_UserStore.sql | 0 .../Resources/011_RegionStore.sql | 0 .../Resources/012_RegionStore.sql | 0 .../Resources/013_RegionStore.sql | 0 .../Resources/014_RegionStore.sql | 0 .../Resources/015_RegionStore.sql | 0 .../Resources/016_RegionStore.sql | 0 .../Resources/017_RegionStore.sql | 0 .../Resources/018_RegionStore.sql | 0 .../Resources/OpenSim.Data.SQLite.addin.xml | 0 .../SQLiteAssetData.cs | 8 +- .../SQLiteAuthenticationData.cs | 25 +- .../SQLiteAvatarData.cs | 10 +- .../SQLiteEstateData.cs | 50 +-- .../SQLiteFramework.cs | 16 +- .../SQLiteFriendsData.cs | 10 +- .../SQLiteGenericTableHandler.cs | 20 +- .../SQLiteInventoryStore.cs | 15 +- .../SQLiteRegionData.cs | 289 +++++++----------- .../SQLiteUserAccountData.cs | 6 +- .../{SQLiteNG => SQLiteLegacy}/SQLiteUtils.cs | 4 +- .../SQLiteXInventoryData.cs | 6 +- OpenSim/Data/Tests/DataTestUtil.cs | 3 +- bin/OpenSim.ini.example | 10 +- .../StandaloneCommon.ini.example | 8 +- ...ndalone.ini => SQLiteLegacyStandalone.ini} | 2 +- prebuild.xml | 4 +- 74 files changed, 459 insertions(+), 458 deletions(-) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Properties/AssemblyInfo.cs (96%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_AssetStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_AuthStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_Avatar.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_FriendsStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_InventoryStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_UserAccount.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/001_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_AssetStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_AuthStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_FriendsStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_InventoryStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_UserAccount.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/002_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/003_AssetStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/003_InventoryStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/003_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/003_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/004_AssetStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/004_InventoryStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/004_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/004_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/005_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/005_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/006_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/006_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/007_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/007_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/008_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/008_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/009_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/009_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/010_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/010_UserStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/011_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/012_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/013_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/014_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/015_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/016_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/017_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/018_RegionStore.sql (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/Resources/OpenSim.Data.SQLite.addin.xml (100%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteAssetData.cs (99%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteAuthenticationData.cs (93%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteAvatarData.cs (92%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteEstateData.cs (87%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteFramework.cs (89%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteFriendsData.cs (91%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteGenericTableHandler.cs (93%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteInventoryStore.cs (98%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteRegionData.cs (91%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteUserAccountData.cs (94%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteUtils.cs (99%) rename OpenSim/Data/{SQLiteNG => SQLiteLegacy}/SQLiteXInventoryData.cs (98%) rename bin/config-include/storage/{SQLiteNGStandalone.ini => SQLiteLegacyStandalone.ini} (88%) diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index a032670588..636bf8645b 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -30,7 +30,7 @@ using System.Data; using System.Reflection; using System.Collections.Generic; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; @@ -137,7 +137,7 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); - + cmd.ExecuteNonQuery(); } } @@ -340,4 +340,4 @@ namespace OpenSim.Data.SQLite #endregion } -} \ No newline at end of file +} diff --git a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs index aa10734d50..086ac0a972 100644 --- a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -56,13 +56,8 @@ namespace OpenSim.Data.SQLite m_Connection = new SqliteConnection(connectionString); m_Connection.Open(); - using (SqliteConnection dbcon = (SqliteConnection)((ICloneable)m_Connection).Clone()) - { - dbcon.Open(); - Migration m = new Migration(dbcon, GetType().Assembly, "AuthStore"); - m.Update(); - dbcon.Close(); - } + Migration m = new Migration(m_Connection, GetType().Assembly, "AuthStore"); + m.Update(); m_initialized = true; } @@ -113,7 +108,7 @@ namespace OpenSim.Data.SQLite } finally { - CloseCommand(cmd); + //CloseCommand(cmd); } return null; @@ -156,14 +151,14 @@ namespace OpenSim.Data.SQLite { if (ExecuteNonQuery(cmd, m_Connection) < 1) { - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } catch (Exception e) { Console.WriteLine(e.ToString()); - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } @@ -184,19 +179,19 @@ namespace OpenSim.Data.SQLite { if (ExecuteNonQuery(cmd, m_Connection) < 1) { - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } catch (Exception e) { Console.WriteLine(e.ToString()); - CloseCommand(cmd); + //CloseCommand(cmd); return false; } } - CloseCommand(cmd); + //CloseCommand(cmd); return true; } diff --git a/OpenSim/Data/SQLite/SQLiteAvatarData.cs b/OpenSim/Data/SQLite/SQLiteAvatarData.cs index b3f4a4c077..c093884db4 100644 --- a/OpenSim/Data/SQLite/SQLiteAvatarData.cs +++ b/OpenSim/Data/SQLite/SQLiteAvatarData.cs @@ -33,7 +33,7 @@ using System.Threading; using log4net; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -55,8 +55,8 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm); - cmd.Parameters.Add(":PrincipalID", principalID.ToString()); - cmd.Parameters.Add(":Name", name); + cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue(":Name", name); try { @@ -67,7 +67,7 @@ namespace OpenSim.Data.SQLite } finally { - CloseCommand(cmd); + //CloseCommand(cmd); } } } diff --git a/OpenSim/Data/SQLite/SQLiteEstateData.cs b/OpenSim/Data/SQLite/SQLiteEstateData.cs index bd6b776f80..9dd4a2e69b 100644 --- a/OpenSim/Data/SQLite/SQLiteEstateData.cs +++ b/OpenSim/Data/SQLite/SQLiteEstateData.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; @@ -62,8 +62,8 @@ namespace OpenSim.Data.SQLite Migration m = new Migration(m_connection, assem, "EstateStore"); m.Update(); - m_connection.Close(); - m_connection.Open(); + //m_connection.Close(); + // m_connection.Open(); Type t = typeof(EstateSettings); m_Fields = t.GetFields(BindingFlags.NonPublic | @@ -87,7 +87,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.Add(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); return DoLoad(cmd, regionID, create); } @@ -143,13 +143,13 @@ namespace OpenSim.Data.SQLite if (m_FieldMap[name].GetValue(es) is bool) { if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.Add(":"+name, "1"); + cmd.Parameters.AddWithValue(":"+name, "1"); else - cmd.Parameters.Add(":"+name, "0"); + cmd.Parameters.AddWithValue(":"+name, "0"); } else { - cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); } } @@ -167,8 +167,8 @@ namespace OpenSim.Data.SQLite r.Close(); cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.Add(":RegionID", regionID.ToString()); - cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); // This will throw on dupe key try @@ -211,13 +211,13 @@ namespace OpenSim.Data.SQLite if (m_FieldMap[name].GetValue(es) is bool) { if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.Add(":"+name, "1"); + cmd.Parameters.AddWithValue(":"+name, "1"); else - cmd.Parameters.Add(":"+name, "0"); + cmd.Parameters.AddWithValue(":"+name, "0"); } else { - cmd.Parameters.Add(":"+name, m_FieldMap[name].GetValue(es).ToString()); + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); } } @@ -236,7 +236,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", es.EstateID); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID); IDataReader r = cmd.ExecuteReader(); @@ -260,7 +260,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "delete from estateban where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); cmd.ExecuteNonQuery(); @@ -270,8 +270,8 @@ namespace OpenSim.Data.SQLite foreach (EstateBan b in es.EstateBans) { - cmd.Parameters.Add(":EstateID", es.EstateID.ToString()); - cmd.Parameters.Add(":bannedUUID", b.BannedUserID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":bannedUUID", b.BannedUserID.ToString()); cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); @@ -283,7 +283,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "delete from "+table+" where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", EstateID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); cmd.ExecuteNonQuery(); @@ -293,8 +293,8 @@ namespace OpenSim.Data.SQLite foreach (UUID uuid in data) { - cmd.Parameters.Add(":EstateID", EstateID.ToString()); - cmd.Parameters.Add(":uuid", uuid.ToString()); + cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); + cmd.Parameters.AddWithValue(":uuid", uuid.ToString()); cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); @@ -308,7 +308,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID"; - cmd.Parameters.Add(":EstateID", EstateID); + cmd.Parameters.AddWithValue(":EstateID", EstateID); IDataReader r = cmd.ExecuteReader(); @@ -333,7 +333,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.Add(":EstateID", estateID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); return DoLoad(cmd, UUID.Zero, false); } @@ -347,7 +347,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = sql; - cmd.Parameters.Add(":EstateName", search); + cmd.Parameters.AddWithValue(":EstateName", search); IDataReader r = cmd.ExecuteReader(); @@ -365,8 +365,8 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.Add(":RegionID", regionID.ToString()); - cmd.Parameters.Add(":EstateID", estateID.ToString()); + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); if (cmd.ExecuteNonQuery() == 0) return false; diff --git a/OpenSim/Data/SQLite/SQLiteFramework.cs b/OpenSim/Data/SQLite/SQLiteFramework.cs index 20b508515a..cf114d1a07 100644 --- a/OpenSim/Data/SQLite/SQLiteFramework.cs +++ b/OpenSim/Data/SQLite/SQLiteFramework.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -55,11 +55,14 @@ namespace OpenSim.Data.SQLite { lock (connection) { +/* SqliteConnection newConnection = (SqliteConnection)((ICloneable)connection).Clone(); newConnection.Open(); cmd.Connection = newConnection; +*/ + cmd.Connection = connection; //Console.WriteLine("XXX " + cmd.CommandText); return cmd.ExecuteNonQuery(); @@ -70,11 +73,12 @@ namespace OpenSim.Data.SQLite { lock (connection) { - SqliteConnection newConnection = - (SqliteConnection)((ICloneable)connection).Clone(); - newConnection.Open(); + //SqliteConnection newConnection = + // (SqliteConnection)((ICloneable)connection).Clone(); + //newConnection.Open(); - cmd.Connection = newConnection; + //cmd.Connection = newConnection; + cmd.Connection = connection; //Console.WriteLine("XXX " + cmd.CommandText); return cmd.ExecuteReader(); diff --git a/OpenSim/Data/SQLite/SQLiteFriendsData.cs b/OpenSim/Data/SQLite/SQLiteFriendsData.cs index 0b121826d6..b06853ce68 100644 --- a/OpenSim/Data/SQLite/SQLiteFriendsData.cs +++ b/OpenSim/Data/SQLite/SQLiteFriendsData.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; namespace OpenSim.Data.SQLite { @@ -47,7 +47,7 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = :PrincipalID", m_Realm); - cmd.Parameters.Add(":PrincipalID", userID.ToString()); + cmd.Parameters.AddWithValue(":PrincipalID", userID.ToString()); return DoQuery(cmd); @@ -58,8 +58,8 @@ namespace OpenSim.Data.SQLite SqliteCommand cmd = new SqliteCommand(); cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm); - cmd.Parameters.Add(":PrincipalID", principalID.ToString()); - cmd.Parameters.Add(":Friend", friend); + cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue(":Friend", friend); ExecuteNonQuery(cmd, cmd.Connection); diff --git a/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs index b39bb19fb7..3c70aeffc4 100644 --- a/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs +++ b/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; @@ -59,19 +59,21 @@ namespace OpenSim.Data.SQLite if (!m_initialized) { m_Connection = new SqliteConnection(connectionString); + Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString)); m_Connection.Open(); if (storeName != String.Empty) { Assembly assem = GetType().Assembly; - SqliteConnection newConnection = - (SqliteConnection)((ICloneable)m_Connection).Clone(); - newConnection.Open(); + //SqliteConnection newConnection = + // (SqliteConnection)((ICloneable)m_Connection).Clone(); + //newConnection.Open(); - Migration m = new Migration(newConnection, assem, storeName); + //Migration m = new Migration(newConnection, assem, storeName); + Migration m = new Migration(m_Connection, assem, storeName); m.Update(); - newConnection.Close(); - newConnection.Dispose(); + //newConnection.Close(); + //newConnection.Dispose(); } m_initialized = true; @@ -197,7 +199,7 @@ namespace OpenSim.Data.SQLite result.Add(row); } - CloseCommand(cmd); + //CloseCommand(cmd); return result.ToArray(); } diff --git a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs index a5e051726e..ece2495c2b 100644 --- a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs +++ b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Data; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; @@ -98,11 +98,13 @@ namespace OpenSim.Data.SQLite ds.Tables.Add(createInventoryFoldersTable()); invFoldersDa.Fill(ds.Tables["inventoryfolders"]); setupFoldersCommands(invFoldersDa, conn); + CreateDataSetMapping(invFoldersDa, "inventoryfolders"); m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); ds.Tables.Add(createInventoryItemsTable()); invItemsDa.Fill(ds.Tables["inventoryitems"]); setupItemsCommands(invItemsDa, conn); + CreateDataSetMapping(invItemsDa, "inventoryitems"); m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); ds.AcceptChanges(); @@ -728,6 +730,15 @@ namespace OpenSim.Data.SQLite * **********************************************************************/ + protected void CreateDataSetMapping(IDataAdapter da, string tableName) + { + ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); + foreach (DataColumn col in ds.Tables[tableName].Columns) + { + dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); + } + } + /// /// Create the "inventoryitems" table /// diff --git a/OpenSim/Data/SQLite/SQLiteRegionData.cs b/OpenSim/Data/SQLite/SQLiteRegionData.cs index d2ba9ae298..0217748b93 100644 --- a/OpenSim/Data/SQLite/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLite/SQLiteRegionData.cs @@ -32,7 +32,7 @@ using System.Drawing; using System.IO; using System.Reflection; using log4net; -using Mono.Data.SqliteClient; +using Mono.Data.Sqlite; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; @@ -87,119 +87,142 @@ namespace OpenSim.Data.SQLite /// the connection string public void Initialise(string connectionString) { - m_connectionString = connectionString; - - ds = new DataSet(); - - m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); - m_conn = new SqliteConnection(m_connectionString); - m_conn.Open(); - - - - SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); - primDa = new SqliteDataAdapter(primSelectCmd); - // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); - - SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); - shapeDa = new SqliteDataAdapter(shapeSelectCmd); - // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); - - SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); - itemsDa = new SqliteDataAdapter(itemsSelectCmd); - - SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); - terrainDa = new SqliteDataAdapter(terrainSelectCmd); - - SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); - landDa = new SqliteDataAdapter(landSelectCmd); - - SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); - landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); - - SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); - regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); - // This actually does the roll forward assembly stuff - Assembly assem = GetType().Assembly; - Migration m = new Migration(m_conn, assem, "RegionStore"); - m.Update(); - - lock (ds) + try { - ds.Tables.Add(createPrimTable()); - setupPrimCommands(primDa, m_conn); - primDa.Fill(ds.Tables["prims"]); + m_connectionString = connectionString; - ds.Tables.Add(createShapeTable()); - setupShapeCommands(shapeDa, m_conn); + ds = new DataSet("Region"); - ds.Tables.Add(createItemsTable()); - setupItemsCommands(itemsDa, m_conn); - itemsDa.Fill(ds.Tables["primitems"]); + m_log.Info("[REGION DB]: Sqlite - connecting: " + connectionString); + m_conn = new SqliteConnection(m_connectionString); + m_conn.Open(); - ds.Tables.Add(createTerrainTable()); - setupTerrainCommands(terrainDa, m_conn); + SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); + primDa = new SqliteDataAdapter(primSelectCmd); - ds.Tables.Add(createLandTable()); - setupLandCommands(landDa, m_conn); + SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); + shapeDa = new SqliteDataAdapter(shapeSelectCmd); + // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); - ds.Tables.Add(createLandAccessListTable()); - setupLandAccessCommands(landAccessListDa, m_conn); + SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); + itemsDa = new SqliteDataAdapter(itemsSelectCmd); - ds.Tables.Add(createRegionSettingsTable()); - - setupRegionSettingsCommands(regionSettingsDa, m_conn); + SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); + terrainDa = new SqliteDataAdapter(terrainSelectCmd); - // WORKAROUND: This is a work around for sqlite on - // windows, which gets really unhappy with blob columns - // that have no sample data in them. At some point we - // need to actually find a proper way to handle this. - try + SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); + landDa = new SqliteDataAdapter(landSelectCmd); + + SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); + landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); + + SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); + regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); + // This actually does the roll forward assembly stuff + Assembly assem = GetType().Assembly; + Migration m = new Migration(m_conn, assem, "RegionStore"); + m.Update(); + + lock (ds) { - shapeDa.Fill(ds.Tables["primshapes"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on primshapes table"); - } + ds.Tables.Add(createPrimTable()); + setupPrimCommands(primDa, m_conn); - try - { - terrainDa.Fill(ds.Tables["terrain"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on terrain table"); - } + ds.Tables.Add(createShapeTable()); + setupShapeCommands(shapeDa, m_conn); - try - { - landDa.Fill(ds.Tables["land"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on land table"); - } + ds.Tables.Add(createItemsTable()); + setupItemsCommands(itemsDa, m_conn); - try - { - landAccessListDa.Fill(ds.Tables["landaccesslist"]); - } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); - } + ds.Tables.Add(createTerrainTable()); + setupTerrainCommands(terrainDa, m_conn); - try - { - regionSettingsDa.Fill(ds.Tables["regionsettings"]); + ds.Tables.Add(createLandTable()); + setupLandCommands(landDa, m_conn); + + ds.Tables.Add(createLandAccessListTable()); + setupLandAccessCommands(landAccessListDa, m_conn); + + ds.Tables.Add(createRegionSettingsTable()); + setupRegionSettingsCommands(regionSettingsDa, m_conn); + + // WORKAROUND: This is a work around for sqlite on + // windows, which gets really unhappy with blob columns + // that have no sample data in them. At some point we + // need to actually find a proper way to handle this. + try + { + primDa.Fill(ds.Tables["prims"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on prims table"); + } + + try + { + shapeDa.Fill(ds.Tables["primshapes"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on primshapes table"); + } + + try + { + terrainDa.Fill(ds.Tables["terrain"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on terrain table"); + } + + try + { + landDa.Fill(ds.Tables["land"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on land table"); + } + + try + { + landAccessListDa.Fill(ds.Tables["landaccesslist"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on landaccesslist table"); + } + + try + { + regionSettingsDa.Fill(ds.Tables["regionsettings"]); + } + catch (Exception) + { + m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); + } + + // We have to create a data set mapping for every table, otherwise the IDataAdaptor.Update() will not populate rows with values! + // Not sure exactly why this is - this kind of thing was not necessary before - justincc 20100409 + // Possibly because we manually set up our own DataTables before connecting to the database + CreateDataSetMapping(primDa, "prims"); + CreateDataSetMapping(shapeDa, "primshapes"); + CreateDataSetMapping(itemsDa, "primitems"); + CreateDataSetMapping(terrainDa, "terrain"); + CreateDataSetMapping(landDa, "land"); + CreateDataSetMapping(landAccessListDa, "landaccesslist"); + CreateDataSetMapping(regionSettingsDa, "regionsettings"); } - catch (Exception) - { - m_log.Info("[REGION DB]: Caught fill error on regionsettings table"); - } - return; } + catch (Exception e) + { + m_log.Error(e); + Environment.Exit(23); + } + + return; } public void Dispose() @@ -603,7 +626,7 @@ namespace OpenSim.Data.SQLite } } } - rev = (int) row["Revision"]; + rev = Convert.ToInt32(row["Revision"]); } else { @@ -755,6 +778,7 @@ namespace OpenSim.Data.SQLite ///
/// /// true if the item was successfully updated - public override bool UpdateItem(InventoryItemBase item) + public bool UpdateItem(InventoryItemBase item) { return m_InventoryService.UpdateItem(item); } - public override bool MoveItems(UUID ownerID, List items) + public bool MoveItems(UUID ownerID, List items) { return m_InventoryService.MoveItems(ownerID, items); } @@ -302,12 +247,12 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory ///
/// /// true if the item was successfully deleted - public override bool DeleteItems(UUID ownerID, List itemIDs) + public bool DeleteItems(UUID ownerID, List itemIDs) { return m_InventoryService.DeleteItems(ownerID, itemIDs); } - public override InventoryItemBase GetItem(InventoryItemBase item) + public InventoryItemBase GetItem(InventoryItemBase item) { // m_log.DebugFormat("[LOCAL INVENTORY SERVICES CONNECTOR]: Requesting inventory item {0}", item.ID); @@ -322,7 +267,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory return item; } - public override InventoryFolderBase GetFolder(InventoryFolderBase folder) + public InventoryFolderBase GetFolder(InventoryFolderBase folder) { return m_InventoryService.GetFolder(folder); } @@ -332,17 +277,17 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory ///
/// /// - public override bool HasInventoryForUser(UUID userID) + public bool HasInventoryForUser(UUID userID) { return m_InventoryService.HasInventoryForUser(userID); } - public override List GetActiveGestures(UUID userId) + public List GetActiveGestures(UUID userId) { return m_InventoryService.GetActiveGestures(userId); } - public override int GetAssetPermissions(UUID userID, UUID assetID) + public int GetAssetPermissions(UUID userID, UUID assetID) { return m_InventoryService.GetAssetPermissions(userID, assetID); } diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index 5e3f9a75ef..489b66f5a5 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -11,7 +11,7 @@ ;; [Startup] -ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:InventoryServiceInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8003/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector,8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector,HGInventoryService@8002/OpenSim.Server.Handlers.dll:HGInventoryServiceInConnector,8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" +ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryServiceInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8003/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector,8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector,HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector,8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none if specified above @@ -44,8 +44,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; * the function of the legacy inventory server ; * [InventoryService] - LocalServiceModule = "OpenSim.Services.InventoryService.dll:InventoryService" - SessionAuthentication = "false" + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" ; * This is the new style grid service. ; * "Realm" is the table that is used for user lookup. @@ -92,7 +91,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" [PresenceService] ; for the server connector @@ -116,7 +115,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; for the service UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 9bedac6699..cfc08bc7dc 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -11,7 +11,7 @@ ; * [Startup] -ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:InventoryServiceInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" +ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none if specified above @@ -45,8 +45,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; * the function of the legacy inventory server ; * [InventoryService] - LocalServiceModule = "OpenSim.Services.InventoryService.dll:InventoryService" - SessionAuthentication = "false" + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" ; * This is the new style grid service. ; * "Realm" is the table that is used for user lookup. @@ -94,7 +93,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" [PresenceService] ; for the server connector @@ -118,7 +117,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 ; for the service UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" diff --git a/bin/config-include/Grid.ini b/bin/config-include/Grid.ini index 9dff325749..4767cbc847 100644 --- a/bin/config-include/Grid.ini +++ b/bin/config-include/Grid.ini @@ -9,7 +9,7 @@ [Modules] AssetServices = "RemoteAssetServicesConnector" - InventoryServices = "RemoteInventoryServicesConnector" + InventoryServices = "RemoteXInventoryServicesConnector" GridServices = "RemoteGridServicesConnector" AvatarServices = "RemoteAvatarServicesConnector" NeighbourServices = "RemoteNeighbourServicesConnector" diff --git a/bin/config-include/GridHypergrid.ini b/bin/config-include/GridHypergrid.ini index b567817cc3..051a87ec45 100644 --- a/bin/config-include/GridHypergrid.ini +++ b/bin/config-include/GridHypergrid.ini @@ -30,8 +30,7 @@ HypergridAssetService = "OpenSim.Services.Connectors.dll:HGAssetServiceConnector" [InventoryService] - LocalGridInventoryService = "OpenSim.Region.CoreModules.dll:RemoteInventoryServicesConnector" - HypergridInventoryService = "OpenSim.Services.Connectors.dll:HGInventoryServiceConnector" + LocalGridInventoryService = "OpenSim.Region.CoreModules.dll:RemoteXInventoryServicesConnector" [GridService] ; RemoteGridServicesConnector instantiates a LocalGridServicesConnector, diff --git a/bin/config-include/Standalone.ini b/bin/config-include/Standalone.ini index 92c215498e..027b4ea66e 100644 --- a/bin/config-include/Standalone.ini +++ b/bin/config-include/Standalone.ini @@ -24,7 +24,7 @@ LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" [InventoryService] - LocalServiceModule = "OpenSim.Services.InventoryService.dll:InventoryService" + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" [LibraryService] LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" @@ -56,7 +56,7 @@ AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" [GridUserService] LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" @@ -71,7 +71,7 @@ LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index 1d8e2efe72..35e6f207eb 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -33,11 +33,10 @@ HypergridAssetService = "OpenSim.Services.Connectors.dll:HGAssetServiceConnector" [InventoryService] - LocalServiceModule = "OpenSim.Services.InventoryService.dll:InventoryService" + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" ; For HGInventoryBroker - LocalGridInventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" - HypergridInventoryService = "OpenSim.Services.Connectors.dll:HGInventoryServiceConnector" + LocalGridInventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" [AvatarService] LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" @@ -74,7 +73,7 @@ AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" [FriendsService] LocalServiceModule = "OpenSim.Services.FriendsService.dll" @@ -88,7 +87,7 @@ UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" GridService = "OpenSim.Services.GridService.dll:GridService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" diff --git a/bin/config-include/storage/SQLiteStandalone.ini b/bin/config-include/storage/SQLiteStandalone.ini index 1ce03578f2..ed88a8a182 100644 --- a/bin/config-include/storage/SQLiteStandalone.ini +++ b/bin/config-include/storage/SQLiteStandalone.ini @@ -3,6 +3,11 @@ [DatabaseService] StorageProvider = "OpenSim.Data.SQLite.dll" +[InventoryService] + ;ConnectionString = "URI=file:inventory.db,version=3" + ; if you have a legacy inventory store use the connection string below + ConnectionString = "URI=file:inventory.db,version=3,UseUTF16Encoding=True" + [AvatarService] ConnectionString = "URI=file:avatars.db,version=3" @@ -14,3 +19,4 @@ [FriendsService] ConnectionString = "URI=file:friends.db,version=3" + From 052580ef44e733138cd4dbf6cfe30acc476d90fc Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 2 May 2010 10:32:47 -0700 Subject: [PATCH 053/260] Deleted HGInventoryBroker, so that the new one can take its name. --- .../Inventory/HGInventoryBroker.cs | 540 ------------------ 1 file changed, 540 deletions(-) delete mode 100644 OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs deleted file mode 100644 index 54508ccfa8..0000000000 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs +++ /dev/null @@ -1,540 +0,0 @@ -/* - * 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 OpenSimulator 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 log4net; -using Nini.Config; -using System; -using System.Collections.Generic; -using System.Reflection; -using OpenSim.Framework; - -using OpenSim.Server.Base; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Services.Interfaces; -using OpenSim.Services.Connectors; -using OpenMetaverse; - -namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory -{ - public class HGInventoryBroker : BaseInventoryConnector, INonSharedRegionModule, IInventoryService - { - private static readonly ILog m_log = - LogManager.GetLogger( - MethodBase.GetCurrentMethod().DeclaringType); - - private static bool m_Initialized = false; - private static bool m_Enabled = false; - - private static IInventoryService m_GridService; - private static ISessionAuthInventoryService m_HGService; - - private Scene m_Scene; - private IUserAccountService m_UserAccountService; - - public Type ReplaceableInterface - { - get { return null; } - } - - public string Name - { - get { return "HGInventoryBroker"; } - } - - public void Initialise(IConfigSource source) - { - if (!m_Initialized) - { - IConfig moduleConfig = source.Configs["Modules"]; - if (moduleConfig != null) - { - string name = moduleConfig.GetString("InventoryServices", ""); - if (name == Name) - { - IConfig inventoryConfig = source.Configs["InventoryService"]; - if (inventoryConfig == null) - { - m_log.Error("[HG INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); - return; - } - - string localDll = inventoryConfig.GetString("LocalGridInventoryService", - String.Empty); - string HGDll = inventoryConfig.GetString("HypergridInventoryService", - String.Empty); - - if (localDll == String.Empty) - { - m_log.Error("[HG INVENTORY CONNECTOR]: No LocalGridInventoryService named in section InventoryService"); - //return; - throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); - } - - if (HGDll == String.Empty) - { - m_log.Error("[HG INVENTORY CONNECTOR]: No HypergridInventoryService named in section InventoryService"); - //return; - throw new Exception("Unable to proceed. Please make sure your ini files in config-include are updated according to .example's"); - } - - Object[] args = new Object[] { source }; - m_GridService = - ServerUtils.LoadPlugin(localDll, - args); - - m_HGService = - ServerUtils.LoadPlugin(HGDll, - args); - - if (m_GridService == null) - { - m_log.Error("[HG INVENTORY CONNECTOR]: Can't load local inventory service"); - return; - } - if (m_HGService == null) - { - m_log.Error("[HG INVENTORY CONNECTOR]: Can't load hypergrid inventory service"); - return; - } - - Init(source); - - m_Enabled = true; - m_log.Info("[HG INVENTORY CONNECTOR]: HG inventory broker enabled"); - } - } - m_Initialized = true; - } - } - - public void PostInitialise() - { - } - - public void Close() - { - } - - public void AddRegion(Scene scene) - { - if (!m_Enabled) - return; - - m_Scene = scene; - m_UserAccountService = m_Scene.UserAccountService; - - scene.RegisterModuleInterface(this); - m_cache.AddRegion(scene); - } - - public void RemoveRegion(Scene scene) - { - if (!m_Enabled) - return; - - m_cache.RemoveRegion(scene); - } - - public void RegionLoaded(Scene scene) - { - if (!m_Enabled) - return; - - m_log.InfoFormat("[HG INVENTORY CONNECTOR]: Enabled HG inventory for region {0}", scene.RegionInfo.RegionName); - - } - - #region IInventoryService - - public override bool CreateUserInventory(UUID userID) - { - return m_GridService.CreateUserInventory(userID); - } - - public override List GetInventorySkeleton(UUID userId) - { - return m_GridService.GetInventorySkeleton(userId); - } - - public override InventoryCollection GetUserInventory(UUID userID) - { - return null; - } - - public override void GetUserInventory(UUID userID, InventoryReceiptCallback callback) - { - } - - // Inherited. See base - //public override InventoryFolderBase GetFolderForType(UUID userID, AssetType type) - //{ - // if (IsLocalGridUser(userID)) - // return m_GridService.GetFolderForType(userID, type); - // else - // { - // UUID sessionID = GetSessionID(userID); - // string uri = GetUserInventoryURI(userID) + "/" + userID.ToString(); - // // !!!!!! - // return null; - // //return m_HGService.GetFolderForType(uri, sessionID, type); - // } - //} - - public override InventoryCollection GetFolderContent(UUID userID, UUID folderID) - { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) - return m_GridService.GetFolderContent(userID, folderID); - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetFolderContent(uri, folderID, sessionID); - } - } - - public override Dictionary GetSystemFolders(UUID userID) - { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) - { - // This is not pretty, but it will have to do for now - if (m_GridService is BaseInventoryConnector) - { - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetSystemsFolders redirected to RemoteInventoryServiceConnector module"); - return ((BaseInventoryConnector)m_GridService).GetSystemFolders(userID); - } - else - { - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetSystemsFolders redirected to GetSystemFoldersLocal"); - return GetSystemFoldersLocal(userID); - } - } - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetSystemFolders(uri, sessionID); - } - } - - private Dictionary GetSystemFoldersLocal(UUID userID) - { - InventoryFolderBase root = m_GridService.GetRootFolder(userID); - if (root != null) - { - InventoryCollection content = m_GridService.GetFolderContent(userID, root.ID); - if (content != null) - { - Dictionary folders = new Dictionary(); - foreach (InventoryFolderBase folder in content.Folders) - { - //m_log.DebugFormat("[HG INVENTORY CONNECTOR]: scanning folder type {0}", (AssetType)folder.Type); - if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) - folders[(AssetType)folder.Type] = folder; - } - // Put the root folder there, as type Folder - folders[AssetType.Folder] = root; - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: System folders count for {0}: {1}", userID, folders.Count); - return folders; - } - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Root folder content not found for {0}", userID); - - } - - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Root folder not found for {0}", userID); - - return new Dictionary(); - } - - public override List GetFolderItems(UUID userID, UUID folderID) - { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) - return m_GridService.GetFolderItems(userID, folderID); - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetFolderItems(uri, folderID, sessionID); - } - } - - public override bool AddFolder(InventoryFolderBase folder) - { - if (folder == null) - return false; - - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) - return m_GridService.AddFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.AddFolder(uri, folder, sessionID); - } - } - - public override bool UpdateFolder(InventoryFolderBase folder) - { - if (folder == null) - return false; - - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) - return m_GridService.UpdateFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.UpdateFolder(uri, folder, sessionID); - } - } - - public override bool DeleteFolders(UUID ownerID, List folderIDs) - { - if (folderIDs == null) - return false; - if (folderIDs.Count == 0) - return false; - - string uri = string.Empty; - if (!IsForeignUser(ownerID, out uri)) - return m_GridService.DeleteFolders(ownerID, folderIDs); - else - { - UUID sessionID = GetSessionID(ownerID); - uri = uri + "/" + ownerID.ToString(); - return m_HGService.DeleteFolders(uri, folderIDs, sessionID); - } - } - - public override bool MoveFolder(InventoryFolderBase folder) - { - if (folder == null) - return false; - - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) - return m_GridService.MoveFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.MoveFolder(uri, folder, sessionID); - } - } - - public override bool PurgeFolder(InventoryFolderBase folder) - { - if (folder == null) - return false; - - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) - return m_GridService.PurgeFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.PurgeFolder(uri, folder, sessionID); - } - } - - // public bool AddItem(InventoryItemBase item) inherited - // Uses AddItemPlain - - protected override bool AddItemPlain(InventoryItemBase item) - { - if (item == null) - return false; - - string uri = string.Empty; - if (!IsForeignUser(item.Owner, out uri)) - { - return m_GridService.AddItem(item); - } - else - { - UUID sessionID = GetSessionID(item.Owner); - uri = uri + "/" + item.Owner.ToString(); - return m_HGService.AddItem(uri, item, sessionID); - } - } - - public override bool UpdateItem(InventoryItemBase item) - { - if (item == null) - return false; - - string uri = string.Empty; - if (!IsForeignUser(item.Owner, out uri)) - return m_GridService.UpdateItem(item); - else - { - UUID sessionID = GetSessionID(item.Owner); - uri = uri + "/" + item.Owner.ToString(); - return m_HGService.UpdateItem(uri, item, sessionID); - } - } - - public override bool MoveItems(UUID ownerID, List items) - { - if (items == null) - return false; - if (items.Count == 0) - return true; - - string uri = string.Empty; - if (!IsForeignUser(ownerID, out uri)) - return m_GridService.MoveItems(ownerID, items); - else - { - UUID sessionID = GetSessionID(ownerID); - uri = uri + "/" + ownerID.ToString(); - return m_HGService.MoveItems(uri, items, sessionID); - } - } - - public override bool DeleteItems(UUID ownerID, List itemIDs) - { - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: Delete {0} items for user {1}", itemIDs.Count, ownerID); - - if (itemIDs == null) - return false; - if (itemIDs.Count == 0) - return true; - - string uri = string.Empty; - if (!IsForeignUser(ownerID, out uri)) - return m_GridService.DeleteItems(ownerID, itemIDs); - else - { - UUID sessionID = GetSessionID(ownerID); - uri = uri + "/" + ownerID.ToString(); - return m_HGService.DeleteItems(uri, itemIDs, sessionID); - } - } - - public override InventoryItemBase GetItem(InventoryItemBase item) - { - if (item == null) - return null; - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: GetItem {0} for user {1}", item.ID, item.Owner); - string uri = string.Empty; - if (!IsForeignUser(item.Owner, out uri)) - return m_GridService.GetItem(item); - else - { - UUID sessionID = GetSessionID(item.Owner); - uri = uri + "/" + item.Owner.ToString(); - return m_HGService.QueryItem(uri, item, sessionID); - } - } - - public override InventoryFolderBase GetFolder(InventoryFolderBase folder) - { - if (folder == null) - return null; - - string uri = string.Empty; - if (!IsForeignUser(folder.Owner, out uri)) - return m_GridService.GetFolder(folder); - else - { - UUID sessionID = GetSessionID(folder.Owner); - uri = uri + "/" + folder.Owner.ToString(); - return m_HGService.QueryFolder(uri, folder, sessionID); - } - } - - public override bool HasInventoryForUser(UUID userID) - { - return false; - } - - public override List GetActiveGestures(UUID userId) - { - return new List(); - } - - public override int GetAssetPermissions(UUID userID, UUID assetID) - { - string uri = string.Empty; - if (!IsForeignUser(userID, out uri)) - return m_GridService.GetAssetPermissions(userID, assetID); - else - { - UUID sessionID = GetSessionID(userID); - uri = uri + "/" + userID.ToString(); - return m_HGService.GetAssetPermissions(uri, assetID, sessionID); - } - } - - #endregion - - private UUID GetSessionID(UUID userID) - { - ScenePresence sp = null; - if (m_Scene.TryGetScenePresence(userID, out sp)) - { - return sp.ControllingClient.SessionId; - } - - m_log.DebugFormat("[HG INVENTORY CONNECTOR]: scene presence for {0} not found", userID); - return UUID.Zero; - } - - private bool IsForeignUser(UUID userID, out string inventoryURL) - { - inventoryURL = string.Empty; - UserAccount account = null; - if (m_Scene.UserAccountService != null) - account = m_Scene.UserAccountService.GetUserAccount(m_Scene.RegionInfo.ScopeID, userID); - - if (account == null) // foreign user - { - ScenePresence sp = null; - m_Scene.TryGetScenePresence(userID, out sp); - if (sp != null) - { - AgentCircuitData aCircuit = m_Scene.AuthenticateHandler.GetAgentCircuitData(sp.ControllingClient.CircuitCode); - if (aCircuit.ServiceURLs.ContainsKey("InventoryServerURI")) - { - inventoryURL = aCircuit.ServiceURLs["InventoryServerURI"].ToString(); - inventoryURL = inventoryURL.Trim(new char[] { '/' }); - return true; - } - } - } - return false; - } - - } -} From cbb297bc79ad0ecaad28ac968e40afe5a3552b2f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 2 May 2010 10:37:57 -0700 Subject: [PATCH 054/260] Renamed HGInventoryBroker2 to HGInventoryBroker. --- .../Region/CoreModules/Resources/CoreModulePlugin.addin.xml | 1 - .../Inventory/{HGInventoryBroker2.cs => HGInventoryBroker.cs} | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) rename OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/{HGInventoryBroker2.cs => HGInventoryBroker.cs} (99%) diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml index c7382965d6..0a5ff3f982 100644 --- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml +++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml @@ -47,7 +47,6 @@ - diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs similarity index 99% rename from OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs rename to OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs index fc3393f53d..e09db154aa 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker2.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/HGInventoryBroker.cs @@ -41,7 +41,7 @@ using OpenMetaverse; namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory { - public class HGInventoryBroker2 : ISharedRegionModule, IInventoryService + public class HGInventoryBroker : ISharedRegionModule, IInventoryService { private static readonly ILog m_log = LogManager.GetLogger( @@ -64,7 +64,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory public string Name { - get { return "HGInventoryBroker2"; } + get { return "HGInventoryBroker"; } } public void Initialise(IConfigSource source) From 7e7429117614b425512d67c9d5696a69248d0536 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 3 May 2010 01:50:32 +0200 Subject: [PATCH 055/260] Make the IUserAccountData properly unpack the god mode data, so grid gods work again --- .../Connectors/UserAccounts/UserAccountServiceConnector.cs | 2 +- OpenSim/Services/Interfaces/IUserAccountService.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs b/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs index 1527db20df..38c191a913 100644 --- a/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs +++ b/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs @@ -113,7 +113,7 @@ namespace OpenSim.Services.Connectors public virtual UserAccount GetUserAccount(UUID scopeID, UUID userID) { - m_log.DebugFormat("[ACCOUNTS CONNECTOR]: GetUSerAccount {0}", userID); + m_log.DebugFormat("[ACCOUNTS CONNECTOR]: GetUserAccount {0}", userID); Dictionary sendData = new Dictionary(); //sendData["SCOPEID"] = scopeID.ToString(); sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); diff --git a/OpenSim/Services/Interfaces/IUserAccountService.cs b/OpenSim/Services/Interfaces/IUserAccountService.cs index befd14e02a..e316731a52 100644 --- a/OpenSim/Services/Interfaces/IUserAccountService.cs +++ b/OpenSim/Services/Interfaces/IUserAccountService.cs @@ -84,11 +84,11 @@ namespace OpenSim.Services.Interfaces if (kvp.ContainsKey("ScopeID")) UUID.TryParse(kvp["ScopeID"].ToString(), out ScopeID); if (kvp.ContainsKey("UserLevel")) - Convert.ToInt32(kvp["UserLevel"].ToString()); + UserLevel = Convert.ToInt32(kvp["UserLevel"].ToString()); if (kvp.ContainsKey("UserFlags")) - Convert.ToInt32(kvp["UserFlags"].ToString()); + UserFlags = Convert.ToInt32(kvp["UserFlags"].ToString()); if (kvp.ContainsKey("UserTitle")) - Email = kvp["UserTitle"].ToString(); + UserTitle = kvp["UserTitle"].ToString(); if (kvp.ContainsKey("Created")) Convert.ToInt32(kvp["Created"].ToString()); From d9910d2a444537216630024ff464d56b2b77e5dd Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 00:08:28 +0100 Subject: [PATCH 056/260] test commit --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index a004cad48e..570f7328af 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) Contributors, http://opensimulator.org/ +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 From 02f9b3ac2da0911d558ccd97523d9b274a4508c5 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 00:09:47 +0100 Subject: [PATCH 057/260] And again --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 570f7328af..a004cad48e 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) Contributors, http://opensimulator.org/ +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 From fbd422253385d6d198d9eb041e56ba9c0b0bce12 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 00:11:12 +0100 Subject: [PATCH 058/260] Last time? --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index a004cad48e..570f7328af 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) Contributors, http://opensimulator.org/ +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 From af0ffb2a5aa3bd2889f9aa45730ea71d68763425 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 00:15:55 +0100 Subject: [PATCH 059/260] Add URL_REQUEST_* script constants --- OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs index ee35fa4b6a..dba6502f4b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs @@ -548,5 +548,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase public const int STATS_ACTIVE_SCRIPTS = 19; public const int STATS_SCRIPT_LPS = 20; + public const string URL_REQUEST_GRANTED = "URL_REQUEST_GRANTED"; + public const string URL_REQUEST_DENIED = "URL_REQUEST_DENIED"; } } From 18f6714451d0b351fc2145d8600b9fd78696a2bf Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sun, 2 May 2010 19:56:40 -0400 Subject: [PATCH 060/260] Modify README.txt to trigger new build --- README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.txt b/README.txt index f1a71beadf..2bf28cec69 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ Welcome to OpenSim! -== OVERVIEW == +=== OVERVIEW === OpenSim is a BSD Licensed Open Source project to develop a functioning virtual worlds server platform capable of supporting multiple clients From 74d63d4da2e0e8b7367a44787cda31c2d5b59c6b Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sun, 2 May 2010 20:08:19 -0400 Subject: [PATCH 061/260] Modify README.txt to trigger new build /again/ --- BUILDING.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDING.txt b/BUILDING.txt index 03fb482226..972fe4696c 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -1,4 +1,4 @@ -== Building OpenSim == +=== Building OpenSim === === Building on Windows === From 45301d8a4984b0a6e5e5df4741175ce81729810d Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 01:14:38 +0100 Subject: [PATCH 062/260] Defer sending of CHANGED_OWNER to make it work on rezzed objects and attachments in addition to objects sold in place --- OpenSim/Framework/TaskInventoryItem.cs | 11 +++++++++++ .../Framework/Scenes/SceneObjectPartInventory.cs | 7 ++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/OpenSim/Framework/TaskInventoryItem.cs b/OpenSim/Framework/TaskInventoryItem.cs index 2b0096bd46..2cb78957f5 100644 --- a/OpenSim/Framework/TaskInventoryItem.cs +++ b/OpenSim/Framework/TaskInventoryItem.cs @@ -122,6 +122,8 @@ namespace OpenSim.Framework private int _type = 0; private UUID _oldID; + private bool _ownerChanged = false; + public UUID AssetID { get { return _assetID; @@ -320,6 +322,15 @@ namespace OpenSim.Framework } } + public bool OwnerChanged { + get { + return _ownerChanged; + } + set { + _ownerChanged = value; + } + } + // See ICloneable #region ICloneable Members diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 4da63c0187..eea73ff5eb 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -955,10 +955,9 @@ namespace OpenSim.Region.Framework.Scenes item.CurrentPermissions &= item.NextPermissions; item.BasePermissions &= item.NextPermissions; item.EveryonePermissions &= item.NextPermissions; + item.OwnerChanged = true; } } - - m_part.TriggerScriptChangedEvent(Changed.OWNER); } public void ApplyGodPermissions(uint perms) @@ -1042,7 +1041,6 @@ namespace OpenSim.Region.Framework.Scenes if (engines == null) return; - lock (m_items) { foreach (TaskInventoryItem item in m_items.Values) @@ -1052,7 +1050,10 @@ namespace OpenSim.Region.Framework.Scenes foreach (IScriptModule engine in engines) { if (engine != null) + { + engine.PostScriptEvent(item.ItemID, "changed", new Object[] { Changed.OWNER }); engine.ResumeScript(item.ItemID); + } } } } From 7a8ad1ceb23b768863a05997d3c0397dc301a323 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 01:30:57 +0100 Subject: [PATCH 063/260] Make in-place sale send CHANGED_OWNER again --- OpenSim/Region/Framework/Scenes/Scene.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 61a2956ed8..3e4694a73a 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4522,6 +4522,7 @@ namespace OpenSim.Region.Framework.Scenes foreach (SceneObjectPart child in partList) { child.Inventory.ChangeInventoryOwner(remoteClient.AgentId); + child.TriggerScriptChangedEvent(Changed.OWNER); child.ApplyNextOwnerPermissions(); } } @@ -4531,6 +4532,8 @@ namespace OpenSim.Region.Framework.Scenes group.HasGroupChanged = true; part.GetProperties(remoteClient); + part.TriggerScriptChangedEvent(Changed.OWNER); + group.ResumeScripts(); part.ScheduleFullUpdate(); break; From b8362499263a7034cc66cbacec2a2cf6a480b7eb Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sun, 2 May 2010 20:44:34 -0400 Subject: [PATCH 064/260] * Untested Suggestion from lkalif to change remoteClient.SendMapBlock(blocks, 0); to remoteClient.SendMapBlock(blocks, 2); in OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs --- OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs index 56b50dc9ca..a1a4f9e125 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs @@ -135,7 +135,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap data.Y = 0; blocks.Add(data); - remoteClient.SendMapBlock(blocks, 0); + remoteClient.SendMapBlock(blocks, 2); } private Scene GetClientScene(IClientAPI client) From 7b80060df1559df4f91d499a9e2374412aa67e91 Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sun, 2 May 2010 21:00:20 -0400 Subject: [PATCH 065/260] * Another one of those super useful commit tests --- README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.txt b/README.txt index 2bf28cec69..f1a71beadf 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ Welcome to OpenSim! -=== OVERVIEW === +== OVERVIEW == OpenSim is a BSD Licensed Open Source project to develop a functioning virtual worlds server platform capable of supporting multiple clients From 1494c84f56221ae264c3bba3dd5a23510bc43aea Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 3 May 2010 02:40:52 +0100 Subject: [PATCH 066/260] Fix a bug in owner change notification --- OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index eea73ff5eb..8b83b0660c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -1051,7 +1051,9 @@ namespace OpenSim.Region.Framework.Scenes { if (engine != null) { - engine.PostScriptEvent(item.ItemID, "changed", new Object[] { Changed.OWNER }); + if (item.OwnerChanged) + engine.PostScriptEvent(item.ItemID, "changed", new Object[] { (int)Changed.OWNER }); + item.OwnerChanged = false; engine.ResumeScript(item.ItemID); } } From b10811a13b8fab81ce00d544d8efe081792bdaaa Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 3 May 2010 09:50:55 -0700 Subject: [PATCH 067/260] Assorted bug fixes in hypergrid linking. --- .../HypergridServiceInConnectorModule.cs | 2 +- .../Grid/RemoteGridServiceConnector.cs | 5 ++ .../Hypergrid/GatekeeperServerConnector.cs | 1 - .../Handlers/Hypergrid/HypergridHandlers.cs | 3 ++ .../Connectors/Grid/GridServiceConnector.cs | 5 +- OpenSim/Services/GridService/GridService.cs | 5 +- .../Services/GridService/HypergridLinker.cs | 54 ++++++++++--------- .../HypergridService/GatekeeperService.cs | 3 +- 8 files changed, 46 insertions(+), 32 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs index c6848bb394..235914a0b0 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsIn/Hypergrid/HypergridServiceInConnectorModule.cs @@ -113,10 +113,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Hypergrid ISimulationService simService = scene.RequestModuleInterface(); m_HypergridHandler = new GatekeeperServiceInConnector(m_Config, MainServer.Instance, simService); - scene.RegisterModuleInterface(m_HypergridHandler.GateKeeper); new UserAgentServerConnector(m_Config, MainServer.Instance); } + scene.RegisterModuleInterface(m_HypergridHandler.GateKeeper); } #endregion diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs index 2c234d274e..d44ddf4b1a 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs @@ -191,10 +191,15 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid public override List GetRegionsByName(UUID scopeID, string name, int maxNumber) { List rinfo = m_LocalGridService.GetRegionsByName(scopeID, name, maxNumber); + //m_log.DebugFormat("[REMOTE GRID CONNECTOR]: Local GetRegionsByName {0} found {1} regions", name, rinfo.Count); List grinfo = base.GetRegionsByName(scopeID, name, maxNumber); if (grinfo != null) + { + //m_log.DebugFormat("[REMOTE GRID CONNECTOR]: Remote GetRegionsByName {0} found {1} regions", name, grinfo.Count); rinfo.AddRange(grinfo); + } + return rinfo; } diff --git a/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs index f2d9321c3f..dcb27257a0 100644 --- a/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs +++ b/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs @@ -70,7 +70,6 @@ namespace OpenSim.Server.Handlers.Hypergrid server.AddXmlRPCHandler("get_region", hghandlers.GetRegion, false); server.AddHTTPHandler("/foreignagent/", new GatekeeperAgentHandler(m_GatekeeperService).Handler); - } public GatekeeperServiceInConnector(IConfigSource config, IHttpServer server) diff --git a/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs index 0b65245896..5d03097b32 100644 --- a/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs +++ b/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs @@ -48,6 +48,7 @@ namespace OpenSim.Server.Handlers.Hypergrid public HypergridHandlers(IGatekeeperService gatekeeper) { m_GatekeeperService = gatekeeper; + m_log.DebugFormat("[HYPERGRID HANDLERS]: Active"); } /// @@ -61,6 +62,8 @@ namespace OpenSim.Server.Handlers.Hypergrid //string host = (string)requestData["host"]; //string portstr = (string)requestData["port"]; string name = (string)requestData["region_name"]; + if (name == null) + name = string.Empty; UUID regionID = UUID.Zero; string externalName = string.Empty; diff --git a/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs b/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs index a453d99721..0ec8912abd 100644 --- a/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs +++ b/OpenSim/Services/Connectors/Grid/GridServiceConnector.cs @@ -300,7 +300,7 @@ namespace OpenSim.Services.Connectors if (replyData["result"] is Dictionary) rinfo = new GridRegion((Dictionary)replyData["result"]); else - m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByPosition {0}, {1}-{2} received invalid response", + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByPosition {0}, {1}-{2} received no region", scopeID, x, y); } else @@ -391,9 +391,6 @@ namespace OpenSim.Services.Connectors GridRegion rinfo = new GridRegion((Dictionary)r); rinfos.Add(rinfo); } - else - m_log.DebugFormat("[GRID CONNECTOR]: GetRegionsByName {0}, {1}, {2} received invalid response", - scopeID, name, maxNumber); } } else diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index 2faf018e72..4089fcee18 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -315,6 +315,8 @@ namespace OpenSim.Services.GridService public List GetRegionsByName(UUID scopeID, string name, int maxNumber) { + m_log.DebugFormat("[GRID SERVICE]: GetRegionsByName {0}", name); + List rdatas = m_Database.Get("%" + name + "%", scopeID); int count = 0; @@ -329,7 +331,7 @@ namespace OpenSim.Services.GridService } } - if (m_AllowHypergridMapSearch && rdatas == null || (rdatas != null && rdatas.Count == 0) && name.Contains(".")) + if (m_AllowHypergridMapSearch && (rdatas == null || (rdatas != null && rdatas.Count == 0) && name.Contains("."))) { GridRegion r = m_HypergridLinker.LinkRegion(scopeID, name); if (r != null) @@ -397,6 +399,7 @@ namespace OpenSim.Services.GridService ret.Add(RegionData2RegionInfo(r)); } + m_log.DebugFormat("[GRID SERVICE]: GetDefaultRegions returning {0} regions", ret.Count); return ret; } diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index 58746d0a1b..af603b23e2 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -79,9 +79,16 @@ namespace OpenSim.Services.GridService m_DefaultRegion = defs[0]; else { - // Best guess, may be totally off - m_DefaultRegion = new GridRegion(1000, 1000); - m_log.WarnFormat("[HYPERGRID LINKER]: This grid does not have a default region. Assuming default coordinates at 1000, 1000."); + // Get any region + defs = m_GridService.GetRegionsByName(m_ScopeID, "", 1); + if (defs != null && defs.Count > 0) + m_DefaultRegion = defs[0]; + else + { + // This shouldn't happen + m_DefaultRegion = new GridRegion(1000, 1000); + m_log.Error("[HYPERGRID LINKER]: Something is wrong with this grid. It has no regions?"); + } } } return m_DefaultRegion; @@ -90,7 +97,7 @@ namespace OpenSim.Services.GridService public HypergridLinker(IConfigSource config, GridService gridService, IRegionData db) { - m_log.DebugFormat("[HYPERGRID LINKER]: Starting..."); + m_log.DebugFormat("[HYPERGRID LINKER]: Starting with db {0}", db.GetType()); m_Database = db; m_GridService = gridService; @@ -196,7 +203,7 @@ namespace OpenSim.Services.GridService public bool TryCreateLink(UUID scopeID, int xloc, int yloc, string externalRegionName, uint externalPort, string externalHostName, out GridRegion regInfo, out string reason) { - m_log.DebugFormat("[HYPERGRID LINKER]: Link to {0}:{1}, in {2}-{3}", externalHostName, externalPort, xloc, yloc); + m_log.DebugFormat("[HYPERGRID LINKER]: Link to {0}:{1}:{2}, in {3}-{4}", externalHostName, externalPort, externalRegionName, xloc, yloc); reason = string.Empty; regInfo = new GridRegion(); @@ -280,29 +287,28 @@ namespace OpenSim.Services.GridService public bool TryUnlinkRegion(string mapName) { + m_log.DebugFormat("[HYPERGRID LINKER]: Request to unlink {0}", mapName); GridRegion regInfo = null; - if (mapName.Contains(":")) - { - string host = "127.0.0.1"; - //string portstr; - //string regionName = ""; - uint port = 9000; - string[] parts = mapName.Split(new char[] { ':' }); - if (parts.Length >= 1) - { - host = parts[0]; - } - foreach (GridRegion r in m_HyperlinkRegions.Values) - if (host.Equals(r.ExternalHostName) && (port == r.HttpPort)) - regInfo = r; - } - else + List regions = m_Database.Get(mapName, m_ScopeID); + if (regions != null && regions.Count > 0) { - foreach (GridRegion r in m_HyperlinkRegions.Values) - if (r.RegionName.Equals(mapName)) - regInfo = r; + OpenSim.Data.RegionFlags rflags = (OpenSim.Data.RegionFlags)Convert.ToInt32(regions[0].Data["flags"]); + if ((rflags & OpenSim.Data.RegionFlags.Hyperlink) != 0) + { + regInfo = new GridRegion(); + regInfo.RegionID = regions[0].RegionID; + regInfo.ScopeID = m_ScopeID; + } } + + //foreach (GridRegion r in m_HyperlinkRegions.Values) + //{ + // m_log.DebugFormat("XXX Comparing {0}:{1} with {2}:{3}", host, port, r.ExternalHostName, r.HttpPort); + // if (host.Equals(r.ExternalHostName) && (port == r.HttpPort)) + // regInfo = r; + //} + if (regInfo != null) { RemoveHyperlinkRegion(regInfo.RegionID); diff --git a/OpenSim/Services/HypergridService/GatekeeperService.cs b/OpenSim/Services/HypergridService/GatekeeperService.cs index 56744b6122..c5cfe75fdf 100644 --- a/OpenSim/Services/HypergridService/GatekeeperService.cs +++ b/OpenSim/Services/HypergridService/GatekeeperService.cs @@ -119,7 +119,8 @@ namespace OpenSim.Services.HypergridService imageURL = string.Empty; reason = string.Empty; - m_log.DebugFormat("[GATEKEEPER SERVICE]: Request to link to {0}", (regionName == string.Empty ? "default region" : regionName)); + + m_log.DebugFormat("[GATEKEEPER SERVICE]: Request to link to {0}", (regionName == string.Empty)? "default region" : regionName); if (!m_AllowTeleportsToAnyRegion || regionName == string.Empty) { List defs = m_GridService.GetDefaultRegions(m_ScopeID); From 92561aef8dd1658bc29e5f9458c94407be7da582 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 3 May 2010 23:44:23 +0200 Subject: [PATCH 068/260] Store given items in correct parent folder. Fixes items given to offline avatars not getting lost. --- .../Framework/Scenes/Scene.Inventory.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 911722444c..20760b2ad8 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -415,6 +415,25 @@ namespace OpenSim.Region.Framework.Scenes itemCopy.BasePermissions = item.BasePermissions; } + if (itemCopy.Folder == UUID.Zero) + { + InventoryFolderBase folder = InventoryService.GetFolderForType(recipient, (AssetType)itemCopy.AssetType); + + if (folder != null) + { + itemCopy.Folder = folder.ID; + } + else + { + InventoryFolderBase root = InventoryService.GetRootFolder(recipient); + + if (root != null) + itemCopy.Folder = root.ID; + else + return null; // No destination + } + } + itemCopy.GroupID = UUID.Zero; itemCopy.GroupOwned = false; itemCopy.Flags = item.Flags; From 92dff5edb1333a798e21f956ea6291b357dd0c20 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 3 May 2010 23:45:05 +0200 Subject: [PATCH 069/260] Add folder version incrementing to XInventoryService. Fixes offline give for avatar->avatar --- OpenSim/Data/MySQL/MySQLXInventoryData.cs | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/OpenSim/Data/MySQL/MySQLXInventoryData.cs b/OpenSim/Data/MySQL/MySQLXInventoryData.cs index 307a4c7cab..a3b728b825 100644 --- a/OpenSim/Data/MySQL/MySQLXInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLXInventoryData.cs @@ -160,5 +160,36 @@ namespace OpenSim.Data.MySQL } } } + + public override bool Store(XInventoryItem item) + { + if (base.Store(item)) + return false; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.Connection = dbcon; + + cmd.CommandText = String.Format("update inventoryfolders set version=version+1 where folderID = ?folderID"); + cmd.Parameters.AddWithValue("?folderID", item.parentFolderID.ToString()); + + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + return false; + } + cmd.Dispose(); + } + dbcon.Close(); + } + return true; + } } } From 23d7a942ea88c597768430c09f7ff42331ac9c96 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 3 May 2010 23:53:49 +0200 Subject: [PATCH 070/260] Refix the fix --- OpenSim/Data/MySQL/MySQLXInventoryData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Data/MySQL/MySQLXInventoryData.cs b/OpenSim/Data/MySQL/MySQLXInventoryData.cs index a3b728b825..0fe801d696 100644 --- a/OpenSim/Data/MySQL/MySQLXInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLXInventoryData.cs @@ -163,7 +163,7 @@ namespace OpenSim.Data.MySQL public override bool Store(XInventoryItem item) { - if (base.Store(item)) + if (!base.Store(item)) return false; using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) From 9635af61f082ab1619a9ca482c76e2e01b0a4c55 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 4 May 2010 17:56:30 +0200 Subject: [PATCH 071/260] Allow regions to get the list of the other regions in the estate --- OpenSim/Data/MySQL/MySQLEstateData.cs | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/OpenSim/Data/MySQL/MySQLEstateData.cs b/OpenSim/Data/MySQL/MySQLEstateData.cs index d0c02f0647..08e21446f2 100644 --- a/OpenSim/Data/MySQL/MySQLEstateData.cs +++ b/OpenSim/Data/MySQL/MySQLEstateData.cs @@ -474,7 +474,36 @@ namespace OpenSim.Data.MySQL public List GetRegions(int estateID) { - return new List(); + List result = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + try + { + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select RegionID from estate_map where EstateID = ?EstateID"; + cmd.Parameters.AddWithValue("?EstateID", estateID.ToString()); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while(reader.Read()) + result.Add(new UUID(reader["RegionID"].ToString())); + reader.Close(); + } + } + } + catch (Exception e) + { + m_log.Error("[REGION DB]: Error reading estate map. " + e.ToString()); + return result; + } + dbcon.Close(); + } + + return result; } public bool DeleteEstate(int estateID) From 6eea0a39316483d2e1d02374e6caf2397ed9e3b6 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 4 May 2010 18:10:13 +0200 Subject: [PATCH 072/260] Allow reloading of estate settings into a running region. Move sun update helper into Scene, since that is less evil than exposing m_storageManager to the public. --- .../World/Estate/EstateManagementModule.cs | 40 ++-------------- OpenSim/Region/Framework/Scenes/Scene.cs | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 91d40ab54a..77068b0ada 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -212,7 +212,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_scene.RegionInfo.RegionSettings.FixedSun = UseFixedSun; m_scene.RegionInfo.RegionSettings.SunPosition = SunHour; - TriggerEstateToolsSunUpdate(); + m_scene.TriggerEstateSunUpdate(); //m_log.Debug("[ESTATE]: UFS: " + UseFixedSun.ToString()); //m_log.Debug("[ESTATE]: SunHour: " + SunHour.ToString()); @@ -861,7 +861,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_scene.RegionInfo.EstateSettings.Save(); - TriggerEstateToolsSunUpdate(); + m_scene.TriggerEstateSunUpdate(); sendDetailedEstateData(remoteClient, invoice); } @@ -983,7 +983,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { // Sets up the sun module based no the saved Estate and Region Settings // DO NOT REMOVE or the sun will stop working - TriggerEstateToolsSunUpdate(); + m_scene.TriggerEstateSunUpdate(); } public void Close() @@ -1004,40 +1004,6 @@ namespace OpenSim.Region.CoreModules.World.Estate #region Other Functions - private void TriggerEstateToolsSunUpdate() - { - float sun; - if (m_scene.RegionInfo.RegionSettings.UseEstateSun) - { - sun = (float)m_scene.RegionInfo.EstateSettings.SunPosition; - if (m_scene.RegionInfo.EstateSettings.UseGlobalTime) - { - sun = m_scene.EventManager.GetCurrentTimeAsSunLindenHour() - 6.0f; - } - - // - m_scene.EventManager.TriggerEstateToolsSunUpdate( - m_scene.RegionInfo.RegionHandle, - m_scene.RegionInfo.EstateSettings.FixedSun, - m_scene.RegionInfo.RegionSettings.UseEstateSun, - sun); - } - else - { - // Use the Sun Position from the Region Settings - sun = (float)m_scene.RegionInfo.RegionSettings.SunPosition - 6.0f; - - m_scene.EventManager.TriggerEstateToolsSunUpdate( - m_scene.RegionInfo.RegionHandle, - m_scene.RegionInfo.RegionSettings.FixedSun, - m_scene.RegionInfo.RegionSettings.UseEstateSun, - sun); - } - - - } - - public void changeWaterHeight(float height) { setRegionTerrainSettings(height, diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 3e4694a73a..da18629a86 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -5075,5 +5075,51 @@ namespace OpenSim.Region.Framework.Scenes { return new Vector3(x, y, GetGroundHeight(x, y)); } + + public List GetEstateRegions(int estateID) + { + if (m_storageManager.EstateDataStore == null) + return new List(); + + return m_storageManager.EstateDataStore.GetRegions(estateID); + } + + public void ReloadEstateData() + { + m_regInfo.EstateSettings = m_storageManager.EstateDataStore.LoadEstateSettings(m_regInfo.RegionID, false); + + TriggerEstateSunUpdate(); + } + + public void TriggerEstateSunUpdate() + { + float sun; + if (RegionInfo.RegionSettings.UseEstateSun) + { + sun = (float)RegionInfo.EstateSettings.SunPosition; + if (RegionInfo.EstateSettings.UseGlobalTime) + { + sun = EventManager.GetCurrentTimeAsSunLindenHour() - 6.0f; + } + + // + EventManager.TriggerEstateToolsSunUpdate( + RegionInfo.RegionHandle, + RegionInfo.EstateSettings.FixedSun, + RegionInfo.RegionSettings.UseEstateSun, + sun); + } + else + { + // Use the Sun Position from the Region Settings + sun = (float)RegionInfo.RegionSettings.SunPosition - 6.0f; + + EventManager.TriggerEstateToolsSunUpdate( + RegionInfo.RegionHandle, + RegionInfo.RegionSettings.FixedSun, + RegionInfo.RegionSettings.UseEstateSun, + sun); + } + } } } From 9cf0077bf9792833ec59b2d3ff69663c4dae1bbd Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 4 May 2010 18:31:52 +0200 Subject: [PATCH 073/260] Add "reload estate" command to sims --- OpenSim/Region/Framework/Scenes/Scene.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index da18629a86..1a46837d60 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -646,6 +646,10 @@ namespace OpenSim.Region.Framework.Scenes } } + MainConsole.Instance.Commands.AddCommand("region", false, "reload estate", + "reload estate", + "Reload the estate data", HandleReloadEstate); + //Bind Storage Manager functions to some land manager functions for this scene EventManager.OnLandObjectAdded += new EventManager.LandObjectAdded(m_storageManager.DataStore.StoreLandObject); @@ -5121,5 +5125,15 @@ namespace OpenSim.Region.Framework.Scenes sun); } } + + private void HandleReloadEstate(string module, string[] cmd) + { + if (MainConsole.Instance.ConsoleScene == null || + (MainConsole.Instance.ConsoleScene is Scene && + (Scene)MainConsole.Instance.ConsoleScene == this)) + { + ReloadEstateData(); + } + } } } From 484584b83ce96825c49ccf7c9c129a3961e5c223 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 4 May 2010 09:44:30 -0700 Subject: [PATCH 074/260] Fixed: migration #2 for SQLite. Problem was that ATTACH cannot be done inside a transaction. --- OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql b/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql index 545d233466..d38e2b77cc 100644 --- a/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql +++ b/OpenSim/Data/SQLite/Resources/002_XInventoryStore.sql @@ -1,9 +1,8 @@ -BEGIN TRANSACTION; +ATTACH 'inventoryStore.db' AS old; -ATTACH 'inventoryStore.db' AS old; +BEGIN TRANSACTION; INSERT INTO inventoryfolders (folderName, type, version, folderID, agentID, parentFolderID) SELECT `name` AS folderName, `type` AS type, `version` AS version, `UUID` AS folderID, `agentID` AS agentID, `parentID` AS parentFolderID from old.inventoryfolders; - INSERT INTO inventoryitems (assetID, assetType, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryID, parentFolderID, avatarID, inventoryGroupPermissions) SELECT `assetID`, `assetType` AS assetType, `inventoryName` AS inventoryName, `inventoryDescription` AS inventoryDescription, `inventoryNextPermissions` AS inventoryNextPermissions, `inventoryCurrentPermissions` AS inventoryCurrentPermissions, `invType` AS invType, `creatorsID` AS creatorID, `inventoryBasePermissions` AS inventoryBasePermissions, `inventoryEveryOnePermissions` AS inventoryEveryOnePermissions, `salePrice` AS salePrice, `saleType` AS saleType, `creationDate` AS creationDate, `groupID` AS groupID, `groupOwned` AS groupOwned, `flags` AS flags, `UUID` AS inventoryID, `parentFolderID` AS parentFolderID, `avatarID` AS avatarID, `inventoryGroupPermissions` AS inventoryGroupPermissions FROM old.inventoryitems; COMMIT; From d997fc7b765d425bd34adf5bc839b80fd9fad37c Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 4 May 2010 20:57:31 +0200 Subject: [PATCH 075/260] Add events to the estate interface to let interested modules know of changes to estate settings --- .../World/Estate/EstateManagementModule.cs | 36 +++++++++++++++++++ .../Framework/Interfaces/IEstateModule.cs | 5 +++ 2 files changed, 41 insertions(+) diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 77068b0ada..92e9eed946 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -48,6 +48,9 @@ namespace OpenSim.Region.CoreModules.World.Estate private EstateTerrainXferHandler TerrainUploader; + public event ChangeDelegate OnRegionInfoChange; + public event ChangeDelegate OnEstateInfoChange; + #region Packet Data Responders private void sendDetailedEstateData(IClientAPI remote_client, UUID invoice) @@ -137,6 +140,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide = false; m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); sendRegionInfoPacketToAll(); } @@ -162,6 +166,7 @@ namespace OpenSim.Region.CoreModules.World.Estate break; } m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); sendRegionInfoPacketToAll(); } @@ -187,6 +192,7 @@ namespace OpenSim.Region.CoreModules.World.Estate break; } m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); sendRegionInfoPacketToAll(); } @@ -219,6 +225,7 @@ namespace OpenSim.Region.CoreModules.World.Estate sendRegionInfoPacketToAll(); m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); } private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds) @@ -230,6 +237,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.RegionSettings.Covenant = estateCovenantID; m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); } private void handleEstateAccessDeltaRequest(IClientAPI remote_client, UUID invoice, int estateAccessType, UUID user) @@ -245,6 +253,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.AddEstateUser(user); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AccessOptions, m_scene.RegionInfo.EstateSettings.EstateAccess, m_scene.RegionInfo.EstateSettings.EstateID); } else @@ -259,6 +268,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.RemoveEstateUser(user); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AccessOptions, m_scene.RegionInfo.EstateSettings.EstateAccess, m_scene.RegionInfo.EstateSettings.EstateID); } @@ -273,6 +283,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.AddEstateGroup(user); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AllowedGroups, m_scene.RegionInfo.EstateSettings.EstateGroups, m_scene.RegionInfo.EstateSettings.EstateID); } else @@ -286,6 +297,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.RemoveEstateGroup(user); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AllowedGroups, m_scene.RegionInfo.EstateSettings.EstateGroups, m_scene.RegionInfo.EstateSettings.EstateID); } @@ -323,6 +335,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_scene.RegionInfo.EstateSettings.AddBan(item); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); ScenePresence s = m_scene.GetScenePresence(user); if (s != null) @@ -370,6 +383,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.RemoveBan(listitem.BannedUserID); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); } else { @@ -389,6 +403,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.AddEstateManager(user); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateID); } else @@ -402,6 +417,7 @@ namespace OpenSim.Region.CoreModules.World.Estate { m_scene.RegionInfo.EstateSettings.RemoveEstateManager(user); m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateID); } @@ -449,6 +465,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); m_scene.SetSceneCoreDebug(scripted, collisionEvents, physics); } @@ -860,6 +877,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_scene.RegionInfo.EstateSettings.DenyMinors = false; m_scene.RegionInfo.EstateSettings.Save(); + TriggerEstateInfoChange(); m_scene.TriggerEstateSunUpdate(); @@ -927,6 +945,7 @@ namespace OpenSim.Region.CoreModules.World.Estate break; } m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); sendRegionInfoPacketToAll(); } @@ -972,6 +991,7 @@ namespace OpenSim.Region.CoreModules.World.Estate break; } m_scene.RegionInfo.RegionSettings.Save(); + TriggerRegionInfoChange(); sendRegionHandshakeToAll(); } } @@ -1141,5 +1161,21 @@ namespace OpenSim.Region.CoreModules.World.Estate return false; } + + protected void TriggerRegionInfoChange() + { + ChangeDelegate change = OnRegionInfoChange; + + if (change != null) + change(); + } + + protected void TriggerEstateInfoChange() + { + ChangeDelegate change = OnEstateInfoChange; + + if (change != null) + change(); + } } } diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs index 890fa31df0..b2135d2531 100644 --- a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs @@ -29,8 +29,13 @@ using OpenMetaverse; namespace OpenSim.Region.Framework.Interfaces { + public delegate void ChangeDelegate(); + public interface IEstateModule : IRegionModule { + event ChangeDelegate OnRegionInfoChange; + event ChangeDelegate OnEstateInfoChange; + uint GetRegionFlags(); bool IsManager(UUID avatarID); From 5a4cef6b362ebea22e240e1e0b48c14ac2d817d5 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 4 May 2010 21:16:20 +0200 Subject: [PATCH 076/260] make the events more useful by providing the source region --- .../Region/CoreModules/World/Estate/EstateManagementModule.cs | 4 ++-- OpenSim/Region/Framework/Interfaces/IEstateModule.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 92e9eed946..dcc66ca4ef 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -1167,7 +1167,7 @@ namespace OpenSim.Region.CoreModules.World.Estate ChangeDelegate change = OnRegionInfoChange; if (change != null) - change(); + change(m_scene.RegionInfo.RegionID); } protected void TriggerEstateInfoChange() @@ -1175,7 +1175,7 @@ namespace OpenSim.Region.CoreModules.World.Estate ChangeDelegate change = OnEstateInfoChange; if (change != null) - change(); + change(m_scene.RegionInfo.RegionID); } } } diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs index b2135d2531..329c3f5792 100644 --- a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs @@ -29,7 +29,7 @@ using OpenMetaverse; namespace OpenSim.Region.Framework.Interfaces { - public delegate void ChangeDelegate(); + public delegate void ChangeDelegate(UUID regionID); public interface IEstateModule : IRegionModule { From 3761f7997134f3515421e9cf19ebb775f296bef2 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 4 May 2010 23:45:59 +0200 Subject: [PATCH 077/260] Strip estate message sending out from the estate management module and the dialog module. Convert it to an event on the estate module interface. The old implementation did the same as message to region, a button that is right next to it on the UI. This implementation prevented people from adding a more sane one in a module. --- .../CoreModules/Avatar/Dialog/DialogModule.cs | 8 -------- .../World/Estate/EstateManagementModule.cs | 14 ++++++++++---- .../Region/Framework/Interfaces/IDialogModule.cs | 13 ------------- .../Region/Framework/Interfaces/IEstateModule.cs | 2 ++ 4 files changed, 12 insertions(+), 25 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs b/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs index c31266c0e1..da38edefc9 100644 --- a/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs @@ -132,14 +132,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog sp.ControllingClient.SendLoadURL(objectName, objectID, ownerID, groupOwned, message, url); } - public void SendNotificationToUsersInEstate( - UUID fromAvatarID, string fromAvatarName, string message) - { - // TODO: This does not yet do what it says on the tin - it only sends the message to users in the same - // region as the sending avatar. - SendNotificationToUsersInRegion(fromAvatarID, fromAvatarName, message); - } - public void SendTextBoxToUser(UUID avatarid, string message, int chatChannel, string name, UUID objectid, UUID ownerid) { UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, ownerid); diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index dcc66ca4ef..abd0fcb9cf 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -50,6 +50,7 @@ namespace OpenSim.Region.CoreModules.World.Estate public event ChangeDelegate OnRegionInfoChange; public event ChangeDelegate OnEstateInfoChange; + public event MessageDelegate OnEstateMessage; #region Packet Data Responders @@ -440,10 +441,7 @@ namespace OpenSim.Region.CoreModules.World.Estate private void SendEstateBlueBoxMessage( IClientAPI remote_client, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message) { - IDialogModule dm = m_scene.RequestModuleInterface(); - - if (dm != null) - dm.SendNotificationToUsersInEstate(senderID, senderName, message); + TriggerEstateMessage(senderID, senderName, message); } private void handleEstateDebugRegionRequest(IClientAPI remote_client, UUID invoice, UUID senderID, bool scripted, bool collisionEvents, bool physics) @@ -1177,5 +1175,13 @@ namespace OpenSim.Region.CoreModules.World.Estate if (change != null) change(m_scene.RegionInfo.RegionID); } + + protected void TriggerEstateMessage(UUID fromID, string fromName, string message) + { + MessageDelegate onmessage = OnEstateMessage; + + if (onmessage != null) + onmessage(m_scene.RegionInfo.RegionID, fromID, fromName, message); + } } } diff --git a/OpenSim/Region/Framework/Interfaces/IDialogModule.cs b/OpenSim/Region/Framework/Interfaces/IDialogModule.cs index 35b4b63152..be9764a7ea 100644 --- a/OpenSim/Region/Framework/Interfaces/IDialogModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IDialogModule.cs @@ -119,19 +119,6 @@ namespace OpenSim.Region.Framework.Interfaces /// The message being sent to the user void SendNotificationToUsersInRegion(UUID fromAvatarID, string fromAvatarName, string message); - /// - /// Send a notification to all users in the estate. This notification should remain around until the - /// user explicitly dismisses it. - /// - /// - /// On the Linden Labs Second Client (as of 1.21), this is a big blue box message on the upper right of the - /// screen. - /// - /// The user sending the message - /// The name of the user doing the sending - /// The message being sent to the user - void SendNotificationToUsersInEstate(UUID fromAvatarID, string fromAvatarName, string message); - /// /// Send a textbox entry for the client to respond to /// diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs index 329c3f5792..c850f7fd81 100644 --- a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs @@ -30,11 +30,13 @@ using OpenMetaverse; namespace OpenSim.Region.Framework.Interfaces { public delegate void ChangeDelegate(UUID regionID); + public delegate void MessageDelegate(UUID regionID, UUID fromID, string fromName, string message); public interface IEstateModule : IRegionModule { event ChangeDelegate OnRegionInfoChange; event ChangeDelegate OnEstateInfoChange; + event MessageDelegate OnEstateMessage; uint GetRegionFlags(); bool IsManager(UUID avatarID); From 34d882b70d0374a445b3f6b1cf4931ef322d9587 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Wed, 5 May 2010 01:32:55 +0200 Subject: [PATCH 078/260] Add perms check to the teleport home client command handlers. --- .../CoreModules/World/Estate/EstateManagementModule.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index abd0fcb9cf..940b535921 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -470,6 +470,9 @@ namespace OpenSim.Region.CoreModules.World.Estate private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey) { + if (!m_scene.Permissions.CanIssueEstateCommand(remover_client.AgentId, false)) + return; + if (prey != UUID.Zero) { ScenePresence s = m_scene.GetScenePresence(prey); @@ -483,6 +486,9 @@ namespace OpenSim.Region.CoreModules.World.Estate private void handleEstateTeleportAllUsersHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID) { + if (!m_scene.Permissions.CanIssueEstateCommand(remover_client.AgentId, false)) + return; + m_scene.ForEachScenePresence(delegate(ScenePresence sp) { if (sp.UUID != senderID) From f005e570aa85fba4e2f82094f9d795db52895eeb Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Tue, 4 May 2010 22:13:25 -0400 Subject: [PATCH 079/260] * This should fix the tests failing because of a MainConsole.Instance null reference * Added a MockConsole that doesn't require a handle to System.Console --- OpenSim/Framework/Console/MockConsole.cs | 32 +++++++++++++++++++ .../Tests/Common/Setup/SceneSetupHelpers.cs | 4 +-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 OpenSim/Framework/Console/MockConsole.cs diff --git a/OpenSim/Framework/Console/MockConsole.cs b/OpenSim/Framework/Console/MockConsole.cs new file mode 100644 index 0000000000..57c56f1017 --- /dev/null +++ b/OpenSim/Framework/Console/MockConsole.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Framework.Console +{ + public class MockConsole : CommandConsole + { + public MockConsole(string defaultPrompt) : base(defaultPrompt) + { + } + public override void Output(string text) + { + } + public override void Output(string text, string level) + { + } + + public override string ReadLine(string p, bool isCommand, bool e) + { + //Thread.CurrentThread.Join(1000); + return string.Empty; + } + public override void UnlockOutput() + { + } + public override void LockOutput() + { + } + } +} diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 864e2aa7c3..ef8ea50e64 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -142,7 +142,7 @@ namespace OpenSim.Tests.Common.Setup //{ // System.Console.WriteLine("Starting a brand new scene"); // newScene = true; - // MainConsole.Instance = new LocalConsole("TEST PROMPT"); + MainConsole.Instance = new MockConsole("TEST PROMPT"); // MainServer.Instance = new BaseHttpServer(980); // commsManager = cm; //} @@ -204,7 +204,7 @@ namespace OpenSim.Tests.Common.Setup m_inventoryService.PostInitialise(); m_assetService.PostInitialise(); m_userAccountService.PostInitialise(); - + testScene.RegionInfo.EstateSettings.EstateOwner = UUID.Random(); testScene.SetModuleInterfaces(); testScene.LandChannel = new TestLandChannel(testScene); From a9db266d93c3ec0f593879daf601f2055dc6fa3e Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Tue, 4 May 2010 22:16:07 -0400 Subject: [PATCH 080/260] * Added an important comment to warn people not to use MockConsole for anything but testing. --- OpenSim/Framework/Console/MockConsole.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OpenSim/Framework/Console/MockConsole.cs b/OpenSim/Framework/Console/MockConsole.cs index 57c56f1017..9eb197750d 100644 --- a/OpenSim/Framework/Console/MockConsole.cs +++ b/OpenSim/Framework/Console/MockConsole.cs @@ -5,6 +5,11 @@ using System.Text; namespace OpenSim.Framework.Console { + /// + /// This is a Fake console that's used when setting up the Scene in Unit Tests + /// Don't use this except for Unit Testing or you're in for a world of hurt when the + /// sim gets to ReadLine + /// public class MockConsole : CommandConsole { public MockConsole(string defaultPrompt) : base(defaultPrompt) From 8187fccd258bf0936d3db8663844e07a7b81e9fc Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 5 May 2010 16:12:52 +0100 Subject: [PATCH 081/260] Patch from mcortez: Update groups, add ALPHA Siman grid connector for groups Signed-off-by: Melanie --- .../XmlRpcGroups/GroupsMessagingModule.cs | 274 ++++++++-------- .../Avatar/XmlRpcGroups/GroupsModule.cs | 65 ++-- .../XmlRpcGroups/IGroupsServicesConnector.cs | 6 - .../SimianGroupsServicesConnectorModule.cs | 292 ++++++------------ .../XmlRpcGroupsServicesConnectorModule.cs | 225 ++++---------- 5 files changed, 316 insertions(+), 546 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs index 185d44de80..00fe5df046 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs @@ -28,30 +28,41 @@ using System; using System.Collections.Generic; using System.Reflection; + + using log4net; using Mono.Addins; using Nini.Config; + using OpenMetaverse; using OpenMetaverse.StructuredData; + using OpenSim.Framework; using OpenSim.Region.CoreModules.Framework.EventQueue; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; + using Caps = OpenSim.Framework.Capabilities.Caps; namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] - public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule + public class GroupsMessagingModule : ISharedRegionModule { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private List m_sceneList = new List(); private IMessageTransferModule m_msgTransferModule = null; - private IGroupsServicesConnector m_groupData = null; + private IGroupsModule m_groupsModule = null; + + // TODO: Move this off to the Groups Server + public Dictionary> m_agentsInGroupSession = new Dictionary>(); + public Dictionary> m_agentsDroppedSession = new Dictionary>(); + // Config Options private bool m_groupMessagingEnabled = false; @@ -97,12 +108,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public void AddRegion(Scene scene) { - if (!m_groupMessagingEnabled) - return; - - scene.RegisterModuleInterface(this); + // NoOp } - public void RegionLoaded(Scene scene) { if (!m_groupMessagingEnabled) @@ -110,12 +117,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - m_groupData = scene.RequestModuleInterface(); + m_groupsModule = scene.RequestModuleInterface(); // No groups module, no groups messaging - if (m_groupData == null) + if (m_groupsModule == null) { - m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled."); + m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsModule, GroupsMessagingModule is now disabled."); Close(); m_groupMessagingEnabled = false; return; @@ -137,7 +144,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - scene.EventManager.OnClientLogin += OnClientLogin; + } public void RemoveRegion(Scene scene) @@ -165,7 +172,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups m_sceneList.Clear(); - m_groupData = null; + m_groupsModule = null; m_msgTransferModule = null; } @@ -190,84 +197,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups #endregion - /// - /// Not really needed, but does confirm that the group exists. - /// - public bool StartGroupChatSession(UUID agentID, UUID groupID) - { - if (m_debugEnabled) - m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null); - - if (groupInfo != null) - { - return true; - } - else - { - return false; - } - } - - public void SendMessageToGroup(GridInstantMessage im, UUID groupID) - { - if (m_debugEnabled) - m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - - foreach (GroupMembersData member in m_groupData.GetGroupMembers(UUID.Zero, groupID)) - { - if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) - { - // Don't deliver messages to people who have dropped this session - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); - continue; - } - - // Copy Message - GridInstantMessage msg = new GridInstantMessage(); - msg.imSessionID = groupID.Guid; - msg.fromAgentName = im.fromAgentName; - msg.message = im.message; - msg.dialog = im.dialog; - msg.offline = im.offline; - msg.ParentEstateID = im.ParentEstateID; - msg.Position = im.Position; - msg.RegionID = im.RegionID; - msg.binaryBucket = im.binaryBucket; - msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); - - msg.fromAgentID = im.fromAgentID; - msg.fromGroup = true; - - msg.toAgentID = member.AgentID.Guid; - - IClientAPI client = GetActiveClient(member.AgentID); - if (client == null) - { - // If they're not local, forward across the grid - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); - m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); - } - else - { - // Deliver locally, directly - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); - ProcessMessageFromGroupSession(msg); - } - } - } - #region SimGridEventHandlers - void OnClientLogin(IClientAPI client) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name); - - - } - private void OnNewClient(IClientAPI client) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name); @@ -305,46 +236,42 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); - UUID AgentID = new UUID(msg.fromAgentID); - UUID GroupID = new UUID(msg.imSessionID); - switch (msg.dialog) { case (byte)InstantMessageDialog.SessionAdd: - m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); + AddAgentToGroupSession(msg.fromAgentID, msg.imSessionID); break; case (byte)InstantMessageDialog.SessionDrop: - m_groupData.AgentDroppedFromGroupChatSession(AgentID, GroupID); + RemoveAgentFromGroupSession(msg.fromAgentID, msg.imSessionID); break; case (byte)InstantMessageDialog.SessionSend: - if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID) - && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID, GroupID) - ) + if (!m_agentsInGroupSession.ContainsKey(msg.toAgentID) + && !m_agentsDroppedSession.ContainsKey(msg.toAgentID)) { // Agent not in session and hasn't dropped from session // Add them to the session for now, and Invite them - m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); + AddAgentToGroupSession(msg.toAgentID, msg.imSessionID); UUID toAgentID = new UUID(msg.toAgentID); IClientAPI activeClient = GetActiveClient(toAgentID); if (activeClient != null) { - GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null); + UUID groupID = new UUID(msg.fromAgentID); + + GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); if (groupInfo != null) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message"); // Force? open the group session dialog??? - // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg); IEventQueue eq = activeClient.Scene.RequestModuleInterface(); eq.ChatterboxInvitation( - GroupID + groupID , groupInfo.GroupName , new UUID(msg.fromAgentID) - , msg.message - , new UUID(msg.toAgentID) + , msg.message, new UUID(msg.toAgentID) , msg.fromAgentName , msg.dialog , msg.timestamp @@ -358,7 +285,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups ); eq.ChatterBoxSessionAgentListUpdates( - new UUID(GroupID) + new UUID(groupID) , new UUID(msg.fromAgentID) , new UUID(msg.toAgentID) , false //canVoiceChat @@ -368,7 +295,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } } - else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID)) + else if (!m_agentsDroppedSession.ContainsKey(msg.toAgentID)) { // User hasn't dropped, so they're in the session, // maybe we should deliver it. @@ -394,8 +321,56 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups #endregion - #region ClientEvents + + private void RemoveAgentFromGroupSession(Guid agentID, Guid sessionID) + { + if (m_agentsInGroupSession.ContainsKey(sessionID)) + { + // If in session remove + if (m_agentsInGroupSession[sessionID].Contains(agentID)) + { + m_agentsInGroupSession[sessionID].Remove(agentID); + } + + // If not in dropped list, add + if (!m_agentsDroppedSession[sessionID].Contains(agentID)) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Dropped {1} from session {0}", sessionID, agentID); + m_agentsDroppedSession[sessionID].Add(agentID); + } + } + } + + private void AddAgentToGroupSession(Guid agentID, Guid sessionID) + { + // Add Session Status if it doesn't exist for this session + CreateGroupSessionTracking(sessionID); + + // If nessesary, remove from dropped list + if (m_agentsDroppedSession[sessionID].Contains(agentID)) + { + m_agentsDroppedSession[sessionID].Remove(agentID); + } + + // If nessesary, add to in session list + if (!m_agentsInGroupSession[sessionID].Contains(agentID)) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Added {1} to session {0}", sessionID, agentID); + m_agentsInGroupSession[sessionID].Add(agentID); + } + } + + private void CreateGroupSessionTracking(Guid sessionID) + { + if (!m_agentsInGroupSession.ContainsKey(sessionID)) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Creating session tracking for : {0}", sessionID); + m_agentsInGroupSession.Add(sessionID, new List()); + m_agentsDroppedSession.Add(sessionID, new List()); + } + } + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) { if (m_debugEnabled) @@ -408,23 +383,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Start group IM session if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) { - if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID); + UUID groupID = new UUID(im.toAgentID); - UUID GroupID = new UUID(im.imSessionID); - UUID AgentID = new UUID(im.fromAgentID); - - GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null); - + GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); if (groupInfo != null) { - m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Start Group Session for {0}", groupInfo.GroupName); - ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID); + AddAgentToGroupSession(im.fromAgentID, im.imSessionID); + + ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, groupID); IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); queue.ChatterBoxSessionAgentListUpdates( - GroupID - , AgentID + new UUID(groupID) + , new UUID(im.fromAgentID) , new UUID(im.toAgentID) , false //canVoiceChat , false //isModerator @@ -436,21 +409,64 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Send a message from locally connected client to a group if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) { - UUID GroupID = new UUID(im.imSessionID); - UUID AgentID = new UUID(im.fromAgentID); + UUID groupID = new UUID(im.toAgentID); - if (m_debugEnabled) - m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString()); + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", groupID, im.imSessionID.ToString()); - //If this agent is sending a message, then they want to be in the session - m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); - - SendMessageToGroup(im, GroupID); + SendMessageToGroup(im, groupID); } } #endregion + private void SendMessageToGroup(GridInstantMessage im, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + foreach (GroupMembersData member in m_groupsModule.GroupMembersRequest(null, groupID)) + { + if (!m_agentsDroppedSession.ContainsKey(im.imSessionID) || m_agentsDroppedSession[im.imSessionID].Contains(member.AgentID.Guid)) + { + // Don't deliver messages to people who have dropped this session + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); + continue; + } + + // Copy Message + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = im.imSessionID; + msg.fromAgentName = im.fromAgentName; + msg.message = im.message; + msg.dialog = im.dialog; + msg.offline = im.offline; + msg.ParentEstateID = im.ParentEstateID; + msg.Position = im.Position; + msg.RegionID = im.RegionID; + msg.binaryBucket = im.binaryBucket; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + + // Updat Pertinate fields to make it a "group message" + msg.fromAgentID = groupID.Guid; + msg.fromGroup = true; + + msg.toAgentID = member.AgentID.Guid; + + IClientAPI client = GetActiveClient(member.AgentID); + if (client == null) + { + // If they're not local, forward across the grid + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + else + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); + ProcessMessageFromGroupSession(msg); + } + } + } + void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); @@ -502,8 +518,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups /// private IClientAPI GetActiveClient(UUID agentID) { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Looking for local client {0}", agentID); - IClientAPI child = null; // Try root avatar first @@ -515,26 +529,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups ScenePresence user = (ScenePresence)scene.Entities[agentID]; if (!user.IsChildAgent) { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Found root agent for client : {0}", user.ControllingClient.Name); return user.ControllingClient; } else { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Found child agent for client : {0}", user.ControllingClient.Name); child = user.ControllingClient; } } } // If we didn't find a root, then just return whichever child we found, or null if none - if (child == null) - { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID); - } - else - { - if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name); - } return child; } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index 56c0d985f1..6b942cbaaf 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -176,6 +176,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + // The InstantMessageModule itself doesn't do this, // so lets see if things explode if we don't do it // scene.EventManager.OnClientClosed += OnClientClosed; @@ -509,7 +510,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups IClientAPI ejectee = GetActiveClient(ejecteeID); if (ejectee != null) { - UUID groupID = new UUID(im.imSessionID); + UUID groupID = new UUID(im.fromAgentID); ejectee.SendAgentDropGroup(groupID); } } @@ -600,14 +601,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - List data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID); - - if (m_debugEnabled) - { - foreach (GroupMembersData member in data) - { - m_log.DebugFormat("[GROUPS]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); - } + List data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupMembersData member in data) + { + m_log.DebugFormat("[GROUPS]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); + } } return data; @@ -627,14 +628,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentID(remoteClient), groupID); - - if (m_debugEnabled) - { - foreach (GroupRoleMembersData member in data) - { - m_log.DebugFormat("[GROUPS]: Member({0}) - Role({1})", member.MemberID, member.RoleID); - } + List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentID(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRoleMembersData member in data) + { + m_log.DebugFormat("[GROUPS]: Member({0}) - Role({1})", member.MemberID, member.RoleID); + } } return data; } @@ -1143,11 +1144,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups OSDMap llDataStruct = new OSDMap(3); llDataStruct.Add("AgentData", AgentData); llDataStruct.Add("GroupData", GroupData); - llDataStruct.Add("NewGroupData", NewGroupData); - - if (m_debugEnabled) - { - m_log.InfoFormat("[GROUPS]: {0}", OSDParser.SerializeJsonString(llDataStruct)); + llDataStruct.Add("NewGroupData", NewGroupData); + + if (m_debugEnabled) + { + m_log.InfoFormat("[GROUPS]: {0}", OSDParser.SerializeJsonString(llDataStruct)); } IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); @@ -1307,16 +1308,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // } - #endregion - - private UUID GetRequestingAgentID(IClientAPI client) - { - UUID requestingAgentID = UUID.Zero; - if (client != null) - { - requestingAgentID = client.AgentId; - } - return requestingAgentID; + #endregion + + private UUID GetRequestingAgentID(IClientAPI client) + { + UUID requestingAgentID = UUID.Zero; + if (client != null) + { + requestingAgentID = client.AgentId; + } + return requestingAgentID; } } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs index a046e094cd..6487967971 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs @@ -71,12 +71,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups void AddGroupNotice(UUID RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket); GroupNoticeInfo GetGroupNotice(UUID RequestingAgentID, UUID noticeID); List GetGroupNotices(UUID RequestingAgentID, UUID GroupID); - - void ResetAgentGroupChatSessions(UUID agentID); - bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID); - bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID); - void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID); - void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID); } public class GroupInviteInfo diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs index 9363205263..590753ebe1 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs @@ -55,9 +55,6 @@ using OpenSim.Services.Interfaces; * UserID -> Group -> ActiveGroup * + GroupID * - * UserID -> GroupSessionDropped -> GroupID - * UserID -> GroupSessionInvited -> GroupID - * * UserID -> GroupMember -> GroupID * + SelectedRoleID [UUID] * + AcceptNotices [bool] @@ -66,7 +63,6 @@ using OpenSim.Services.Interfaces; * * UserID -> GroupRole[GroupID] -> RoleID * - * * GroupID -> Group -> GroupName * + Charter * + ShowInList @@ -163,13 +159,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private bool m_connectorEnabled = false; - private string m_groupsServerURI = string.Empty; + private string m_serviceURL = string.Empty; private bool m_debugEnabled = false; - private ExpiringCache m_memoryCache; - private int m_cacheTimeout = 30; - // private IUserAccountService m_accountService = null; @@ -206,33 +199,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return; } - m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name); + m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); - m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); - if ((m_groupsServerURI == null) || - (m_groupsServerURI == string.Empty)) + m_serviceURL = groupsConfig.GetString("XmlRpcServiceURL", string.Empty); + if ((m_serviceURL == null) || + (m_serviceURL == string.Empty)) { - m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]"); + m_log.ErrorFormat("Please specify a valid Simian Server URL for XmlRpcServiceURL in OpenSim.ini, [Groups]"); m_connectorEnabled = false; return; } - - m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30); - if (m_cacheTimeout == 0) - { - m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Disabled."); - } - else - { - m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Timeout set to {0}.", m_cacheTimeout); - } - - - - m_memoryCache = new ExpiringCache(); - - // If we got all the config options we need, lets start'er'up m_connectorEnabled = true; @@ -243,7 +220,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public void Close() { - m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Closing {0}", this.Name); + m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); } public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) @@ -311,8 +288,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if(SimianAddGeneric(GroupID, "Group", name, GroupInfoMap)) { - AddGroupRole(requestingAgentID, GroupID, UUID.Zero, "Everyone", "Members of " + name, "Member of " + name, (ulong)m_DefaultEveryonePowers); - AddGroupRole(requestingAgentID, GroupID, OwnerRoleID, "Owners", "Owners of " + name, "Owner of " + name, (ulong)m_DefaultOwnerPowers); + AddGroupRole(requestingAgentID, GroupID, UUID.Zero, "Everyone", "Members of " + name, "Member of " + name, (ulong)m_DefaultEveryonePowers); + AddGroupRole(requestingAgentID, GroupID, OwnerRoleID, "Owners", "Owners of " + name, "Owner of " + name, (ulong)m_DefaultOwnerPowers); AddAgentToGroup(requestingAgentID, requestingAgentID, GroupID, OwnerRoleID); @@ -436,7 +413,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } else if ((groupName != null) && (groupName != string.Empty)) - { + { if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap)) { return null; @@ -445,7 +422,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups GroupRecord GroupInfo = new GroupRecord(); - GroupInfo.GroupID = groupID; + GroupInfo.GroupID = groupID; GroupInfo.GroupName = groupName; GroupInfo.Charter = GroupInfoMap["Charter"].AsString(); GroupInfo.ShowInList = GroupInfoMap["ShowInList"].AsBoolean(); @@ -676,7 +653,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap response = CachedPostRequest(requestArgs); + OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)response["Entries"]; @@ -774,9 +751,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (SimianGetGenericEntry(agentID, "Group", "ActiveGroup", out UserActiveGroup)) { GroupID = UserActiveGroup["GroupID"].AsUUID(); - } - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Active GroupID : {0}", GroupID.ToString()); + } + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Active GroupID : {0}", GroupID.ToString()); return GetAgentGroupMembership(requestingAgentID, agentID, GroupID); } @@ -804,24 +781,24 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups List Roles = new List(); - Dictionary GroupRoles; - if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) - { - Dictionary MemberRoles; - if (SimianGetGenericEntries(agentID, "GroupRole" + groupID.ToString(), out MemberRoles)) - { - foreach (KeyValuePair kvp in MemberRoles) - { - GroupRolesData data = new GroupRolesData(); - data.RoleID = UUID.Parse(kvp.Key); - data.Name = GroupRoles[kvp.Key]["Name"].AsString(); - data.Description = GroupRoles[kvp.Key]["Description"].AsString(); - data.Title = GroupRoles[kvp.Key]["Title"].AsString(); - data.Powers = GroupRoles[kvp.Key]["Powers"].AsULong(); - - Roles.Add(data); - } - } + Dictionary GroupRoles; + if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) + { + Dictionary MemberRoles; + if (SimianGetGenericEntries(agentID, "GroupRole" + groupID.ToString(), out MemberRoles)) + { + foreach (KeyValuePair kvp in MemberRoles) + { + GroupRolesData data = new GroupRolesData(); + data.RoleID = UUID.Parse(kvp.Key); + data.Name = GroupRoles[kvp.Key]["Name"].AsString(); + data.Description = GroupRoles[kvp.Key]["Description"].AsString(); + data.Title = GroupRoles[kvp.Key]["Title"].AsString(); + data.Powers = GroupRoles[kvp.Key]["Powers"].AsULong(); + + Roles.Add(data); + } + } } return Roles; } @@ -935,8 +912,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { foreach( KeyValuePair GroupRoleMember in GroupRoleMembers ) { - GroupRoleMembersData data = new GroupRoleMembersData(); - + GroupRoleMembersData data = new GroupRoleMembersData(); + data.MemberID = GroupRoleMember.Key; data.RoleID = UUID.Parse(Role.Key); @@ -1021,52 +998,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } #endregion - #region GroupSessionTracking - - public void ResetAgentGroupChatSessions(UUID agentID) - { - Dictionary agentSessions; - - if (SimianGetGenericEntries(agentID, "GroupSessionDropped", out agentSessions)) - { - foreach (string GroupID in agentSessions.Keys) - { - SimianRemoveGenericEntry(agentID, "GroupSessionDropped", GroupID); - } - } - - if (SimianGetGenericEntries(agentID, "GroupSessionInvited", out agentSessions)) - { - foreach (string GroupID in agentSessions.Keys) - { - SimianRemoveGenericEntry(agentID, "GroupSessionInvited", GroupID); - } - } - } - - public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID) - { - OSDMap session; - return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session); - } - - public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID) - { - SimianAddGeneric(agentID, "GroupSessionDropped", groupID.ToString(), new OSDMap()); - } - - public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID) - { - SimianAddGeneric(agentID, "GroupSessionInvited", groupID.ToString(), new OSDMap()); - } - - public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID) - { - OSDMap session; - return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session); - } - - #endregion private void EnsureRoleNotSelectedByMember(UUID groupID, UUID roleID, UUID userID) { @@ -1105,7 +1036,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = CachedPostRequest(RequestArgs); + OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); if (Response["Success"].AsBoolean()) { return true; @@ -1132,23 +1063,23 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = CachedPostRequest(RequestArgs); + OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { - OSDArray entryArray = (OSDArray)Response["Entries"]; - if (entryArray.Count >= 1) - { - OSDMap entryMap = entryArray[0] as OSDMap; - key = entryMap["Key"].AsString(); - map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); - - return true; - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + OSDArray entryArray = (OSDArray)Response["Entries"]; + if (entryArray.Count >= 1) + { + OSDMap entryMap = entryArray[0] as OSDMap; + key = entryMap["Key"].AsString(); + map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + + return true; + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else @@ -1172,23 +1103,23 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = CachedPostRequest(RequestArgs); + OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { - OSDArray entryArray = (OSDArray)Response["Entries"]; - if (entryArray.Count >= 1) - { - OSDMap entryMap = entryArray[0] as OSDMap; - ownerID = entryMap["OwnerID"].AsUUID(); - map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); - - return true; - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + OSDArray entryArray = (OSDArray)Response["Entries"]; + if (entryArray.Count >= 1) + { + OSDMap entryMap = entryArray[0] as OSDMap; + ownerID = entryMap["OwnerID"].AsUUID(); + map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + + return true; + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else @@ -1213,7 +1144,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = CachedPostRequest(RequestArgs); + OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; @@ -1221,16 +1152,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { OSDMap entryMap = entryArray[0] as OSDMap; key = entryMap["Key"].AsString(); - map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); - + map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + return true; - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); - } + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + } } else { @@ -1253,20 +1184,20 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups - OSDMap response = CachedPostRequest(requestArgs); + OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { maps = new Dictionary(); OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); maps.Add(entryMap["Key"].AsString(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString())); } - if(maps.Count == 0) - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + if(maps.Count == 0) + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } return true; @@ -1291,21 +1222,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups - OSDMap response = CachedPostRequest(requestArgs); + OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { maps = new Dictionary(); OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) - { + { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); maps.Add(entryMap["OwnerID"].AsUUID(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString())); - } - if (maps.Count == 0) - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); - } + } + if (maps.Count == 0) + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + } return true; } else @@ -1329,7 +1260,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap response = CachedPostRequest(requestArgs); + OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); if (response["Success"].AsBoolean()) { return true; @@ -1341,49 +1272,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } #endregion - - #region CheesyCache - OSDMap CachedPostRequest(NameValueCollection requestArgs) - { - // Immediately forward the request if the cache is disabled. - if (m_cacheTimeout == 0) - { - return WebUtil.PostToService(m_groupsServerURI, requestArgs); - } - - // Check if this is an update or a request - if ( requestArgs["RequestMethod"] == "RemoveGeneric" - || requestArgs["RequestMethod"] == "AddGeneric" - ) - - { - // Any and all updates cause the cache to clear - m_memoryCache.Clear(); - - // Send update to server, return the response without caching it - return WebUtil.PostToService(m_groupsServerURI, requestArgs); - - } - - // If we're not doing an update, we must be requesting data - - // Create the cache key for the request and see if we have it cached - string CacheKey = WebUtil.BuildQueryString(requestArgs); - OSDMap response = null; - if (!m_memoryCache.TryGetValue(CacheKey, out response)) - { - // if it wasn't in the cache, pass the request to the Simian Grid Services - response = WebUtil.PostToService(m_groupsServerURI, requestArgs); - - // and cache the response - m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); - } - - // return cached response - return response; - } - #endregion - } } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs index 79b9a167d0..ab343c8dce 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs @@ -29,7 +29,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Reflection; -using System.Text; using Nwc.XmlRpc; @@ -62,7 +61,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private bool m_connectorEnabled = false; - private string m_groupsServerURI = string.Empty; + private string m_serviceURL = string.Empty; private bool m_disableKeepAlive = false; @@ -70,17 +69,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private string m_groupWriteKey = string.Empty; private IUserAccountService m_accountService = null; - - private ExpiringCache m_memoryCache; - private int m_cacheTimeout = 30; - - // Used to track which agents are have dropped from a group chat session - // Should be reset per agent, on logon - // TODO: move this to Flotsam XmlRpc Service - // SessionID, List - private Dictionary> m_groupsAgentsDroppedFromChatSession = new Dictionary>(); - private Dictionary> m_groupsAgentsInvitedToChatSession = new Dictionary>(); - + #region IRegionModuleBase Members @@ -115,13 +104,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return; } - m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name); + m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); - m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); - if ((m_groupsServerURI == null) || - (m_groupsServerURI == string.Empty)) + m_serviceURL = groupsConfig.GetString("XmlRpcServiceURL", string.Empty); + if ((m_serviceURL == null) || + (m_serviceURL == string.Empty)) { - m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]"); + m_log.ErrorFormat("Please specify a valid URL for XmlRpcServiceURL in OpenSim.ini, [Groups]"); m_connectorEnabled = false; return; } @@ -131,26 +120,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups m_groupReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty); m_groupWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty); + - m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30); - if (m_cacheTimeout == 0) - { - m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Disabled."); - } - else - { - m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Timeout set to {0}.", m_cacheTimeout); - } // If we got all the config options we need, lets start'er'up - m_memoryCache = new ExpiringCache(); m_connectorEnabled = true; } } public void Close() { - m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Closing {0}", this.Name); + m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); } public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) @@ -776,69 +756,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param); } - - - - #endregion - - #region GroupSessionTracking - - public void ResetAgentGroupChatSessions(UUID agentID) - { - foreach (List agentList in m_groupsAgentsDroppedFromChatSession.Values) - { - agentList.Remove(agentID); - } - } - - public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID) - { - // If we're tracking this group, and we can find them in the tracking, then they've been invited - return m_groupsAgentsInvitedToChatSession.ContainsKey(groupID) - && m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID); - } - - public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID) - { - // If we're tracking drops for this group, - // and we find them, well... then they've dropped - return m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID) - && m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID); - } - - public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID) - { - if (m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)) - { - // If not in dropped list, add - if (!m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID)) - { - m_groupsAgentsDroppedFromChatSession[groupID].Add(agentID); - } - } - } - - public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID) - { - // Add Session Status if it doesn't exist for this session - CreateGroupChatSessionTracking(groupID); - - // If nessesary, remove from dropped list - if (m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID)) - { - m_groupsAgentsDroppedFromChatSession[groupID].Remove(agentID); - } - } - - private void CreateGroupChatSessionTracking(UUID groupID) - { - if (!m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)) - { - m_groupsAgentsDroppedFromChatSession.Add(groupID, new List()); - m_groupsAgentsInvitedToChatSession.Add(groupID, new List()); - } - - } #endregion #region XmlRpcHashtableMarshalling @@ -932,84 +849,50 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups ///
private Hashtable XmlRpcCall(UUID requestingAgentID, string function, Hashtable param) { - XmlRpcResponse resp = null; - string CacheKey = null; - - // Only bother with the cache if it isn't disabled. - if (m_cacheTimeout > 0) - { - if (!function.StartsWith("groups.get")) - { - // Any and all updates cause the cache to clear - m_memoryCache.Clear(); - } - else - { - StringBuilder sb = new StringBuilder(requestingAgentID + function); - foreach (object key in param.Keys) - { - if (param[key] != null) - { - sb.AppendFormat(",{0}:{1}", key.ToString(), param[key].ToString()); - } - } - - CacheKey = sb.ToString(); - m_memoryCache.TryGetValue(CacheKey, out resp); - } - - } + string UserService; + UUID SessionID; + GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID); + param.Add("requestingAgentID", requestingAgentID.ToString()); + param.Add("RequestingAgentUserService", UserService); + param.Add("RequestingSessionID", SessionID.ToString()); - if( resp == null ) + + param.Add("ReadKey", m_groupReadKey); + param.Add("WriteKey", m_groupWriteKey); + + + IList parameters = new ArrayList(); + parameters.Add(param); + + ConfigurableKeepAliveXmlRpcRequest req; + req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive); + + XmlRpcResponse resp = null; + + try { - string UserService; - UUID SessionID; - GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID); - param.Add("requestingAgentID", requestingAgentID.ToString()); - param.Add("RequestingAgentUserService", UserService); - param.Add("RequestingSessionID", SessionID.ToString()); + resp = req.Send(m_serviceURL, 10000); + } + catch (Exception e) + { + + m_log.ErrorFormat("[XMLRPCGROUPDATA]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", e.ToString()); - param.Add("ReadKey", m_groupReadKey); - param.Add("WriteKey", m_groupWriteKey); - - - IList parameters = new ArrayList(); - parameters.Add(param); - - ConfigurableKeepAliveXmlRpcRequest req; - req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive); - - - try + foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine },StringSplitOptions.None)) { - resp = req.Send(m_groupsServerURI, 10000); - - if ((m_cacheTimeout > 0) && (CacheKey != null)) - { - m_memoryCache.AddOrUpdate(CacheKey, resp, TimeSpan.FromSeconds(m_cacheTimeout)); - } - + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", ResponseLine); } - catch (Exception e) + + foreach (string key in param.Keys) { - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function); - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", e.ToString()); - - foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) - { - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", ResponseLine); - } - - foreach (string key in param.Keys) - { - m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", key, param[key].ToString()); - } - - Hashtable respData = new Hashtable(); - respData.Add("error", e.ToString()); - return respData; + m_log.WarnFormat("[XMLRPCGROUPDATA]: {0} :: {1}", key, param[key].ToString()); } + + Hashtable respData = new Hashtable(); + respData.Add("error", e.ToString()); + return respData; } if (resp.Value is Hashtable) @@ -1023,21 +906,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return respData; } - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); if (resp.Value is ArrayList) { ArrayList al = (ArrayList)resp.Value; - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Contains {0} elements", al.Count); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: Contains {0} elements", al.Count); foreach (object o in al) { - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", o.GetType().ToString(), o.ToString()); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} :: {1}", o.GetType().ToString(), o.ToString()); } } else { - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Function returned: {0}", resp.Value.ToString()); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: Function returned: {0}", resp.Value.ToString()); } Hashtable error = new Hashtable(); @@ -1047,16 +930,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private void LogRespDataToConsoleError(Hashtable respData) { - m_log.Error("[XMLRPC-GROUPS-CONNECTOR]: Error:"); + m_log.Error("[XMLRPCGROUPDATA]: Error:"); foreach (string key in respData.Keys) { - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Key: {0}", key); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: Key: {0}", key); string[] lines = respData[key].ToString().Split(new char[] { '\n' }); foreach (string line in lines) { - m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0}", line); + m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0}", line); } } @@ -1065,8 +948,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups /// /// Group Request Tokens are an attempt to allow the groups service to authenticate - /// requests. - /// TODO: This broke after the big grid refactor, either find a better way, or discard this + /// requests. Currently uses UserService, AgentID, and SessionID + /// TODO: Find a better way to do this. /// /// /// From 7aed89a8d099f3eecdaec10ec0cb6cd546d3bd3b Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Wed, 5 May 2010 21:12:02 +0200 Subject: [PATCH 082/260] Removed a test for a "can't happen" case. ParentGroup is never null anymore. --- .../ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 3ccbb3abc6..b2eb585b12 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -1890,14 +1890,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api float ground = World.GetGroundHeight((float)targetPos.x, (float)targetPos.y); bool disable_underground_movement = m_ScriptEngine.Config.GetBoolean("DisableUndergroundMovement", true); - if (part.ParentGroup == null) - { - if ((targetPos.z < ground) && disable_underground_movement) - targetPos.z = ground; - LSL_Vector real_vec = SetPosAdjust(currentPos, targetPos); - part.UpdateOffSet(new Vector3((float)real_vec.x, (float)real_vec.y, (float)real_vec.z)); - } - else if (part.ParentGroup.RootPart == part) + if (part.ParentGroup.RootPart == part) { if ((targetPos.z < ground) && disable_underground_movement) targetPos.z = ground; @@ -1907,7 +1900,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - //it's late... i think this is right ? if (llVecDist(new LSL_Vector(0,0,0), targetPos) <= 10.0f) { part.OffsetPosition = new Vector3((float)targetPos.x, (float)targetPos.y, (float)targetPos.z); From fe8399d1bf6ed84435c41d495b04feb25fb9a988 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Wed, 5 May 2010 23:06:36 +0200 Subject: [PATCH 083/260] Add a XMLRPC method to remotely set the login level for the LLLoginService. This requires a special XMLRPC call, which has to supply the credentials of a god user (User level >= 200). Disabled by default. Also Adds a configuration option to set the initial permitted login level. --- .../Server/Handlers/Login/LLLoginHandlers.cs | 37 +++++++++++++ .../Login/LLLoginServiceInConnector.cs | 1 + OpenSim/Services/Interfaces/ILoginService.cs | 1 + .../Services/LLLoginService/LLLoginService.cs | 53 +++++++++++++++++++ bin/Robust.ini.example | 1 + 5 files changed, 93 insertions(+) diff --git a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs index daf27043df..83b3e317d2 100644 --- a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs +++ b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs @@ -99,6 +99,43 @@ namespace OpenSim.Server.Handlers.Login } + public XmlRpcResponse HandleXMLRPCSetLoginLevel(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + + if (requestData != null) + { + if (requestData.ContainsKey("first") && requestData["first"] != null && + requestData.ContainsKey("last") && requestData["last"] != null && + requestData.ContainsKey("level") && requestData["level"] != null && + requestData.ContainsKey("passwd") && requestData["passwd"] != null) + { + string first = requestData["first"].ToString(); + string last = requestData["last"].ToString(); + string passwd = requestData["passwd"].ToString(); + int level = Int32.Parse(requestData["level"].ToString()); + + m_log.InfoFormat("[LOGIN]: XMLRPC Set Level to {2} Requested by {0} {1}", first, last, level); + + Hashtable reply = m_LocalService.SetLevel(first, last, passwd, level, remoteClient); + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = reply; + + return response; + + } + } + + XmlRpcResponse failResponse = new XmlRpcResponse(); + Hashtable failHash = new Hashtable(); + failHash["success"] = "false"; + failResponse.Value = failHash; + + return failResponse; + + } + public OSD HandleLLSDLogin(OSD request, IPEndPoint remoteClient) { if (request.Type == OSDType.Map) diff --git a/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs b/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs index e24055b015..67e83924d0 100644 --- a/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs +++ b/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs @@ -88,6 +88,7 @@ namespace OpenSim.Server.Handlers.Login { LLLoginHandlers loginHandlers = new LLLoginHandlers(m_LoginService); server.AddXmlRPCHandler("login_to_simulator", loginHandlers.HandleXMLRPCLogin, false); + server.AddXmlRPCHandler("set_login_level", loginHandlers.HandleXMLRPCSetLoginLevel, false); server.SetDefaultLLSDHandler(loginHandlers.HandleLLSDLogin); } diff --git a/OpenSim/Services/Interfaces/ILoginService.cs b/OpenSim/Services/Interfaces/ILoginService.cs index 49efbe2572..513ab4a4a9 100644 --- a/OpenSim/Services/Interfaces/ILoginService.cs +++ b/OpenSim/Services/Interfaces/ILoginService.cs @@ -48,6 +48,7 @@ namespace OpenSim.Services.Interfaces public interface ILoginService { LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, IPEndPoint clientIP); + Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP); } diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 4d7dfd1eda..95127d2fac 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -26,6 +26,7 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.Net; using System.Reflection; @@ -70,6 +71,7 @@ namespace OpenSim.Services.LLLoginService private bool m_RequireInventory; protected int m_MinLoginLevel; private string m_GatekeeperURL; + private bool m_AllowRemoteSetLoginLevel; IConfig m_LoginServerConfig; @@ -93,6 +95,8 @@ namespace OpenSim.Services.LLLoginService m_DefaultRegionName = m_LoginServerConfig.GetString("DefaultRegion", String.Empty); m_WelcomeMessage = m_LoginServerConfig.GetString("WelcomeMessage", "Welcome to OpenSim!"); m_RequireInventory = m_LoginServerConfig.GetBoolean("RequireInventory", true); + m_AllowRemoteSetLoginLevel = m_LoginServerConfig.GetBoolean("AllowRemoteSetLoginLevel", false); + m_MinLoginLevel = m_LoginServerConfig.GetInt("MinLoginLevel", 0); m_GatekeeperURL = m_LoginServerConfig.GetString("GatekeeperURI", string.Empty); // These are required; the others aren't @@ -147,6 +151,55 @@ namespace OpenSim.Services.LLLoginService { } + public Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP) + { + Hashtable response = new Hashtable(); + response["success"] = "false"; + + if (!m_AllowRemoteSetLoginLevel) + return response; + + try + { + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, firstName, lastName); + if (account == null) + { + m_log.InfoFormat("[LLOGIN SERVICE]: Set Level failed, user {0} {1} not found", firstName, lastName); + return response; + } + + if (account.UserLevel < 200) + { + m_log.InfoFormat("[LLOGIN SERVICE]: Set Level failed, reason: user level too low"); + return response; + } + + // + // Authenticate this user + // + // We don't support clear passwords here + // + string token = m_AuthenticationService.Authenticate(account.PrincipalID, passwd, 30); + UUID secureSession = UUID.Zero; + if ((token == string.Empty) || (token != string.Empty && !UUID.TryParse(token, out secureSession))) + { + m_log.InfoFormat("[LLOGIN SERVICE]: SetLevel failed, reason: authentication failed"); + return response; + } + } + catch (Exception e) + { + m_log.Error("[LLOGIN SERVICE]: SetLevel failed, exception " + e.ToString()); + return response; + } + + m_MinLoginLevel = level; + m_log.InfoFormat("[LLOGIN SERVICE]: Login level set to {0} by {1} {2}", level, firstName, lastName); + + response["success"] = true; + return response; + } + public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, IPEndPoint clientIP) { bool success = false; diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index cfc08bc7dc..2679523e4f 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -126,6 +126,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" WelcomeMessage = "Welcome, Avatar!" + AllowRemoteSetLoginLevel = "false"; [GridInfoService] From 2ebe1482664533bb9b9040134fe4a23fbc939d51 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 6 May 2010 00:34:49 +0200 Subject: [PATCH 084/260] Plumb the viewer version string through into AgentCircuitData. Now all that is left os to figure out what black magic turns AgentCircuitData into AgentData and then copy that into the ScenePresence, where m_Viewer is already added with this commit and waits for the data. --- OpenSim/Framework/AgentCircuitData.cs | 11 +++++++++++ OpenSim/Region/Framework/Scenes/ScenePresence.cs | 6 ++++++ OpenSim/Server/Handlers/Login/LLLoginHandlers.cs | 4 ++-- OpenSim/Services/Interfaces/ILoginService.cs | 2 +- OpenSim/Services/LLLoginService/LLLoginService.cs | 13 +++++++------ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/OpenSim/Framework/AgentCircuitData.cs b/OpenSim/Framework/AgentCircuitData.cs index 353e5bf96d..783a8337a5 100644 --- a/OpenSim/Framework/AgentCircuitData.cs +++ b/OpenSim/Framework/AgentCircuitData.cs @@ -107,6 +107,11 @@ namespace OpenSim.Framework ///
public string ServiceSessionID = string.Empty; + /// + /// Viewer's version string + /// + public string Viewer; + /// /// Position the Agent's Avatar starts in the region /// @@ -136,6 +141,7 @@ namespace OpenSim.Framework BaseFolder = new UUID(cAgent.BaseFolder); CapsPath = cAgent.CapsPath; ChildrenCapSeeds = cAgent.ChildrenCapSeeds; + Viewer = cAgent.Viewer; } /// @@ -173,6 +179,7 @@ namespace OpenSim.Framework args["service_session_id"] = OSD.FromString(ServiceSessionID); args["start_pos"] = OSD.FromString(startpos.ToString()); args["appearance_serial"] = OSD.FromInteger(Appearance.Serial); + args["viewer"] = OSD.FromString(Viewer); if (Appearance != null) { @@ -272,6 +279,8 @@ namespace OpenSim.Framework SessionID = args["session_id"].AsUUID(); if (args["service_session_id"] != null) ServiceSessionID = args["service_session_id"].AsString(); + if (args["viewer"] != null) + Viewer = args["viewer"].AsString(); if (args["start_pos"] != null) Vector3.TryParse(args["start_pos"].AsString(), out startpos); @@ -339,6 +348,7 @@ namespace OpenSim.Framework public float startposx; public float startposy; public float startposz; + public string Viewer; public sAgentCircuitData() { @@ -360,6 +370,7 @@ namespace OpenSim.Framework BaseFolder = cAgent.BaseFolder.Guid; CapsPath = cAgent.CapsPath; ChildrenCapSeeds = cAgent.ChildrenCapSeeds; + Viewer = cAgent.Viewer; } } } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 3efb45fa87..b4bbbe3b26 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -218,6 +218,7 @@ namespace OpenSim.Region.Framework.Scenes private bool m_followCamAuto; private int m_movementUpdateCount; + private string m_Viewer = String.Empty; private const int NumMovementsBetweenRayCast = 5; @@ -651,6 +652,11 @@ namespace OpenSim.Region.Framework.Scenes set { m_flyDisabled = value; } } + public string Viewer + { + get { return m_Viewer; } + } + #endregion #region Constructor(s) diff --git a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs index 83b3e317d2..c9bf99619f 100644 --- a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs +++ b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs @@ -86,7 +86,7 @@ namespace OpenSim.Server.Handlers.Login m_log.InfoFormat("[LOGIN]: XMLRPC Login Requested for {0} {1}, starting in {2}, using {3}", first, last, startLocation, clientVersion); LoginResponse reply = null; - reply = m_LocalService.Login(first, last, passwd, startLocation, scopeID, remoteClient); + reply = m_LocalService.Login(first, last, passwd, startLocation, scopeID, clientVersion, remoteClient); XmlRpcResponse response = new XmlRpcResponse(); response.Value = reply.ToHashtable(); @@ -157,7 +157,7 @@ namespace OpenSim.Server.Handlers.Login m_log.Info("[LOGIN]: LLSD Login Requested for: '" + map["first"].AsString() + "' '" + map["last"].AsString() + "' / " + startLocation); LoginResponse reply = null; - reply = m_LocalService.Login(map["first"].AsString(), map["last"].AsString(), map["passwd"].AsString(), startLocation, scopeID, remoteClient); + reply = m_LocalService.Login(map["first"].AsString(), map["last"].AsString(), map["passwd"].AsString(), startLocation, scopeID, String.Empty, remoteClient); return reply.ToOSDMap(); } diff --git a/OpenSim/Services/Interfaces/ILoginService.cs b/OpenSim/Services/Interfaces/ILoginService.cs index 513ab4a4a9..9e573393d7 100644 --- a/OpenSim/Services/Interfaces/ILoginService.cs +++ b/OpenSim/Services/Interfaces/ILoginService.cs @@ -47,7 +47,7 @@ namespace OpenSim.Services.Interfaces public interface ILoginService { - LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, IPEndPoint clientIP); + LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, string clientVersion, IPEndPoint clientIP); Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP); } diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 95127d2fac..be90d38d2e 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -200,7 +200,7 @@ namespace OpenSim.Services.LLLoginService return response; } - public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, IPEndPoint clientIP) + public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, string clientVersion, IPEndPoint clientIP) { bool success = false; UUID session = UUID.Random(); @@ -320,7 +320,7 @@ namespace OpenSim.Services.LLLoginService // Instantiate/get the simulation interface and launch an agent at the destination // string reason = string.Empty; - AgentCircuitData aCircuit = LaunchAgentAtGrid(gatekeeper, destination, account, avatar, session, secureSession, position, where, out where, out reason); + AgentCircuitData aCircuit = LaunchAgentAtGrid(gatekeeper, destination, account, avatar, session, secureSession, position, where, clientVersion, out where, out reason); if (aCircuit == null) { @@ -586,7 +586,7 @@ namespace OpenSim.Services.LLLoginService } protected AgentCircuitData LaunchAgentAtGrid(GridRegion gatekeeper, GridRegion destination, UserAccount account, AvatarData avatar, - UUID session, UUID secureSession, Vector3 position, string currentWhere, out string where, out string reason) + UUID session, UUID secureSession, Vector3 position, string currentWhere, string viewer, out string where, out string reason) { where = currentWhere; ISimulationService simConnector = null; @@ -626,7 +626,7 @@ namespace OpenSim.Services.LLLoginService if (m_UserAgentService == null && simConnector != null) { circuitCode = (uint)Util.RandomClass.Next(); ; - aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position); + aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, viewer); success = LaunchAgentDirectly(simConnector, destination, aCircuit, out reason); if (!success && m_GridService != null) { @@ -651,7 +651,7 @@ namespace OpenSim.Services.LLLoginService if (m_UserAgentService != null) { circuitCode = (uint)Util.RandomClass.Next(); ; - aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position); + aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, viewer); success = LaunchAgentIndirectly(gatekeeper, destination, aCircuit, out reason); if (!success && m_GridService != null) { @@ -680,7 +680,7 @@ namespace OpenSim.Services.LLLoginService } private AgentCircuitData MakeAgent(GridRegion region, UserAccount account, - AvatarData avatar, UUID session, UUID secureSession, uint circuit, Vector3 position) + AvatarData avatar, UUID session, UUID secureSession, uint circuit, Vector3 position, string viewer) { AgentCircuitData aCircuit = new AgentCircuitData(); @@ -701,6 +701,7 @@ namespace OpenSim.Services.LLLoginService aCircuit.SecureSessionID = secureSession; aCircuit.SessionID = session; aCircuit.startpos = position; + aCircuit.Viewer = viewer; SetServiceURLs(aCircuit, account); return aCircuit; From e45f5ac12653f2ca5cef495db1607bea699ceb76 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 6 May 2010 00:54:21 +0200 Subject: [PATCH 085/260] Plumb Viewer version into ScenePresence for initial login. It's still not carried along --- OpenSim/Region/Framework/Scenes/Scene.cs | 1 + OpenSim/Region/Framework/Scenes/ScenePresence.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 1a46837d60..2579e9d040 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2652,6 +2652,7 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence sp = CreateAndAddScenePresence(client); if (aCircuit != null) sp.Appearance = aCircuit.Appearance; + sp.Viewer = aCircuit.Viewer; // HERE!!! Do the initial attachments right here // first agent upon login is a root agent by design. diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index b4bbbe3b26..fb66fcdb86 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -655,6 +655,7 @@ namespace OpenSim.Region.Framework.Scenes public string Viewer { get { return m_Viewer; } + set { m_Viewer = value; } } #endregion From ad2039a8c49cbdbee719fa6578365391f6acfc7e Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 5 May 2010 22:48:05 +0100 Subject: [PATCH 086/260] Stab a test fail --- OpenSim/Region/Framework/Scenes/Scene.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 2579e9d040..fff024fc1e 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2651,8 +2651,10 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence sp = CreateAndAddScenePresence(client); if (aCircuit != null) + { sp.Appearance = aCircuit.Appearance; - sp.Viewer = aCircuit.Viewer; + sp.Viewer = aCircuit.Viewer; + } // HERE!!! Do the initial attachments right here // first agent upon login is a root agent by design. From 2b48ed60ecf45e4ec07af1cadbb6325c3e678a13 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 6 May 2010 02:02:12 +0200 Subject: [PATCH 087/260] Remove the m_Viewer variable and make the property a shortcut to the proper field in AgentCircuitData instead --- OpenSim/Region/Framework/Scenes/Scene.cs | 3 --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index fff024fc1e..1a46837d60 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2651,10 +2651,7 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence sp = CreateAndAddScenePresence(client); if (aCircuit != null) - { sp.Appearance = aCircuit.Appearance; - sp.Viewer = aCircuit.Viewer; - } // HERE!!! Do the initial attachments right here // first agent upon login is a root agent by design. diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index fb66fcdb86..30eafd7c65 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -218,8 +218,6 @@ namespace OpenSim.Region.Framework.Scenes private bool m_followCamAuto; private int m_movementUpdateCount; - private string m_Viewer = String.Empty; - private const int NumMovementsBetweenRayCast = 5; private bool CameraConstraintActive; @@ -654,8 +652,7 @@ namespace OpenSim.Region.Framework.Scenes public string Viewer { - get { return m_Viewer; } - set { m_Viewer = value; } + get { return m_scene.AuthenticateHandler.GetAgentCircuitData(ControllingClient.CircuitCode).Viewer; } } #endregion From ebc3726d52c772962cfa551721168079d5d563d2 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 5 May 2010 16:54:48 -0700 Subject: [PATCH 088/260] Added copying of Viewer field to the agent circuit data that is being passed on TPs and crossings. (XmlRpcGroups files want to be committed too) --- .../EntityTransfer/EntityTransferModule.cs | 6 + .../Avatar/XmlRpcGroups/GroupsModule.cs | 62 ++++---- .../SimianGroupsServicesConnectorModule.cs | 150 +++++++++--------- 3 files changed, 112 insertions(+), 106 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 80c0af8461..f2b03e4177 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -290,7 +290,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer agentCircuit.child = true; agentCircuit.Appearance = sp.Appearance; if (currentAgentCircuit != null) + { agentCircuit.ServiceURLs = currentAgentCircuit.ServiceURLs; + agentCircuit.Viewer = currentAgentCircuit.Viewer; + } if (NeedsNewAgent(oldRegionX, newRegionX, oldRegionY, newRegionY)) { @@ -986,7 +989,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer agent.child = true; agent.Appearance = sp.Appearance; if (currentAgentCircuit != null) + { agent.ServiceURLs = currentAgentCircuit.ServiceURLs; + agent.Viewer = currentAgentCircuit.Viewer; + } if (newRegions.Contains(neighbour.RegionHandle)) { diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index 6b942cbaaf..b2b8110c64 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -601,14 +601,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - List data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID); - - if (m_debugEnabled) - { - foreach (GroupMembersData member in data) - { - m_log.DebugFormat("[GROUPS]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); - } + List data = m_groupData.GetGroupMembers(GetRequestingAgentID(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupMembersData member in data) + { + m_log.DebugFormat("[GROUPS]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); + } } return data; @@ -628,14 +628,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (m_debugEnabled) m_log.DebugFormat("[GROUPS]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentID(remoteClient), groupID); - - if (m_debugEnabled) - { - foreach (GroupRoleMembersData member in data) - { - m_log.DebugFormat("[GROUPS]: Member({0}) - Role({1})", member.MemberID, member.RoleID); - } + List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentID(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRoleMembersData member in data) + { + m_log.DebugFormat("[GROUPS]: Member({0}) - Role({1})", member.MemberID, member.RoleID); + } } return data; } @@ -1144,11 +1144,11 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups OSDMap llDataStruct = new OSDMap(3); llDataStruct.Add("AgentData", AgentData); llDataStruct.Add("GroupData", GroupData); - llDataStruct.Add("NewGroupData", NewGroupData); - - if (m_debugEnabled) - { - m_log.InfoFormat("[GROUPS]: {0}", OSDParser.SerializeJsonString(llDataStruct)); + llDataStruct.Add("NewGroupData", NewGroupData); + + if (m_debugEnabled) + { + m_log.InfoFormat("[GROUPS]: {0}", OSDParser.SerializeJsonString(llDataStruct)); } IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); @@ -1308,16 +1308,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // } - #endregion - - private UUID GetRequestingAgentID(IClientAPI client) - { - UUID requestingAgentID = UUID.Zero; - if (client != null) - { - requestingAgentID = client.AgentId; - } - return requestingAgentID; + #endregion + + private UUID GetRequestingAgentID(IClientAPI client) + { + UUID requestingAgentID = UUID.Zero; + if (client != null) + { + requestingAgentID = client.AgentId; + } + return requestingAgentID; } } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs index 590753ebe1..bc05b0f246 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs @@ -288,8 +288,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if(SimianAddGeneric(GroupID, "Group", name, GroupInfoMap)) { - AddGroupRole(requestingAgentID, GroupID, UUID.Zero, "Everyone", "Members of " + name, "Member of " + name, (ulong)m_DefaultEveryonePowers); - AddGroupRole(requestingAgentID, GroupID, OwnerRoleID, "Owners", "Owners of " + name, "Owner of " + name, (ulong)m_DefaultOwnerPowers); + AddGroupRole(requestingAgentID, GroupID, UUID.Zero, "Everyone", "Members of " + name, "Member of " + name, (ulong)m_DefaultEveryonePowers); + AddGroupRole(requestingAgentID, GroupID, OwnerRoleID, "Owners", "Owners of " + name, "Owner of " + name, (ulong)m_DefaultOwnerPowers); AddAgentToGroup(requestingAgentID, requestingAgentID, GroupID, OwnerRoleID); @@ -413,7 +413,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } else if ((groupName != null) && (groupName != string.Empty)) - { + { if (!SimianGetFirstGenericEntry("Group", groupName, out groupID, out GroupInfoMap)) { return null; @@ -422,7 +422,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups GroupRecord GroupInfo = new GroupRecord(); - GroupInfo.GroupID = groupID; + GroupInfo.GroupID = groupID; GroupInfo.GroupName = groupName; GroupInfo.Charter = GroupInfoMap["Charter"].AsString(); GroupInfo.ShowInList = GroupInfoMap["ShowInList"].AsBoolean(); @@ -751,9 +751,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (SimianGetGenericEntry(agentID, "Group", "ActiveGroup", out UserActiveGroup)) { GroupID = UserActiveGroup["GroupID"].AsUUID(); - } - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Active GroupID : {0}", GroupID.ToString()); + } + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Active GroupID : {0}", GroupID.ToString()); return GetAgentGroupMembership(requestingAgentID, agentID, GroupID); } @@ -781,24 +781,24 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups List Roles = new List(); - Dictionary GroupRoles; - if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) - { - Dictionary MemberRoles; - if (SimianGetGenericEntries(agentID, "GroupRole" + groupID.ToString(), out MemberRoles)) - { - foreach (KeyValuePair kvp in MemberRoles) - { - GroupRolesData data = new GroupRolesData(); - data.RoleID = UUID.Parse(kvp.Key); - data.Name = GroupRoles[kvp.Key]["Name"].AsString(); - data.Description = GroupRoles[kvp.Key]["Description"].AsString(); - data.Title = GroupRoles[kvp.Key]["Title"].AsString(); - data.Powers = GroupRoles[kvp.Key]["Powers"].AsULong(); - - Roles.Add(data); - } - } + Dictionary GroupRoles; + if (SimianGetGenericEntries(groupID, "GroupRole", out GroupRoles)) + { + Dictionary MemberRoles; + if (SimianGetGenericEntries(agentID, "GroupRole" + groupID.ToString(), out MemberRoles)) + { + foreach (KeyValuePair kvp in MemberRoles) + { + GroupRolesData data = new GroupRolesData(); + data.RoleID = UUID.Parse(kvp.Key); + data.Name = GroupRoles[kvp.Key]["Name"].AsString(); + data.Description = GroupRoles[kvp.Key]["Description"].AsString(); + data.Title = GroupRoles[kvp.Key]["Title"].AsString(); + data.Powers = GroupRoles[kvp.Key]["Powers"].AsULong(); + + Roles.Add(data); + } + } } return Roles; } @@ -912,8 +912,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { foreach( KeyValuePair GroupRoleMember in GroupRoleMembers ) { - GroupRoleMembersData data = new GroupRoleMembersData(); - + GroupRoleMembersData data = new GroupRoleMembersData(); + data.MemberID = GroupRoleMember.Key; data.RoleID = UUID.Parse(Role.Key); @@ -1066,20 +1066,20 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { - OSDArray entryArray = (OSDArray)Response["Entries"]; - if (entryArray.Count >= 1) - { - OSDMap entryMap = entryArray[0] as OSDMap; - key = entryMap["Key"].AsString(); - map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); - - return true; - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + OSDArray entryArray = (OSDArray)Response["Entries"]; + if (entryArray.Count >= 1) + { + OSDMap entryMap = entryArray[0] as OSDMap; + key = entryMap["Key"].AsString(); + map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + + return true; + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else @@ -1106,20 +1106,20 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { - OSDArray entryArray = (OSDArray)Response["Entries"]; - if (entryArray.Count >= 1) - { - OSDMap entryMap = entryArray[0] as OSDMap; - ownerID = entryMap["OwnerID"].AsUUID(); - map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); - - return true; - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + OSDArray entryArray = (OSDArray)Response["Entries"]; + if (entryArray.Count >= 1) + { + OSDMap entryMap = entryArray[0] as OSDMap; + ownerID = entryMap["OwnerID"].AsUUID(); + map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + + return true; + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } } else @@ -1152,16 +1152,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { OSDMap entryMap = entryArray[0] as OSDMap; key = entryMap["Key"].AsString(); - map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); - - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); - + map = (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString()); + + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + return true; - } - else - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); - } + } + else + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + } } else { @@ -1191,13 +1191,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); maps.Add(entryMap["Key"].AsString(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString())); } - if(maps.Count == 0) - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + if(maps.Count == 0) + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); } return true; @@ -1229,14 +1229,14 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups OSDArray entryArray = (OSDArray)response["Entries"]; foreach (OSDMap entryMap in entryArray) - { + { if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Generics Result {0}", entryMap["Value"].AsString()); maps.Add(entryMap["OwnerID"].AsUUID(), (OSDMap)OSDParser.DeserializeJson(entryMap["Value"].AsString())); - } - if (maps.Count == 0) - { - if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); - } + } + if (maps.Count == 0) + { + if (m_debugEnabled) m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] No Generics Results"); + } return true; } else From 4183cef4094364d28fd4497fdb4332ff4be34914 Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Wed, 5 May 2010 21:22:29 -0400 Subject: [PATCH 089/260] * Fixes LandDataSerializerTests on Windows by stripping CR from the serialization result since the reference serialization has LF only. * Added a bool result and then an Assert.That(result) because resharper was having a hard time with the equality compare in Assert.That. --- .../Tests/LandDataSerializerTests.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs index 14e04621a4..70e87b3c08 100644 --- a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs +++ b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs @@ -96,15 +96,19 @@ namespace OpenSim.Framework.Serialization.Tests [Test] public void LandDataSerializerSerializeTest() { - string serialized = LandDataSerializer.Serialize(this.land); + string serialized = LandDataSerializer.Serialize(this.land).Replace("\r\n", "\n"); Assert.That(serialized.Length > 0, "Serialize(LandData) returned empty string"); - Assert.That(serialized == LandDataSerializerTest.preSerialized, - "result of Serialize(LandData) does not match expected result"); - string serializedWithParcelAccessList = LandDataSerializer.Serialize(this.landWithParcelAccessList); - Assert.That(serializedWithParcelAccessList.Length > 0, + // adding a simple boolean variable because resharper nUnit integration doesn't like this + // XML data in the Assert.That statement. Not sure why. + bool result = (serialized == preSerialized); + Assert.That(result, "result of Serialize LandData does not match expected result"); + + string serializedWithParcelAccessList = LandDataSerializer.Serialize(this.landWithParcelAccessList).Replace("\r\n", "\n"); + Assert.That(serializedWithParcelAccessList.Length > 0, "Serialize(LandData) returned empty string for LandData object with ParcelAccessList"); - Assert.That(serializedWithParcelAccessList == LandDataSerializerTest.preSerializedWithParcelAccessList, + result = (serializedWithParcelAccessList == preSerializedWithParcelAccessList); + Assert.That(result, "result of Serialize(LandData) does not match expected result (pre-serialized with parcel access list"); } From 53594e599e7f10bed441007f29a08d23e41e9188 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 5 May 2010 21:12:13 -0700 Subject: [PATCH 090/260] * Fixes Library bugs in grid mode. Partly a missing check and partly a missing configuration. * Made previous Robust config changes consistent in Robust.HG.ini.example --- .../Region/Framework/Scenes/Scene.Inventory.cs | 17 ++++++++++++++++- .../Framework/Scenes/Scene.PacketHandlers.cs | 4 ++-- bin/Robust.HG.ini.example | 2 ++ bin/Robust.ini.example | 2 +- bin/config-include/GridHypergrid.ini | 5 +++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 20760b2ad8..60f730de10 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1154,6 +1154,21 @@ namespace OpenSim.Region.Framework.Scenes if (folder == null) return; + // TODO: This code for looking in the folder for the library should be folded somewhere else + // so that this class doesn't have to know the details (and so that multiple libraries, etc. + // can be handled transparently). + InventoryFolderImpl fold = null; + if (LibraryService != null && LibraryService.LibraryRootFolder != null) + { + if ((fold = LibraryService.LibraryRootFolder.FindFolder(folder.ID)) != null) + { + client.SendInventoryFolderDetails( + fold.Owner, folder.ID, fold.RequestListOfItems(), + fold.RequestListOfFolders(), fold.Version, fetchFolders, fetchItems); + return; + } + } + // Fetch the folder contents InventoryCollection contents = InventoryService.GetFolderContent(client.AgentId, folder.ID); @@ -1164,7 +1179,7 @@ namespace OpenSim.Region.Framework.Scenes //m_log.DebugFormat("[AGENT INVENTORY]: Sending inventory folder contents ({0} nodes) for \"{1}\" to {2} {3}", // contents.Folders.Count + contents.Items.Count, containingFolder.Name, client.FirstName, client.LastName); - if (containingFolder != null) + if (containingFolder != null && containingFolder != null) client.SendInventoryFolderDetails(client.AgentId, folder.ID, contents.Items, contents.Folders, containingFolder.Version, fetchFolders, fetchItems); } diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index bc1023075d..e25b1f1503 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -513,8 +513,8 @@ namespace OpenSim.Region.Framework.Scenes { // FIXME MAYBE: We're not handling sortOrder! - // TODO: This code for looking in the folder for the library should be folded back into the - // CachedUserInfo so that this class doesn't have to know the details (and so that multiple libraries, etc. + // TODO: This code for looking in the folder for the library should be folded somewhere else + // so that this class doesn't have to know the details (and so that multiple libraries, etc. // can be handled transparently). InventoryFolderImpl fold = null; if (LibraryService != null && LibraryService.LibraryRootFolder != null) diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index 489b66f5a5..d8145af2db 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -125,6 +125,8 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" WelcomeMessage = "Welcome, Avatar!" + AllowRemoteSetLoginLevel = "false" + ; Defaults for the users, if none is specified in the useraccounts table entry (ServiceURLs) ; CHANGE THIS HomeURI = "http://127.0.0.1:8002" diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 2679523e4f..ae3a53c635 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -126,7 +126,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" WelcomeMessage = "Welcome, Avatar!" - AllowRemoteSetLoginLevel = "false"; + AllowRemoteSetLoginLevel = "false" [GridInfoService] diff --git a/bin/config-include/GridHypergrid.ini b/bin/config-include/GridHypergrid.ini index 051a87ec45..1e24f88749 100644 --- a/bin/config-include/GridHypergrid.ini +++ b/bin/config-include/GridHypergrid.ini @@ -40,5 +40,10 @@ AllowHypergridMapSearch = true +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + [Friends] Connector = "OpenSim.Services.Connectors.dll:FriendsServicesConnector" From adc34c712967e1d90aaa11e2bd9ff538da1641c0 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 6 May 2010 16:07:15 +0100 Subject: [PATCH 091/260] Ensure the show in search flag is cleared on ownership change. Also, when land is reclaimed, reset it's for sale flags so it can't be bought again right away. --- .../Region/CoreModules/World/Land/LandManagementModule.cs | 4 ++++ OpenSim/Region/CoreModules/World/Land/LandObject.cs | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index 5750aa42fc..a691acd566 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -1188,6 +1188,10 @@ namespace OpenSim.Region.CoreModules.World.Land land.LandData.ClaimDate = Util.UnixTimeSinceEpoch(); land.LandData.GroupID = UUID.Zero; land.LandData.IsGroupOwned = false; + land.LandData.SalePrice = 0; + land.LandData.AuthBuyerID = UUID.Zero; + land.LandData.Flags &= ~(uint) (ParcelFlags.ForSale | ParcelFlags.ForSaleObjects | ParcelFlags.SellParcelObjects | ParcelFlags.ShowDirectory); + m_scene.ForEachClient(SendParcelOverlay); land.SendLandUpdateToClient(true, remote_client); } diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs index aca551479f..39451421c3 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs @@ -247,7 +247,7 @@ namespace OpenSim.Region.CoreModules.World.Land newData.ClaimPrice = claimprice; newData.SalePrice = 0; newData.AuthBuyerID = UUID.Zero; - newData.Flags &= ~(uint) (ParcelFlags.ForSale | ParcelFlags.ForSaleObjects | ParcelFlags.SellParcelObjects); + newData.Flags &= ~(uint) (ParcelFlags.ForSale | ParcelFlags.ForSaleObjects | ParcelFlags.SellParcelObjects | ParcelFlags.ShowDirectory); m_scene.LandChannel.UpdateLandObject(LandData.LocalID, newData); SendLandUpdateToAvatarsOverMe(true); @@ -260,6 +260,9 @@ namespace OpenSim.Region.CoreModules.World.Land newData.GroupID = groupID; newData.IsGroupOwned = true; + // Reset show in directory flag on deed + newData.Flags &= ~(uint) (ParcelFlags.ForSale | ParcelFlags.ForSaleObjects | ParcelFlags.SellParcelObjects | ParcelFlags.ShowDirectory); + m_scene.LandChannel.UpdateLandObject(LandData.LocalID, newData); SendLandUpdateToAvatarsOverMe(true); From 9ecebcdf13ddb29ae1aeec0d080371b7d0696e4d Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 6 May 2010 16:38:23 +0100 Subject: [PATCH 092/260] Revert "Patch from mcortez: Update groups, add ALPHA Siman grid connector for groups" Causes an exception within HttpServer, headers have already been sent. This reverts commit 8187fccd258bf0936d3db8663844e07a7b81e9fc. --- .../XmlRpcGroups/GroupsMessagingModule.cs | 274 +++++++++--------- .../Avatar/XmlRpcGroups/GroupsModule.cs | 3 +- .../XmlRpcGroups/IGroupsServicesConnector.cs | 6 + .../SimianGroupsServicesConnectorModule.cs | 142 ++++++++- .../XmlRpcGroupsServicesConnectorModule.cs | 219 ++++++++++---- 5 files changed, 437 insertions(+), 207 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs index 00fe5df046..185d44de80 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsMessagingModule.cs @@ -28,41 +28,30 @@ using System; using System.Collections.Generic; using System.Reflection; - - using log4net; using Mono.Addins; using Nini.Config; - using OpenMetaverse; using OpenMetaverse.StructuredData; - using OpenSim.Framework; using OpenSim.Region.CoreModules.Framework.EventQueue; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; - using Caps = OpenSim.Framework.Capabilities.Caps; namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] - public class GroupsMessagingModule : ISharedRegionModule + public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private List m_sceneList = new List(); private IMessageTransferModule m_msgTransferModule = null; - private IGroupsModule m_groupsModule = null; - - // TODO: Move this off to the Groups Server - public Dictionary> m_agentsInGroupSession = new Dictionary>(); - public Dictionary> m_agentsDroppedSession = new Dictionary>(); - + private IGroupsServicesConnector m_groupData = null; // Config Options private bool m_groupMessagingEnabled = false; @@ -108,8 +97,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public void AddRegion(Scene scene) { - // NoOp + if (!m_groupMessagingEnabled) + return; + + scene.RegisterModuleInterface(this); } + public void RegionLoaded(Scene scene) { if (!m_groupMessagingEnabled) @@ -117,12 +110,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - m_groupsModule = scene.RequestModuleInterface(); + m_groupData = scene.RequestModuleInterface(); // No groups module, no groups messaging - if (m_groupsModule == null) + if (m_groupData == null) { - m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsModule, GroupsMessagingModule is now disabled."); + m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled."); Close(); m_groupMessagingEnabled = false; return; @@ -144,7 +137,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - + scene.EventManager.OnClientLogin += OnClientLogin; } public void RemoveRegion(Scene scene) @@ -172,7 +165,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups m_sceneList.Clear(); - m_groupsModule = null; + m_groupData = null; m_msgTransferModule = null; } @@ -197,8 +190,84 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups #endregion + /// + /// Not really needed, but does confirm that the group exists. + /// + public bool StartGroupChatSession(UUID agentID, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null); + + if (groupInfo != null) + { + return true; + } + else + { + return false; + } + } + + public void SendMessageToGroup(GridInstantMessage im, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + + foreach (GroupMembersData member in m_groupData.GetGroupMembers(UUID.Zero, groupID)) + { + if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID)) + { + // Don't deliver messages to people who have dropped this session + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); + continue; + } + + // Copy Message + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = groupID.Guid; + msg.fromAgentName = im.fromAgentName; + msg.message = im.message; + msg.dialog = im.dialog; + msg.offline = im.offline; + msg.ParentEstateID = im.ParentEstateID; + msg.Position = im.Position; + msg.RegionID = im.RegionID; + msg.binaryBucket = im.binaryBucket; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + + msg.fromAgentID = im.fromAgentID; + msg.fromGroup = true; + + msg.toAgentID = member.AgentID.Guid; + + IClientAPI client = GetActiveClient(member.AgentID); + if (client == null) + { + // If they're not local, forward across the grid + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); + } + else + { + // Deliver locally, directly + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); + ProcessMessageFromGroupSession(msg); + } + } + } + #region SimGridEventHandlers + void OnClientLogin(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name); + + + } + private void OnNewClient(IClientAPI client) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name); @@ -236,42 +305,46 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Session message from {0} going to agent {1}", msg.fromAgentName, msg.toAgentID); + UUID AgentID = new UUID(msg.fromAgentID); + UUID GroupID = new UUID(msg.imSessionID); + switch (msg.dialog) { case (byte)InstantMessageDialog.SessionAdd: - AddAgentToGroupSession(msg.fromAgentID, msg.imSessionID); + m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); break; case (byte)InstantMessageDialog.SessionDrop: - RemoveAgentFromGroupSession(msg.fromAgentID, msg.imSessionID); + m_groupData.AgentDroppedFromGroupChatSession(AgentID, GroupID); break; case (byte)InstantMessageDialog.SessionSend: - if (!m_agentsInGroupSession.ContainsKey(msg.toAgentID) - && !m_agentsDroppedSession.ContainsKey(msg.toAgentID)) + if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID) + && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID, GroupID) + ) { // Agent not in session and hasn't dropped from session // Add them to the session for now, and Invite them - AddAgentToGroupSession(msg.toAgentID, msg.imSessionID); + m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); UUID toAgentID = new UUID(msg.toAgentID); IClientAPI activeClient = GetActiveClient(toAgentID); if (activeClient != null) { - UUID groupID = new UUID(msg.fromAgentID); - - GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); + GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null); if (groupInfo != null) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message"); // Force? open the group session dialog??? + // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg); IEventQueue eq = activeClient.Scene.RequestModuleInterface(); eq.ChatterboxInvitation( - groupID + GroupID , groupInfo.GroupName , new UUID(msg.fromAgentID) - , msg.message, new UUID(msg.toAgentID) + , msg.message + , new UUID(msg.toAgentID) , msg.fromAgentName , msg.dialog , msg.timestamp @@ -285,7 +358,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups ); eq.ChatterBoxSessionAgentListUpdates( - new UUID(groupID) + new UUID(GroupID) , new UUID(msg.fromAgentID) , new UUID(msg.toAgentID) , false //canVoiceChat @@ -295,7 +368,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } } - else if (!m_agentsDroppedSession.ContainsKey(msg.toAgentID)) + else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID)) { // User hasn't dropped, so they're in the session, // maybe we should deliver it. @@ -321,56 +394,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups #endregion + #region ClientEvents - - private void RemoveAgentFromGroupSession(Guid agentID, Guid sessionID) - { - if (m_agentsInGroupSession.ContainsKey(sessionID)) - { - // If in session remove - if (m_agentsInGroupSession[sessionID].Contains(agentID)) - { - m_agentsInGroupSession[sessionID].Remove(agentID); - } - - // If not in dropped list, add - if (!m_agentsDroppedSession[sessionID].Contains(agentID)) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Dropped {1} from session {0}", sessionID, agentID); - m_agentsDroppedSession[sessionID].Add(agentID); - } - } - } - - private void AddAgentToGroupSession(Guid agentID, Guid sessionID) - { - // Add Session Status if it doesn't exist for this session - CreateGroupSessionTracking(sessionID); - - // If nessesary, remove from dropped list - if (m_agentsDroppedSession[sessionID].Contains(agentID)) - { - m_agentsDroppedSession[sessionID].Remove(agentID); - } - - // If nessesary, add to in session list - if (!m_agentsInGroupSession[sessionID].Contains(agentID)) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Added {1} to session {0}", sessionID, agentID); - m_agentsInGroupSession[sessionID].Add(agentID); - } - } - - private void CreateGroupSessionTracking(Guid sessionID) - { - if (!m_agentsInGroupSession.ContainsKey(sessionID)) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Creating session tracking for : {0}", sessionID); - m_agentsInGroupSession.Add(sessionID, new List()); - m_agentsDroppedSession.Add(sessionID, new List()); - } - } - private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) { if (m_debugEnabled) @@ -383,21 +408,23 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Start group IM session if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart)) { - UUID groupID = new UUID(im.toAgentID); + if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID); - GroupRecord groupInfo = m_groupsModule.GetGroupRecord(groupID); + UUID GroupID = new UUID(im.imSessionID); + UUID AgentID = new UUID(im.fromAgentID); + + GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null); + if (groupInfo != null) { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Start Group Session for {0}", groupInfo.GroupName); + m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); - AddAgentToGroupSession(im.fromAgentID, im.imSessionID); - - ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, groupID); + ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID); IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); queue.ChatterBoxSessionAgentListUpdates( - new UUID(groupID) - , new UUID(im.fromAgentID) + GroupID + , AgentID , new UUID(im.toAgentID) , false //canVoiceChat , false //isModerator @@ -409,64 +436,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups // Send a message from locally connected client to a group if ((im.dialog == (byte)InstantMessageDialog.SessionSend)) { - UUID groupID = new UUID(im.toAgentID); + UUID GroupID = new UUID(im.imSessionID); + UUID AgentID = new UUID(im.fromAgentID); - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", groupID, im.imSessionID.ToString()); + if (m_debugEnabled) + m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString()); - SendMessageToGroup(im, groupID); + //If this agent is sending a message, then they want to be in the session + m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID); + + SendMessageToGroup(im, GroupID); } } #endregion - private void SendMessageToGroup(GridInstantMessage im, UUID groupID) - { - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); - - foreach (GroupMembersData member in m_groupsModule.GroupMembersRequest(null, groupID)) - { - if (!m_agentsDroppedSession.ContainsKey(im.imSessionID) || m_agentsDroppedSession[im.imSessionID].Contains(member.AgentID.Guid)) - { - // Don't deliver messages to people who have dropped this session - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID); - continue; - } - - // Copy Message - GridInstantMessage msg = new GridInstantMessage(); - msg.imSessionID = im.imSessionID; - msg.fromAgentName = im.fromAgentName; - msg.message = im.message; - msg.dialog = im.dialog; - msg.offline = im.offline; - msg.ParentEstateID = im.ParentEstateID; - msg.Position = im.Position; - msg.RegionID = im.RegionID; - msg.binaryBucket = im.binaryBucket; - msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); - - // Updat Pertinate fields to make it a "group message" - msg.fromAgentID = groupID.Guid; - msg.fromGroup = true; - - msg.toAgentID = member.AgentID.Guid; - - IClientAPI client = GetActiveClient(member.AgentID); - if (client == null) - { - // If they're not local, forward across the grid - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} via Grid", member.AgentID); - m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { }); - } - else - { - // Deliver locally, directly - if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Passing to ProcessMessageFromGroupSession to deliver to {0} locally", client.Name); - ProcessMessageFromGroupSession(msg); - } - } - } - void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID) { if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); @@ -518,6 +502,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups /// private IClientAPI GetActiveClient(UUID agentID) { + if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Looking for local client {0}", agentID); + IClientAPI child = null; // Try root avatar first @@ -529,16 +515,26 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups ScenePresence user = (ScenePresence)scene.Entities[agentID]; if (!user.IsChildAgent) { + if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Found root agent for client : {0}", user.ControllingClient.Name); return user.ControllingClient; } else { + if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Found child agent for client : {0}", user.ControllingClient.Name); child = user.ControllingClient; } } } // If we didn't find a root, then just return whichever child we found, or null if none + if (child == null) + { + if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID); + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name); + } return child; } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs index b2b8110c64..56c0d985f1 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/GroupsModule.cs @@ -176,7 +176,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - // The InstantMessageModule itself doesn't do this, // so lets see if things explode if we don't do it // scene.EventManager.OnClientClosed += OnClientClosed; @@ -510,7 +509,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups IClientAPI ejectee = GetActiveClient(ejecteeID); if (ejectee != null) { - UUID groupID = new UUID(im.fromAgentID); + UUID groupID = new UUID(im.imSessionID); ejectee.SendAgentDropGroup(groupID); } } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs index 6487967971..a046e094cd 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs @@ -71,6 +71,12 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups void AddGroupNotice(UUID RequestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket); GroupNoticeInfo GetGroupNotice(UUID RequestingAgentID, UUID noticeID); List GetGroupNotices(UUID RequestingAgentID, UUID GroupID); + + void ResetAgentGroupChatSessions(UUID agentID); + bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID); + bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID); + void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID); + void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID); } public class GroupInviteInfo diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs index bc05b0f246..9363205263 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs @@ -55,6 +55,9 @@ using OpenSim.Services.Interfaces; * UserID -> Group -> ActiveGroup * + GroupID * + * UserID -> GroupSessionDropped -> GroupID + * UserID -> GroupSessionInvited -> GroupID + * * UserID -> GroupMember -> GroupID * + SelectedRoleID [UUID] * + AcceptNotices [bool] @@ -63,6 +66,7 @@ using OpenSim.Services.Interfaces; * * UserID -> GroupRole[GroupID] -> RoleID * + * * GroupID -> Group -> GroupName * + Charter * + ShowInList @@ -159,10 +163,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private bool m_connectorEnabled = false; - private string m_serviceURL = string.Empty; + private string m_groupsServerURI = string.Empty; private bool m_debugEnabled = false; + private ExpiringCache m_memoryCache; + private int m_cacheTimeout = 30; + // private IUserAccountService m_accountService = null; @@ -199,17 +206,33 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return; } - m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); + m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name); - m_serviceURL = groupsConfig.GetString("XmlRpcServiceURL", string.Empty); - if ((m_serviceURL == null) || - (m_serviceURL == string.Empty)) + m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); + if ((m_groupsServerURI == null) || + (m_groupsServerURI == string.Empty)) { - m_log.ErrorFormat("Please specify a valid Simian Server URL for XmlRpcServiceURL in OpenSim.ini, [Groups]"); + m_log.ErrorFormat("Please specify a valid Simian Server for GroupsServerURI in OpenSim.ini, [Groups]"); m_connectorEnabled = false; return; } + + m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30); + if (m_cacheTimeout == 0) + { + m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Disabled."); + } + else + { + m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Timeout set to {0}.", m_cacheTimeout); + } + + + + m_memoryCache = new ExpiringCache(); + + // If we got all the config options we need, lets start'er'up m_connectorEnabled = true; @@ -220,7 +243,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups public void Close() { - m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); + m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Closing {0}", this.Name); } public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) @@ -653,7 +676,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); + OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)response["Entries"]; @@ -998,6 +1021,52 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } #endregion + #region GroupSessionTracking + + public void ResetAgentGroupChatSessions(UUID agentID) + { + Dictionary agentSessions; + + if (SimianGetGenericEntries(agentID, "GroupSessionDropped", out agentSessions)) + { + foreach (string GroupID in agentSessions.Keys) + { + SimianRemoveGenericEntry(agentID, "GroupSessionDropped", GroupID); + } + } + + if (SimianGetGenericEntries(agentID, "GroupSessionInvited", out agentSessions)) + { + foreach (string GroupID in agentSessions.Keys) + { + SimianRemoveGenericEntry(agentID, "GroupSessionInvited", GroupID); + } + } + } + + public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID) + { + OSDMap session; + return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session); + } + + public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID) + { + SimianAddGeneric(agentID, "GroupSessionDropped", groupID.ToString(), new OSDMap()); + } + + public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID) + { + SimianAddGeneric(agentID, "GroupSessionInvited", groupID.ToString(), new OSDMap()); + } + + public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID) + { + OSDMap session; + return SimianGetGenericEntry(agentID, "GroupSessionDropped", groupID.ToString(), out session); + } + + #endregion private void EnsureRoleNotSelectedByMember(UUID groupID, UUID roleID, UUID userID) { @@ -1036,7 +1105,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); + OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean()) { return true; @@ -1063,7 +1132,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); + OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; @@ -1103,7 +1172,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); + OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; @@ -1144,7 +1213,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap Response = WebUtil.PostToService(m_serviceURL, RequestArgs); + OSDMap Response = CachedPostRequest(RequestArgs); if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) { OSDArray entryArray = (OSDArray)Response["Entries"]; @@ -1184,7 +1253,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups - OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); + OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { maps = new Dictionary(); @@ -1222,7 +1291,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups - OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); + OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) { maps = new Dictionary(); @@ -1260,7 +1329,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups }; - OSDMap response = WebUtil.PostToService(m_serviceURL, requestArgs); + OSDMap response = CachedPostRequest(requestArgs); if (response["Success"].AsBoolean()) { return true; @@ -1272,6 +1341,49 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups } } #endregion + + #region CheesyCache + OSDMap CachedPostRequest(NameValueCollection requestArgs) + { + // Immediately forward the request if the cache is disabled. + if (m_cacheTimeout == 0) + { + return WebUtil.PostToService(m_groupsServerURI, requestArgs); + } + + // Check if this is an update or a request + if ( requestArgs["RequestMethod"] == "RemoveGeneric" + || requestArgs["RequestMethod"] == "AddGeneric" + ) + + { + // Any and all updates cause the cache to clear + m_memoryCache.Clear(); + + // Send update to server, return the response without caching it + return WebUtil.PostToService(m_groupsServerURI, requestArgs); + + } + + // If we're not doing an update, we must be requesting data + + // Create the cache key for the request and see if we have it cached + string CacheKey = WebUtil.BuildQueryString(requestArgs); + OSDMap response = null; + if (!m_memoryCache.TryGetValue(CacheKey, out response)) + { + // if it wasn't in the cache, pass the request to the Simian Grid Services + response = WebUtil.PostToService(m_groupsServerURI, requestArgs); + + // and cache the response + m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout)); + } + + // return cached response + return response; + } + #endregion + } } diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs index ab343c8dce..79b9a167d0 100644 --- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs @@ -29,6 +29,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Reflection; +using System.Text; using Nwc.XmlRpc; @@ -61,7 +62,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private bool m_connectorEnabled = false; - private string m_serviceURL = string.Empty; + private string m_groupsServerURI = string.Empty; private bool m_disableKeepAlive = false; @@ -69,7 +70,17 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private string m_groupWriteKey = string.Empty; private IUserAccountService m_accountService = null; - + + private ExpiringCache m_memoryCache; + private int m_cacheTimeout = 30; + + // Used to track which agents are have dropped from a group chat session + // Should be reset per agent, on logon + // TODO: move this to Flotsam XmlRpc Service + // SessionID, List + private Dictionary> m_groupsAgentsDroppedFromChatSession = new Dictionary>(); + private Dictionary> m_groupsAgentsInvitedToChatSession = new Dictionary>(); + #region IRegionModuleBase Members @@ -104,13 +115,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return; } - m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); + m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name); - m_serviceURL = groupsConfig.GetString("XmlRpcServiceURL", string.Empty); - if ((m_serviceURL == null) || - (m_serviceURL == string.Empty)) + m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); + if ((m_groupsServerURI == null) || + (m_groupsServerURI == string.Empty)) { - m_log.ErrorFormat("Please specify a valid URL for XmlRpcServiceURL in OpenSim.ini, [Groups]"); + m_log.ErrorFormat("Please specify a valid URL for GroupsServerURI in OpenSim.ini, [Groups]"); m_connectorEnabled = false; return; } @@ -120,17 +131,26 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups m_groupReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty); m_groupWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty); - + m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30); + if (m_cacheTimeout == 0) + { + m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Disabled."); + } + else + { + m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Timeout set to {0}.", m_cacheTimeout); + } // If we got all the config options we need, lets start'er'up + m_memoryCache = new ExpiringCache(); m_connectorEnabled = true; } } public void Close() { - m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); + m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Closing {0}", this.Name); } public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) @@ -756,6 +776,69 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param); } + + + + #endregion + + #region GroupSessionTracking + + public void ResetAgentGroupChatSessions(UUID agentID) + { + foreach (List agentList in m_groupsAgentsDroppedFromChatSession.Values) + { + agentList.Remove(agentID); + } + } + + public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID) + { + // If we're tracking this group, and we can find them in the tracking, then they've been invited + return m_groupsAgentsInvitedToChatSession.ContainsKey(groupID) + && m_groupsAgentsInvitedToChatSession[groupID].Contains(agentID); + } + + public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID) + { + // If we're tracking drops for this group, + // and we find them, well... then they've dropped + return m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID) + && m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID); + } + + public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID) + { + if (m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)) + { + // If not in dropped list, add + if (!m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID)) + { + m_groupsAgentsDroppedFromChatSession[groupID].Add(agentID); + } + } + } + + public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID) + { + // Add Session Status if it doesn't exist for this session + CreateGroupChatSessionTracking(groupID); + + // If nessesary, remove from dropped list + if (m_groupsAgentsDroppedFromChatSession[groupID].Contains(agentID)) + { + m_groupsAgentsDroppedFromChatSession[groupID].Remove(agentID); + } + } + + private void CreateGroupChatSessionTracking(UUID groupID) + { + if (!m_groupsAgentsDroppedFromChatSession.ContainsKey(groupID)) + { + m_groupsAgentsDroppedFromChatSession.Add(groupID, new List()); + m_groupsAgentsInvitedToChatSession.Add(groupID, new List()); + } + + } #endregion #region XmlRpcHashtableMarshalling @@ -849,50 +932,84 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups ///
private Hashtable XmlRpcCall(UUID requestingAgentID, string function, Hashtable param) { - string UserService; - UUID SessionID; - GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID); - param.Add("requestingAgentID", requestingAgentID.ToString()); - param.Add("RequestingAgentUserService", UserService); - param.Add("RequestingSessionID", SessionID.ToString()); - - - param.Add("ReadKey", m_groupReadKey); - param.Add("WriteKey", m_groupWriteKey); - - - IList parameters = new ArrayList(); - parameters.Add(param); - - ConfigurableKeepAliveXmlRpcRequest req; - req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive); - XmlRpcResponse resp = null; + string CacheKey = null; - try + // Only bother with the cache if it isn't disabled. + if (m_cacheTimeout > 0) { - resp = req.Send(m_serviceURL, 10000); + if (!function.StartsWith("groups.get")) + { + // Any and all updates cause the cache to clear + m_memoryCache.Clear(); + } + else + { + StringBuilder sb = new StringBuilder(requestingAgentID + function); + foreach (object key in param.Keys) + { + if (param[key] != null) + { + sb.AppendFormat(",{0}:{1}", key.ToString(), param[key].ToString()); + } + } + + CacheKey = sb.ToString(); + m_memoryCache.TryGetValue(CacheKey, out resp); + } + } - catch (Exception e) + + if( resp == null ) { - + string UserService; + UUID SessionID; + GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID); + param.Add("requestingAgentID", requestingAgentID.ToString()); + param.Add("RequestingAgentUserService", UserService); + param.Add("RequestingSessionID", SessionID.ToString()); - m_log.ErrorFormat("[XMLRPCGROUPDATA]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function); - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", e.ToString()); - foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine },StringSplitOptions.None)) + param.Add("ReadKey", m_groupReadKey); + param.Add("WriteKey", m_groupWriteKey); + + + IList parameters = new ArrayList(); + parameters.Add(param); + + ConfigurableKeepAliveXmlRpcRequest req; + req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive); + + + try { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", ResponseLine); - } + resp = req.Send(m_groupsServerURI, 10000); - foreach (string key in param.Keys) + if ((m_cacheTimeout > 0) && (CacheKey != null)) + { + m_memoryCache.AddOrUpdate(CacheKey, resp, TimeSpan.FromSeconds(m_cacheTimeout)); + } + + } + catch (Exception e) { - m_log.WarnFormat("[XMLRPCGROUPDATA]: {0} :: {1}", key, param[key].ToString()); - } + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", e.ToString()); - Hashtable respData = new Hashtable(); - respData.Add("error", e.ToString()); - return respData; + foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)) + { + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", ResponseLine); + } + + foreach (string key in param.Keys) + { + m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", key, param[key].ToString()); + } + + Hashtable respData = new Hashtable(); + respData.Add("error", e.ToString()); + return respData; + } } if (resp.Value is Hashtable) @@ -906,21 +1023,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups return respData; } - m_log.ErrorFormat("[XMLRPCGROUPDATA]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); if (resp.Value is ArrayList) { ArrayList al = (ArrayList)resp.Value; - m_log.ErrorFormat("[XMLRPCGROUPDATA]: Contains {0} elements", al.Count); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Contains {0} elements", al.Count); foreach (object o in al) { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} :: {1}", o.GetType().ToString(), o.ToString()); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", o.GetType().ToString(), o.ToString()); } } else { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: Function returned: {0}", resp.Value.ToString()); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Function returned: {0}", resp.Value.ToString()); } Hashtable error = new Hashtable(); @@ -930,16 +1047,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups private void LogRespDataToConsoleError(Hashtable respData) { - m_log.Error("[XMLRPCGROUPDATA]: Error:"); + m_log.Error("[XMLRPC-GROUPS-CONNECTOR]: Error:"); foreach (string key in respData.Keys) { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: Key: {0}", key); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Key: {0}", key); string[] lines = respData[key].ToString().Split(new char[] { '\n' }); foreach (string line in lines) { - m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0}", line); + m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0}", line); } } @@ -948,8 +1065,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups /// /// Group Request Tokens are an attempt to allow the groups service to authenticate - /// requests. Currently uses UserService, AgentID, and SessionID - /// TODO: Find a better way to do this. + /// requests. + /// TODO: This broke after the big grid refactor, either find a better way, or discard this /// /// /// From 907fce8406ea4db252a53ed6f0963d08ec4eff5a Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 7 May 2010 00:50:26 +0100 Subject: [PATCH 093/260] Remove land that is being abandoned from search. This is now consistent with sale and reclaim. --- OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index a691acd566..32be16020c 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -1166,6 +1166,7 @@ namespace OpenSim.Region.CoreModules.World.Land land.LandData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; land.LandData.GroupID = UUID.Zero; land.LandData.IsGroupOwned = false; + land.LandData.Flags &= ~(uint) (ParcelFlags.ForSale | ParcelFlags.ForSaleObjects | ParcelFlags.SellParcelObjects | ParcelFlags.ShowDirectory); m_scene.ForEachClient(SendParcelOverlay); land.SendLandUpdateToClient(true, remote_client); } From 6182d71326cc4041d867a447270179442c768e7f Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 7 May 2010 00:52:52 +0100 Subject: [PATCH 094/260] Also remove sale and search flags on god owner change. --- OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index 32be16020c..139e6ff13d 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -1144,6 +1144,7 @@ namespace OpenSim.Region.CoreModules.World.Land land.LandData.OwnerID = ownerID; land.LandData.GroupID = UUID.Zero; land.LandData.IsGroupOwned = false; + land.LandData.Flags &= ~(uint) (ParcelFlags.ForSale | ParcelFlags.ForSaleObjects | ParcelFlags.SellParcelObjects | ParcelFlags.ShowDirectory); m_scene.ForEachClient(SendParcelOverlay); land.SendLandUpdateToClient(true, remote_client); From 5d1e9947ed43490fb458af62bd9e8596a816f7b8 Mon Sep 17 00:00:00 2001 From: dahlia Date: Thu, 6 May 2010 21:36:27 -0700 Subject: [PATCH 095/260] Sculpt meshing refactoring - improves mesh accuracy and UV mapping Sync with PrimMesher r55 --- OpenSim/Region/Physics/Meshing/SculptMap.cs | 169 +++ OpenSim/Region/Physics/Meshing/SculptMesh.cs | 1280 +++++++++--------- 2 files changed, 810 insertions(+), 639 deletions(-) create mode 100644 OpenSim/Region/Physics/Meshing/SculptMap.cs diff --git a/OpenSim/Region/Physics/Meshing/SculptMap.cs b/OpenSim/Region/Physics/Meshing/SculptMap.cs new file mode 100644 index 0000000000..4d3f82b22b --- /dev/null +++ b/OpenSim/Region/Physics/Meshing/SculptMap.cs @@ -0,0 +1,169 @@ +/* + * Copyright (c) Contributors + * 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 OpenSimulator 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. + */ + +// to build without references to System.Drawing, comment this out +#define SYSTEM_DRAWING + +using System; +using System.Collections.Generic; +using System.Text; + +#if SYSTEM_DRAWING +using System.Drawing; +using System.Drawing.Imaging; + +namespace PrimMesher +{ + public class SculptMap + { + public int width; + public int height; + public byte[] redBytes; + public byte[] greenBytes; + public byte[] blueBytes; + + public SculptMap() + { + } + + public SculptMap(Bitmap bm, int lod) + { + int bmW = bm.Width; + int bmH = bm.Height; + + if (bmW == 0 || bmH == 0) + throw new Exception("SculptMap: bitmap has no data"); + + int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image + + width = bmW; + height = bmH; + while (width * height > numLodPixels) + { + width >>= 1; + height >>= 1; + } + + width >>= 1; + height >>= 1; + + try + { + if (!(bmW == width * 2 && bmH == height * 2)) + bm = ScaleImage(bm, width * 2, height * 2, + System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor); + } + + catch (Exception e) + { + throw new Exception("Exception in ScaleImage(): e: " + e.ToString()); + } + + + int numBytes = (width + 1) * (height + 1); + redBytes = new byte[numBytes]; + greenBytes = new byte[numBytes]; + blueBytes = new byte[numBytes]; + + int byteNdx = 0; + + try + { + for (int y = 0; y <= height; y++) + { + for (int x = 0; x <= width; x++) + { + int bmY = y < height ? y * 2 : y * 2 - 1; + int bmX = x < width ? x * 2 : x * 2 - 1; + Color c = bm.GetPixel(bmX, bmY); + + redBytes[byteNdx] = c.R; + greenBytes[byteNdx] = c.G; + blueBytes[byteNdx] = c.B; + + ++byteNdx; + } + } + } + catch (Exception e) + { + throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString()); + } + + width++; + height++; + } + + public List> ToRows(bool mirror) + { + int numRows = height; + int numCols = width; + + List> rows = new List>(numRows); + + float pixScale = 1.0f / 255; + + int rowNdx, colNdx; + int smNdx = 0; + + for (rowNdx = 0; rowNdx < numRows; rowNdx++) + { + List row = new List(numCols); + for (colNdx = 0; colNdx < numCols; colNdx++) + { + if (mirror) + row.Add(new Coord(-(redBytes[smNdx] * pixScale - 0.5f), (greenBytes[smNdx] * pixScale - 0.5f), blueBytes[smNdx] * pixScale - 0.5f)); + else + row.Add(new Coord(redBytes[smNdx] * pixScale - 0.5f, greenBytes[smNdx] * pixScale - 0.5f, blueBytes[smNdx] * pixScale - 0.5f)); + + ++smNdx; + } + rows.Add(row); + } + return rows; + } + + private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight, + System.Drawing.Drawing2D.InterpolationMode interpMode) + { + Bitmap scaledImage = new Bitmap(srcImage, destWidth, destHeight); + scaledImage.SetResolution(96.0f, 96.0f); + + Graphics grPhoto = Graphics.FromImage(scaledImage); + grPhoto.InterpolationMode = interpMode; + + grPhoto.DrawImage(srcImage, + new Rectangle(0, 0, destWidth, destHeight), + new Rectangle(0, 0, srcImage.Width, srcImage.Height), + GraphicsUnit.Pixel); + + grPhoto.Dispose(); + return scaledImage; + } + } +} +#endif diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs index ebc5be61b1..06606c39db 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs @@ -1,645 +1,647 @@ -/* - * Copyright (c) Contributors - * 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 OpenSimulator 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. - */ - -// to build without references to System.Drawing, comment this out -#define SYSTEM_DRAWING - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -#if SYSTEM_DRAWING -using System.Drawing; -using System.Drawing.Imaging; -#endif - -namespace PrimMesher -{ - - public class SculptMesh - { - public List coords; - public List faces; - - public List viewerFaces; - public List normals; - public List uvs; - - public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; - -#if SYSTEM_DRAWING - private Bitmap ScaleImage(Bitmap srcImage, float scale, bool removeAlpha) - { - int sourceWidth = srcImage.Width; - int sourceHeight = srcImage.Height; - int sourceX = 0; - int sourceY = 0; - - int destX = 0; - int destY = 0; - int destWidth = (int)(srcImage.Width * scale); - int destHeight = (int)(srcImage.Height * scale); - - Bitmap scaledImage; - - if (removeAlpha) - { - if (srcImage.PixelFormat == PixelFormat.Format32bppArgb) - for (int y = 0; y < srcImage.Height; y++) - for (int x = 0; x < srcImage.Width; x++) - { - Color c = srcImage.GetPixel(x, y); - srcImage.SetPixel(x, y, Color.FromArgb(255, c.R, c.G, c.B)); - } - - scaledImage = new Bitmap(destWidth, destHeight, - PixelFormat.Format24bppRgb); - } - else - scaledImage = new Bitmap(srcImage, destWidth, destHeight); - - scaledImage.SetResolution(96.0f, 96.0f); - - Graphics grPhoto = Graphics.FromImage(scaledImage); - grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low; - - grPhoto.DrawImage(srcImage, - new Rectangle(destX, destY, destWidth, destHeight), - new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), - GraphicsUnit.Pixel); - - grPhoto.Dispose(); - return scaledImage; - } - - - public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) - { - Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); - SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); - bitmap.Dispose(); - return sculptMesh; - } - - public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) - { - Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); - _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); - bitmap.Dispose(); - } -#endif - - /// - /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications - /// Construct a sculpt mesh from a 2D array of floats - /// - /// - /// - /// - /// - /// - /// - public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) - { - float xStep, yStep; - float uStep, vStep; - - int numYElements = zMap.GetLength(0); - int numXElements = zMap.GetLength(1); - - try - { - xStep = (xEnd - xBegin) / (float)(numXElements - 1); - yStep = (yEnd - yBegin) / (float)(numYElements - 1); - - uStep = 1.0f / (numXElements - 1); - vStep = 1.0f / (numYElements - 1); - } - catch (DivideByZeroException) - { - return; - } - - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - viewerFaces = new List(); - - int p1, p2, p3, p4; - - int x, y; - int xStart = 0, yStart = 0; - - for (y = yStart; y < numYElements; y++) - { - int rowOffset = y * numXElements; - - for (x = xStart; x < numXElements; x++) - { +/* + * Copyright (c) Contributors + * 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 OpenSimulator 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. + */ + +// to build without references to System.Drawing, comment this out +#define SYSTEM_DRAWING + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +#if SYSTEM_DRAWING +using System.Drawing; +using System.Drawing.Imaging; +#endif + +namespace PrimMesher +{ + + public class SculptMesh + { + public List coords; + public List faces; + + public List viewerFaces; + public List normals; + public List uvs; + + public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; + +#if SYSTEM_DRAWING + + public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) + { + Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); + SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); + bitmap.Dispose(); + return sculptMesh; + } + + + public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) + { + Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); + _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); + bitmap.Dispose(); + } +#endif + + /// + /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications + /// Construct a sculpt mesh from a 2D array of floats + /// + /// + /// + /// + /// + /// + /// + public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) + { + float xStep, yStep; + float uStep, vStep; + + int numYElements = zMap.GetLength(0); + int numXElements = zMap.GetLength(1); + + try + { + xStep = (xEnd - xBegin) / (float)(numXElements - 1); + yStep = (yEnd - yBegin) / (float)(numYElements - 1); + + uStep = 1.0f / (numXElements - 1); + vStep = 1.0f / (numYElements - 1); + } + catch (DivideByZeroException) + { + return; + } + + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + viewerFaces = new List(); + + int p1, p2, p3, p4; + + int x, y; + int xStart = 0, yStart = 0; + + for (y = yStart; y < numYElements; y++) + { + int rowOffset = y * numXElements; + + for (x = xStart; x < numXElements; x++) + { + /* + * p1-----p2 + * | \ f2 | + * | \ | + * | f1 \| + * p3-----p4 + */ + + p4 = rowOffset + x; + p3 = p4 - 1; + + p2 = p4 - numXElements; + p1 = p3 - numXElements; + + Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); + this.coords.Add(c); + if (viewerMode) + { + this.normals.Add(new Coord()); + this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); + } + + if (y > 0 && x > 0) + { + Face f1, f2; + + if (viewerMode) + { + f1 = new Face(p1, p4, p3, p1, p4, p3); + f1.uv1 = p1; + f1.uv2 = p4; + f1.uv3 = p3; + + f2 = new Face(p1, p2, p4, p1, p2, p4); + f2.uv1 = p1; + f2.uv2 = p2; + f2.uv3 = p4; + } + else + { + f1 = new Face(p1, p4, p3); + f2 = new Face(p1, p2, p4); + } + + this.faces.Add(f1); + this.faces.Add(f2); + } + } + } + + if (viewerMode) + calcVertexNormals(SculptType.plane, numXElements, numYElements); + } + +#if SYSTEM_DRAWING + public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) + { + _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); + } + + public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); + } +#endif + + public SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(rows, sculptType, viewerMode, mirror, invert); + } + +#if SYSTEM_DRAWING + /// + /// converts a bitmap to a list of lists of coords, while scaling the image. + /// the scaling is done in floating point so as to allow for reduced vertex position + /// quantization as the position will be averaged between pixel values. this routine will + /// likely fail if the bitmap width and height are not powers of 2. + /// + /// + /// + /// + /// + private List> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) + { + int numRows = bitmap.Height / scale; + int numCols = bitmap.Width / scale; + List> rows = new List>(numRows); + + float pixScale = 1.0f / (scale * scale); + pixScale /= 255; + + int imageX, imageY = 0; + + int rowNdx, colNdx; + + for (rowNdx = 0; rowNdx < numRows; rowNdx++) + { + List row = new List(numCols); + for (colNdx = 0; colNdx < numCols; colNdx++) + { + imageX = colNdx * scale; + int imageYStart = rowNdx * scale; + int imageYEnd = imageYStart + scale; + int imageXEnd = imageX + scale; + float rSum = 0.0f; + float gSum = 0.0f; + float bSum = 0.0f; + for (; imageX < imageXEnd; imageX++) + { + for (imageY = imageYStart; imageY < imageYEnd; imageY++) + { + Color c = bitmap.GetPixel(imageX, imageY); + if (c.A != 255) + { + bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); + c = bitmap.GetPixel(imageX, imageY); + } + rSum += c.R; + gSum += c.G; + bSum += c.B; + } + } + if (mirror) + row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); + else + row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); + + } + rows.Add(row); + } + return rows; + } + + private List> bitmap2CoordsSampled(Bitmap bitmap, int scale, bool mirror) + { + int numRows = bitmap.Height / scale; + int numCols = bitmap.Width / scale; + List> rows = new List>(numRows); + + float pixScale = 1.0f / 256.0f; + + int imageX, imageY = 0; + + int rowNdx, colNdx; + + for (rowNdx = 0; rowNdx <= numRows; rowNdx++) + { + List row = new List(numCols); + imageY = rowNdx * scale; + if (rowNdx == numRows) imageY--; + for (colNdx = 0; colNdx <= numCols; colNdx++) + { + imageX = colNdx * scale; + if (colNdx == numCols) imageX--; + + Color c = bitmap.GetPixel(imageX, imageY); + if (c.A != 255) + { + bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); + c = bitmap.GetPixel(imageX, imageY); + } + + if (mirror) + row.Add(new Coord(-(c.R * pixScale - 0.5f), c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); + else + row.Add(new Coord(c.R * pixScale - 0.5f, c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); + + } + rows.Add(row); + } + return rows; + } + + + void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert); + } +#endif + + void _SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) + { + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + sculptType = (SculptType)(((int)sculptType) & 0x07); + + if (mirror) + if (sculptType == SculptType.plane) + invert = !invert; + + viewerFaces = new List(); + + int width = rows[0].Count; + + int p1, p2, p3, p4; + + int imageX, imageY; + + if (sculptType != SculptType.plane) + { + if (rows.Count % 2 == 0) + { + for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) + rows[rowNdx].Add(rows[rowNdx][0]); + } + else + { + int lastIndex = rows[0].Count - 1; + + for (int i = 0; i < rows.Count; i++) + rows[i][0] = rows[i][lastIndex]; + } + } + + Coord topPole = rows[0][width / 2]; + Coord bottomPole = rows[rows.Count - 1][width / 2]; + + if (sculptType == SculptType.sphere) + { + if (rows.Count % 2 == 0) + { + int count = rows[0].Count; + List topPoleRow = new List(count); + List bottomPoleRow = new List(count); + + for (int i = 0; i < count; i++) + { + topPoleRow.Add(topPole); + bottomPoleRow.Add(bottomPole); + } + rows.Insert(0, topPoleRow); + rows.Add(bottomPoleRow); + } + else + { + int count = rows[0].Count; + + List topPoleRow = rows[0]; + List bottomPoleRow = rows[rows.Count - 1]; + + for (int i = 0; i < count; i++) + { + topPoleRow[i] = topPole; + bottomPoleRow[i] = bottomPole; + } + } + } + + if (sculptType == SculptType.torus) + rows.Add(rows[0]); + + int coordsDown = rows.Count; + int coordsAcross = rows[0].Count; + int lastColumn = coordsAcross - 1; + + float widthUnit = 1.0f / (coordsAcross - 1); + float heightUnit = 1.0f / (coordsDown - 1); + + for (imageY = 0; imageY < coordsDown; imageY++) + { + int rowOffset = imageY * coordsAcross; + + for (imageX = 0; imageX < coordsAcross; imageX++) + { /* * p1-----p2 * | \ f2 | * | \ | * | f1 \| * p3-----p4 - */ - - p4 = rowOffset + x; - p3 = p4 - 1; - - p2 = p4 - numXElements; - p1 = p3 - numXElements; - - Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); - this.coords.Add(c); - if (viewerMode) - { - this.normals.Add(new Coord()); - this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); - } - - if (y > 0 && x > 0) - { - Face f1, f2; - - if (viewerMode) - { - f1 = new Face(p1, p4, p3, p1, p4, p3); - f1.uv1 = p1; - f1.uv2 = p4; - f1.uv3 = p3; - - f2 = new Face(p1, p2, p4, p1, p2, p4); - f2.uv1 = p1; - f2.uv2 = p2; - f2.uv3 = p4; - } - else - { - f1 = new Face(p1, p4, p3); - f2 = new Face(p1, p2, p4); - } - - this.faces.Add(f1); - this.faces.Add(f2); - } - } - } - - if (viewerMode) - calcVertexNormals(SculptType.plane, numXElements, numYElements); - } - -#if SYSTEM_DRAWING - public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) - { - _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); - } - - public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); - } -#endif - - public SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(rows, sculptType, viewerMode, mirror, invert); - } - -#if SYSTEM_DRAWING - /// - /// converts a bitmap to a list of lists of coords, while scaling the image. - /// the scaling is done in floating point so as to allow for reduced vertex position - /// quantization as the position will be averaged between pixel values. this routine will - /// likely fail if the bitmap width and height are not powers of 2. - /// - /// - /// - /// - /// - private List> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) - { - int numRows = bitmap.Height / scale; - int numCols = bitmap.Width / scale; - List> rows = new List>(numRows); - - float pixScale = 1.0f / (scale * scale); - pixScale /= 255; - - int imageX, imageY = 0; - - int rowNdx, colNdx; - - for (rowNdx = 0; rowNdx < numRows; rowNdx++) - { - List row = new List(numCols); - for (colNdx = 0; colNdx < numCols; colNdx++) - { - imageX = colNdx * scale; - int imageYStart = rowNdx * scale; - int imageYEnd = imageYStart + scale; - int imageXEnd = imageX + scale; - float rSum = 0.0f; - float gSum = 0.0f; - float bSum = 0.0f; - for (; imageX < imageXEnd; imageX++) - { - for (imageY = imageYStart; imageY < imageYEnd; imageY++) - { - Color c = bitmap.GetPixel(imageX, imageY); - if (c.A != 255) - { - bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); - c = bitmap.GetPixel(imageX, imageY); - } - rSum += c.R; - gSum += c.G; - bSum += c.B; - } - } - if (mirror) - row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); - else - row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); - - } - rows.Add(row); - } - return rows; - } - - - void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) - { - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - sculptType = (SculptType)(((int)sculptType) & 0x07); - - if (mirror) - if (sculptType == SculptType.plane) - invert = !invert; - - float sculptBitmapLod = (float)Math.Sqrt(sculptBitmap.Width * sculptBitmap.Height); - - float sourceScaleFactor = (float)(lod) / sculptBitmapLod; - - float fScale = 1.0f / sourceScaleFactor; - - int iScale = (int)fScale; - if (iScale < 1) iScale = 1; - if (iScale > 2 && iScale % 2 == 0) - _SculptMesh(bitmap2Coords(ScaleImage(sculptBitmap, 64.0f / sculptBitmapLod, true), 64 / lod, mirror), sculptType, viewerMode, mirror, invert); - else - _SculptMesh(bitmap2Coords(sculptBitmap, iScale, mirror), sculptType, viewerMode, mirror, invert); - } -#endif - - - void _SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) - { - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - sculptType = (SculptType)(((int)sculptType) & 0x07); - - if (mirror) - if (sculptType == SculptType.plane) - invert = !invert; - - viewerFaces = new List(); - - int width = rows[0].Count; - - int p1, p2, p3, p4; - - int imageX, imageY; - - if (sculptType != SculptType.plane) - { - for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) - rows[rowNdx].Add(rows[rowNdx][0]); - } - - Coord topPole = rows[0][width / 2]; - Coord bottomPole = rows[rows.Count - 1][width / 2]; - - if (sculptType == SculptType.sphere) - { - int count = rows[0].Count; - List topPoleRow = new List(count); - List bottomPoleRow = new List(count); - - for (int i = 0; i < count; i++) - { - topPoleRow.Add(topPole); - bottomPoleRow.Add(bottomPole); - } - rows.Insert(0, topPoleRow); - rows.Add(bottomPoleRow); - } - else if (sculptType == SculptType.torus) - rows.Add(rows[0]); - - int coordsDown = rows.Count; - int coordsAcross = rows[0].Count; - - float widthUnit = 1.0f / (coordsAcross - 1); - float heightUnit = 1.0f / (coordsDown - 1); - - for (imageY = 0; imageY < coordsDown; imageY++) - { - int rowOffset = imageY * coordsAcross; - - for (imageX = 0; imageX < coordsAcross; imageX++) - { - /* - * p1-----p2 - * | \ f2 | - * | \ | - * | f1 \| - * p3-----p4 - */ - - p4 = rowOffset + imageX; - p3 = p4 - 1; - - p2 = p4 - coordsAcross; - p1 = p3 - coordsAcross; - - this.coords.Add(rows[imageY][imageX]); - if (viewerMode) - { - this.normals.Add(new Coord()); - this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); - } - - if (imageY > 0 && imageX > 0) - { - Face f1, f2; - - if (viewerMode) - { - if (invert) - { - f1 = new Face(p1, p4, p3, p1, p4, p3); - f1.uv1 = p1; - f1.uv2 = p4; - f1.uv3 = p3; - - f2 = new Face(p1, p2, p4, p1, p2, p4); - f2.uv1 = p1; - f2.uv2 = p2; - f2.uv3 = p4; - } - else - { - f1 = new Face(p1, p3, p4, p1, p3, p4); - f1.uv1 = p1; - f1.uv2 = p3; - f1.uv3 = p4; - - f2 = new Face(p1, p4, p2, p1, p4, p2); - f2.uv1 = p1; - f2.uv2 = p4; - f2.uv3 = p2; - } - } - else - { - if (invert) - { - f1 = new Face(p1, p4, p3); - f2 = new Face(p1, p2, p4); - } - else - { - f1 = new Face(p1, p3, p4); - f2 = new Face(p1, p4, p2); - } - } - - this.faces.Add(f1); - this.faces.Add(f2); - } - } - } - - if (viewerMode) - calcVertexNormals(sculptType, coordsAcross, coordsDown); - } - - /// - /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. - /// - /// - public SculptMesh Copy() - { - return new SculptMesh(this); - } - - public SculptMesh(SculptMesh sm) - { - coords = new List(sm.coords); - faces = new List(sm.faces); - viewerFaces = new List(sm.viewerFaces); - normals = new List(sm.normals); - uvs = new List(sm.uvs); - } - - private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) - { // compute vertex normals by summing all the surface normals of all the triangles sharing - // each vertex and then normalizing - int numFaces = this.faces.Count; - for (int i = 0; i < numFaces; i++) - { - Face face = this.faces[i]; - Coord surfaceNormal = face.SurfaceNormal(this.coords); - this.normals[face.n1] += surfaceNormal; - this.normals[face.n2] += surfaceNormal; - this.normals[face.n3] += surfaceNormal; - } - - int numNormals = this.normals.Count; - for (int i = 0; i < numNormals; i++) - this.normals[i] = this.normals[i].Normalize(); - - if (sculptType != SculptType.plane) - { // blend the vertex normals at the cylinder seam - for (int y = 0; y < ySize; y++) - { - int rowOffset = y * xSize; - - this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); - } - } - - foreach (Face face in this.faces) - { - ViewerFace vf = new ViewerFace(0); - vf.v1 = this.coords[face.v1]; - vf.v2 = this.coords[face.v2]; - vf.v3 = this.coords[face.v3]; - - vf.coordIndex1 = face.v1; - vf.coordIndex2 = face.v2; - vf.coordIndex3 = face.v3; - - vf.n1 = this.normals[face.n1]; - vf.n2 = this.normals[face.n2]; - vf.n3 = this.normals[face.n3]; - - vf.uv1 = this.uvs[face.uv1]; - vf.uv2 = this.uvs[face.uv2]; - vf.uv3 = this.uvs[face.uv3]; - - this.viewerFaces.Add(vf); - } - } - - /// - /// Adds a value to each XYZ vertex coordinate in the mesh - /// - /// - /// - /// - public void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.AddPos(x, y, z); - this.viewerFaces[i] = v; - } - } - } - - /// - /// Rotates the mesh - /// - /// - public void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - int numNormals = this.normals.Count; - for (i = 0; i < numNormals; i++) - this.normals[i] *= q; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= q; - v.v2 *= q; - v.v3 *= q; - - v.n1 *= q; - v.n2 *= q; - v.n3 *= q; - - this.viewerFaces[i] = v; - } - } - } - - public void Scale(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - - Coord m = new Coord(x, y, z); - for (i = 0; i < numVerts; i++) - this.coords[i] *= m; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= m; - v.v2 *= m; - v.v3 *= m; - this.viewerFaces[i] = v; - } - } - } - - public void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } -} + */ + + p4 = rowOffset + imageX; + p3 = p4 - 1; + + p2 = p4 - coordsAcross; + p1 = p3 - coordsAcross; + + this.coords.Add(rows[imageY][imageX]); + if (viewerMode) + { + this.normals.Add(new Coord()); + this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); + } + + if (imageY > 0 && imageX > 0) + { + Face f1, f2; + + if (viewerMode) + { + if (invert) + { + f1 = new Face(p1, p4, p3, p1, p4, p3); + f1.uv1 = p1; + f1.uv2 = p4; + f1.uv3 = p3; + + f2 = new Face(p1, p2, p4, p1, p2, p4); + f2.uv1 = p1; + f2.uv2 = p2; + f2.uv3 = p4; + } + else + { + f1 = new Face(p1, p3, p4, p1, p3, p4); + f1.uv1 = p1; + f1.uv2 = p3; + f1.uv3 = p4; + + f2 = new Face(p1, p4, p2, p1, p4, p2); + f2.uv1 = p1; + f2.uv2 = p4; + f2.uv3 = p2; + } + } + else + { + if (invert) + { + f1 = new Face(p1, p4, p3); + f2 = new Face(p1, p2, p4); + } + else + { + f1 = new Face(p1, p3, p4); + f2 = new Face(p1, p4, p2); + } + } + + this.faces.Add(f1); + this.faces.Add(f2); + } + } + } + + if (viewerMode) + calcVertexNormals(sculptType, coordsAcross, coordsDown); + } + + /// + /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. + /// + /// + public SculptMesh Copy() + { + return new SculptMesh(this); + } + + public SculptMesh(SculptMesh sm) + { + coords = new List(sm.coords); + faces = new List(sm.faces); + viewerFaces = new List(sm.viewerFaces); + normals = new List(sm.normals); + uvs = new List(sm.uvs); + } + + private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) + { // compute vertex normals by summing all the surface normals of all the triangles sharing + // each vertex and then normalizing + int numFaces = this.faces.Count; + for (int i = 0; i < numFaces; i++) + { + Face face = this.faces[i]; + Coord surfaceNormal = face.SurfaceNormal(this.coords); + this.normals[face.n1] += surfaceNormal; + this.normals[face.n2] += surfaceNormal; + this.normals[face.n3] += surfaceNormal; + } + + int numNormals = this.normals.Count; + for (int i = 0; i < numNormals; i++) + this.normals[i] = this.normals[i].Normalize(); + + if (sculptType != SculptType.plane) + { // blend the vertex normals at the cylinder seam + for (int y = 0; y < ySize; y++) + { + int rowOffset = y * xSize; + + this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); + } + } + + foreach (Face face in this.faces) + { + ViewerFace vf = new ViewerFace(0); + vf.v1 = this.coords[face.v1]; + vf.v2 = this.coords[face.v2]; + vf.v3 = this.coords[face.v3]; + + vf.coordIndex1 = face.v1; + vf.coordIndex2 = face.v2; + vf.coordIndex3 = face.v3; + + vf.n1 = this.normals[face.n1]; + vf.n2 = this.normals[face.n2]; + vf.n3 = this.normals[face.n3]; + + vf.uv1 = this.uvs[face.uv1]; + vf.uv2 = this.uvs[face.uv2]; + vf.uv3 = this.uvs[face.uv3]; + + this.viewerFaces.Add(vf); + } + } + + /// + /// Adds a value to each XYZ vertex coordinate in the mesh + /// + /// + /// + /// + public void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.AddPos(x, y, z); + this.viewerFaces[i] = v; + } + } + } + + /// + /// Rotates the mesh + /// + /// + public void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + int numNormals = this.normals.Count; + for (i = 0; i < numNormals; i++) + this.normals[i] *= q; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= q; + v.v2 *= q; + v.v3 *= q; + + v.n1 *= q; + v.n2 *= q; + v.n3 *= q; + + this.viewerFaces[i] = v; + } + } + } + + public void Scale(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + + Coord m = new Coord(x, y, z); + for (i = 0; i < numVerts; i++) + this.coords[i] *= m; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= m; + v.v2 *= m; + v.v3 *= m; + this.viewerFaces[i] = v; + } + } + } + + public void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } +} From 39c5ddc8374d7f984bbb1562c8074ff02ac82e47 Mon Sep 17 00:00:00 2001 From: dahlia Date: Thu, 6 May 2010 22:08:59 -0700 Subject: [PATCH 096/260] corrections for face numbering and uv mapping, mostly for spheres - sync with PrimMesher r56 --- OpenSim/Region/Physics/Meshing/PrimMesher.cs | 4485 +++++++++--------- 1 file changed, 2284 insertions(+), 2201 deletions(-) diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index 932943c081..b4e101a445 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs @@ -1,2201 +1,2284 @@ -/* - * Copyright (c) Contributors - * 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 OpenSimulator 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.Text; -using System.IO; - -namespace PrimMesher -{ - public struct Quat - { - /// X value - public float X; - /// Y value - public float Y; - /// Z value - public float Z; - /// W value - public float W; - - public Quat(float x, float y, float z, float w) - { - X = x; - Y = y; - Z = z; - W = w; - } - - public Quat(Coord axis, float angle) - { - axis = axis.Normalize(); - - angle *= 0.5f; - float c = (float)Math.Cos(angle); - float s = (float)Math.Sin(angle); - - X = axis.X * s; - Y = axis.Y * s; - Z = axis.Z * s; - W = c; - - Normalize(); - } - - public float Length() - { - return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); - } - - public Quat Normalize() - { - const float MAG_THRESHOLD = 0.0000001f; - float mag = Length(); - - // Catch very small rounding errors when normalizing - if (mag > MAG_THRESHOLD) - { - float oomag = 1f / mag; - X *= oomag; - Y *= oomag; - Z *= oomag; - W *= oomag; - } - else - { - X = 0f; - Y = 0f; - Z = 0f; - W = 1f; - } - - return this; - } - - public static Quat operator *(Quat q1, Quat q2) - { - float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y; - float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X; - float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W; - float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z; - return new Quat(x, y, z, w); - } - - public override string ToString() - { - return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">"; - } - } - - public struct Coord - { - public float X; - public float Y; - public float Z; - - public Coord(float x, float y, float z) - { - this.X = x; - this.Y = y; - this.Z = z; - } - - public float Length() - { - return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); - } - - public Coord Invert() - { - this.X = -this.X; - this.Y = -this.Y; - this.Z = -this.Z; - - return this; - } - - public Coord Normalize() - { - const float MAG_THRESHOLD = 0.0000001f; - float mag = Length(); - - // Catch very small rounding errors when normalizing - if (mag > MAG_THRESHOLD) - { - float oomag = 1.0f / mag; - this.X *= oomag; - this.Y *= oomag; - this.Z *= oomag; - } - else - { - this.X = 0.0f; - this.Y = 0.0f; - this.Z = 0.0f; - } - - return this; - } - - public override string ToString() - { - return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString(); - } - - public static Coord Cross(Coord c1, Coord c2) - { - return new Coord( - c1.Y * c2.Z - c2.Y * c1.Z, - c1.Z * c2.X - c2.Z * c1.X, - c1.X * c2.Y - c2.X * c1.Y - ); - } - - public static Coord operator +(Coord v, Coord a) - { - return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z); - } - - public static Coord operator *(Coord v, Coord m) - { - return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z); - } - - public static Coord operator *(Coord v, Quat q) - { - // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/ - - Coord c2 = new Coord(0.0f, 0.0f, 0.0f); - - c2.X = q.W * q.W * v.X + - 2f * q.Y * q.W * v.Z - - 2f * q.Z * q.W * v.Y + - q.X * q.X * v.X + - 2f * q.Y * q.X * v.Y + - 2f * q.Z * q.X * v.Z - - q.Z * q.Z * v.X - - q.Y * q.Y * v.X; - - c2.Y = - 2f * q.X * q.Y * v.X + - q.Y * q.Y * v.Y + - 2f * q.Z * q.Y * v.Z + - 2f * q.W * q.Z * v.X - - q.Z * q.Z * v.Y + - q.W * q.W * v.Y - - 2f * q.X * q.W * v.Z - - q.X * q.X * v.Y; - - c2.Z = - 2f * q.X * q.Z * v.X + - 2f * q.Y * q.Z * v.Y + - q.Z * q.Z * v.Z - - 2f * q.W * q.Y * v.X - - q.Y * q.Y * v.Z + - 2f * q.W * q.X * v.Y - - q.X * q.X * v.Z + - q.W * q.W * v.Z; - - return c2; - } - } - - public struct UVCoord - { - public float U; - public float V; - - - public UVCoord(float u, float v) - { - this.U = u; - this.V = v; - } - } - - public struct Face - { - public int primFace; - - // vertices - public int v1; - public int v2; - public int v3; - - //normals - public int n1; - public int n2; - public int n3; - - // uvs - public int uv1; - public int uv2; - public int uv3; - - - public Face(int v1, int v2, int v3) - { - primFace = 0; - - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - this.n1 = 0; - this.n2 = 0; - this.n3 = 0; - - this.uv1 = 0; - this.uv2 = 0; - this.uv3 = 0; - - } - - public Face(int v1, int v2, int v3, int n1, int n2, int n3) - { - primFace = 0; - - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - this.n1 = n1; - this.n2 = n2; - this.n3 = n3; - - this.uv1 = 0; - this.uv2 = 0; - this.uv3 = 0; - } - - public Coord SurfaceNormal(List coordList) - { - Coord c1 = coordList[this.v1]; - Coord c2 = coordList[this.v2]; - Coord c3 = coordList[this.v3]; - - Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - return Coord.Cross(edge1, edge2).Normalize(); - } - } - - public struct ViewerFace - { - public int primFaceNumber; - - public Coord v1; - public Coord v2; - public Coord v3; - - public int coordIndex1; - public int coordIndex2; - public int coordIndex3; - - public Coord n1; - public Coord n2; - public Coord n3; - - public UVCoord uv1; - public UVCoord uv2; - public UVCoord uv3; - - public ViewerFace(int primFaceNumber) - { - this.primFaceNumber = primFaceNumber; - - this.v1 = new Coord(); - this.v2 = new Coord(); - this.v3 = new Coord(); - - this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet - - this.n1 = new Coord(); - this.n2 = new Coord(); - this.n3 = new Coord(); - - this.uv1 = new UVCoord(); - this.uv2 = new UVCoord(); - this.uv3 = new UVCoord(); - } - - public void Scale(float x, float y, float z) - { - this.v1.X *= x; - this.v1.Y *= y; - this.v1.Z *= z; - - this.v2.X *= x; - this.v2.Y *= y; - this.v2.Z *= z; - - this.v3.X *= x; - this.v3.Y *= y; - this.v3.Z *= z; - } - - public void AddPos(float x, float y, float z) - { - this.v1.X += x; - this.v2.X += x; - this.v3.X += x; - - this.v1.Y += y; - this.v2.Y += y; - this.v3.Y += y; - - this.v1.Z += z; - this.v2.Z += z; - this.v3.Z += z; - } - - public void AddRot(Quat q) - { - this.v1 *= q; - this.v2 *= q; - this.v3 *= q; - - this.n1 *= q; - this.n2 *= q; - this.n3 *= q; - } - - public void CalcSurfaceNormal() - { - - Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z); - Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z); - - this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); - } - } - - internal struct Angle - { - internal float angle; - internal float X; - internal float Y; - - internal Angle(float angle, float x, float y) - { - this.angle = angle; - this.X = x; - this.Y = y; - } - } - - internal class AngleList - { - private float iX, iY; // intersection point - - private static Angle[] angles3 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), - new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private static Coord[] normals3 = - { - new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), - new Coord(-0.5f, 0.0f, 0.0f).Normalize(), - new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), - new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() - }; - - private static Angle[] angles4 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.25f, 0.0f, 1.0f), - new Angle(0.5f, -1.0f, 0.0f), - new Angle(0.75f, 0.0f, -1.0f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private static Coord[] normals4 = - { - new Coord(0.5f, 0.5f, 0.0f).Normalize(), - new Coord(-0.5f, 0.5f, 0.0f).Normalize(), - new Coord(-0.5f, -0.5f, 0.0f).Normalize(), - new Coord(0.5f, -0.5f, 0.0f).Normalize(), - new Coord(0.5f, 0.5f, 0.0f).Normalize() - }; - - private static Angle[] angles24 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f), - new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f), - new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f), - new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f), - new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f), - new Angle(0.25f, 0.0f, 1.0f), - new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f), - new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), - new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f), - new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f), - new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f), - new Angle(0.5f, -1.0f, 0.0f), - new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f), - new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f), - new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f), - new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), - new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f), - new Angle(0.75f, 0.0f, -1.0f), - new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f), - new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f), - new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f), - new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f), - new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private Angle interpolatePoints(float newPoint, Angle p1, Angle p2) - { - float m = (newPoint - p1.angle) / (p2.angle - p1.angle); - return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y)); - } - - private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) - { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ - double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); - double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); - - if (denom != 0.0) - { - double ua = uaNumerator / denom; - iX = (float)(x1 + ua * (x2 - x1)); - iY = (float)(y1 + ua * (y2 - y1)); - } - } - - internal List angles; - internal List normals; - - internal void makeAngles(int sides, float startAngle, float stopAngle) - { - angles = new List(); - normals = new List(); - - double twoPi = System.Math.PI * 2.0; - float twoPiInv = 1.0f / (float)twoPi; - - if (sides < 1) - throw new Exception("number of sides not greater than zero"); - if (stopAngle <= startAngle) - throw new Exception("stopAngle not greater than startAngle"); - - if ((sides == 3 || sides == 4 || sides == 24)) - { - startAngle *= twoPiInv; - stopAngle *= twoPiInv; - - Angle[] sourceAngles; - if (sides == 3) - sourceAngles = angles3; - else if (sides == 4) - sourceAngles = angles4; - else sourceAngles = angles24; - - int startAngleIndex = (int)(startAngle * sides); - int endAngleIndex = sourceAngles.Length - 1; - if (stopAngle < 1.0f) - endAngleIndex = (int)(stopAngle * sides) + 1; - if (endAngleIndex == startAngleIndex) - endAngleIndex++; - - for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) - { - angles.Add(sourceAngles[angleIndex]); - if (sides == 3) - normals.Add(normals3[angleIndex]); - else if (sides == 4) - normals.Add(normals4[angleIndex]); - } - - if (startAngle > 0.0f) - angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); - - if (stopAngle < 1.0f) - { - int lastAngleIndex = angles.Count - 1; - angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]); - } - } - else - { - double stepSize = twoPi / sides; - - int startStep = (int)(startAngle / stepSize); - double angle = stepSize * startStep; - int step = startStep; - double stopAngleTest = stopAngle; - if (stopAngle < twoPi) - { - stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1); - if (stopAngleTest < stopAngle) - stopAngleTest += stepSize; - if (stopAngleTest > twoPi) - stopAngleTest = twoPi; - } - - while (angle <= stopAngleTest) - { - Angle newAngle; - newAngle.angle = (float)angle; - newAngle.X = (float)System.Math.Cos(angle); - newAngle.Y = (float)System.Math.Sin(angle); - angles.Add(newAngle); - step += 1; - angle = stepSize * step; - } - - if (startAngle > angles[0].angle) - { - Angle newAngle; - intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle)); - newAngle.angle = startAngle; - newAngle.X = iX; - newAngle.Y = iY; - angles[0] = newAngle; - } - - int index = angles.Count - 1; - if (stopAngle < angles[index].angle) - { - Angle newAngle; - intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle)); - newAngle.angle = stopAngle; - newAngle.X = iX; - newAngle.Y = iY; - angles[index] = newAngle; - } - } - } - } - - /// - /// generates a profile for extrusion - /// - internal class Profile - { - private const float twoPi = 2.0f * (float)Math.PI; - - internal string errorMessage = null; - - internal List coords; - internal List faces; - internal List vertexNormals; - internal List us; - internal List faceUVs; - internal List faceNumbers; - - // use these for making individual meshes for each prim face - internal List outerCoordIndices = null; - internal List hollowCoordIndices = null; - internal List cut1CoordIndices = null; - internal List cut2CoordIndices = null; - - internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); - internal Coord cutNormal1 = new Coord(); - internal Coord cutNormal2 = new Coord(); - - internal int numOuterVerts = 0; - internal int numHollowVerts = 0; - - internal bool calcVertexNormals = false; - internal int bottomFaceNumber = 0; - internal int numPrimFaces = 0; - - internal Profile() - { - this.coords = new List(); - this.faces = new List(); - this.vertexNormals = new List(); - this.us = new List(); - this.faceUVs = new List(); - this.faceNumbers = new List(); - } - - internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) - { - this.calcVertexNormals = calcVertexNormals; - this.coords = new List(); - this.faces = new List(); - this.vertexNormals = new List(); - this.us = new List(); - this.faceUVs = new List(); - this.faceNumbers = new List(); - - Coord center = new Coord(0.0f, 0.0f, 0.0f); - //bool hasCenter = false; - - List hollowCoords = new List(); - List hollowNormals = new List(); - List hollowUs = new List(); - - if (calcVertexNormals) - { - this.outerCoordIndices = new List(); - this.hollowCoordIndices = new List(); - this.cut1CoordIndices = new List(); - this.cut2CoordIndices = new List(); - } - - bool hasHollow = (hollow > 0.0f); - - bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); - - AngleList angles = new AngleList(); - AngleList hollowAngles = new AngleList(); - - float xScale = 0.5f; - float yScale = 0.5f; - if (sides == 4) // corners of a square are sqrt(2) from center - { - xScale = 0.707f; - yScale = 0.707f; - } - - float startAngle = profileStart * twoPi; - float stopAngle = profileEnd * twoPi; - - try { angles.makeAngles(sides, startAngle, stopAngle); } - catch (Exception ex) - { - - errorMessage = "makeAngles failed: Exception: " + ex.ToString() - + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); - - return; - } - - this.numOuterVerts = angles.angles.Count; - - // flag to create as few triangles as possible for 3 or 4 side profile - bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut); - - if (hasHollow) - { - if (sides == hollowSides) - hollowAngles = angles; - else - { - try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); } - catch (Exception ex) - { - errorMessage = "makeAngles failed: Exception: " + ex.ToString() - + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); - - return; - } - } - this.numHollowVerts = hollowAngles.angles.Count; - } - else if (!simpleFace) - { - this.coords.Add(center); - //hasCenter = true; - if (this.calcVertexNormals) - this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); - this.us.Add(0.0f); - } - - float z = 0.0f; - - Angle angle; - Coord newVert = new Coord(); - if (hasHollow && hollowSides != sides) - { - int numHollowAngles = hollowAngles.angles.Count; - for (int i = 0; i < numHollowAngles; i++) - { - angle = hollowAngles.angles[i]; - newVert.X = hollow * xScale * angle.X; - newVert.Y = hollow * yScale * angle.Y; - newVert.Z = z; - - hollowCoords.Add(newVert); - if (this.calcVertexNormals) - { - if (hollowSides < 5) - hollowNormals.Add(hollowAngles.normals[i].Invert()); - else - hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); - - hollowUs.Add(angle.angle * hollow); - } - } - } - - int index = 0; - int numAngles = angles.angles.Count; - - for (int i = 0; i < numAngles; i++) - { - angle = angles.angles[i]; - newVert.X = angle.X * xScale; - newVert.Y = angle.Y * yScale; - newVert.Z = z; - this.coords.Add(newVert); - if (this.calcVertexNormals) - { - this.outerCoordIndices.Add(this.coords.Count - 1); - - if (sides < 5) - { - this.vertexNormals.Add(angles.normals[i]); - float u = angle.angle; - this.us.Add(u); - } - else - { - this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); - this.us.Add(angle.angle); - } - } - - if (hasHollow) - { - if (hollowSides == sides) - { - newVert.X *= hollow; - newVert.Y *= hollow; - newVert.Z = z; - hollowCoords.Add(newVert); - if (this.calcVertexNormals) - { - if (sides < 5) - { - hollowNormals.Add(angles.normals[i].Invert()); - } - - else - hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); - - hollowUs.Add(angle.angle * hollow); - } - } - } - else if (!simpleFace && createFaces && angle.angle > 0.0001f) - { - Face newFace = new Face(); - newFace.v1 = 0; - newFace.v2 = index; - newFace.v3 = index + 1; - - this.faces.Add(newFace); - } - index += 1; - } - - if (hasHollow) - { - hollowCoords.Reverse(); - if (this.calcVertexNormals) - { - hollowNormals.Reverse(); - hollowUs.Reverse(); - } - - if (createFaces) - { - //int numOuterVerts = this.coords.Count; - //numOuterVerts = this.coords.Count; - //int numHollowVerts = hollowCoords.Count; - int numTotalVerts = this.numOuterVerts + this.numHollowVerts; - - if (this.numOuterVerts == this.numHollowVerts) - { - Face newFace = new Face(); - - for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++) - { - newFace.v1 = coordIndex; - newFace.v2 = coordIndex + 1; - newFace.v3 = numTotalVerts - coordIndex - 1; - this.faces.Add(newFace); - - newFace.v1 = coordIndex + 1; - newFace.v2 = numTotalVerts - coordIndex - 2; - newFace.v3 = numTotalVerts - coordIndex - 1; - this.faces.Add(newFace); - } - } - else - { - if (this.numOuterVerts < this.numHollowVerts) - { - Face newFace = new Face(); - int j = 0; // j is the index for outer vertices - int maxJ = this.numOuterVerts - 1; - for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices - { - if (j < maxJ) - if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) - { - newFace.v1 = numTotalVerts - i - 1; - newFace.v2 = j; - newFace.v3 = j + 1; - - this.faces.Add(newFace); - j += 1; - } - - newFace.v1 = j; - newFace.v2 = numTotalVerts - i - 2; - newFace.v3 = numTotalVerts - i - 1; - - this.faces.Add(newFace); - } - } - else // numHollowVerts < numOuterVerts - { - Face newFace = new Face(); - int j = 0; // j is the index for inner vertices - int maxJ = this.numHollowVerts - 1; - for (int i = 0; i < this.numOuterVerts; i++) - { - if (j < maxJ) - if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) - { - newFace.v1 = i; - newFace.v2 = numTotalVerts - j - 2; - newFace.v3 = numTotalVerts - j - 1; - - this.faces.Add(newFace); - j += 1; - } - - newFace.v1 = numTotalVerts - j - 1; - newFace.v2 = i; - newFace.v3 = i + 1; - - this.faces.Add(newFace); - } - } - } - } - - if (calcVertexNormals) - { - foreach (Coord hc in hollowCoords) - { - this.coords.Add(hc); - hollowCoordIndices.Add(this.coords.Count - 1); - } - } - else - this.coords.AddRange(hollowCoords); - - if (this.calcVertexNormals) - { - this.vertexNormals.AddRange(hollowNormals); - this.us.AddRange(hollowUs); - - } - } - - if (simpleFace && createFaces) - { - if (sides == 3) - this.faces.Add(new Face(0, 1, 2)); - else if (sides == 4) - { - this.faces.Add(new Face(0, 1, 2)); - this.faces.Add(new Face(0, 2, 3)); - } - } - - if (calcVertexNormals && hasProfileCut) - { - if (hasHollow) - { - int lastOuterVertIndex = this.numOuterVerts - 1; - - this.cut1CoordIndices.Add(0); - this.cut1CoordIndices.Add(this.coords.Count - 1); - - this.cut2CoordIndices.Add(lastOuterVertIndex + 1); - this.cut2CoordIndices.Add(lastOuterVertIndex); - - this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; - this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); - - this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; - this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); - } - - else - { - this.cutNormal1.X = this.vertexNormals[1].Y; - this.cutNormal1.Y = -this.vertexNormals[1].X; - - this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; - this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; - - } - this.cutNormal1.Normalize(); - this.cutNormal2.Normalize(); - } - - this.MakeFaceUVs(); - - hollowCoords = null; - hollowNormals = null; - hollowUs = null; - - if (calcVertexNormals) - { // calculate prim face numbers - - // face number order is top, outer, hollow, bottom, start cut, end cut - // I know it's ugly but so is the whole concept of prim face numbers - - int faceNum = 1; // start with outer faces - int startVert = hasProfileCut && !hasHollow ? 1 : 0; - if (startVert > 0) - this.faceNumbers.Add(-1); - for (int i = 0; i < this.numOuterVerts - 1; i++) - this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); - - //if (!hasHollow && !hasProfileCut) - // this.bottomFaceNumber = faceNum++; - - this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); - - if (sides > 4 && (hasHollow || hasProfileCut)) - faceNum++; - - if (hasHollow) - { - for (int i = 0; i < this.numHollowVerts; i++) - this.faceNumbers.Add(faceNum); - - faceNum++; - } - //if (hasProfileCut || hasHollow) - // this.bottomFaceNumber = faceNum++; - this.bottomFaceNumber = faceNum++; - - if (hasHollow && hasProfileCut) - this.faceNumbers.Add(faceNum++); - for (int i = 0; i < this.faceNumbers.Count; i++) - if (this.faceNumbers[i] == -1) - this.faceNumbers[i] = faceNum++; - - - this.numPrimFaces = faceNum; - } - - } - - internal void MakeFaceUVs() - { - this.faceUVs = new List(); - foreach (Coord c in this.coords) - this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); - } - - internal Profile Copy() - { - return this.Copy(true); - } - - internal Profile Copy(bool needFaces) - { - Profile copy = new Profile(); - - copy.coords.AddRange(this.coords); - copy.faceUVs.AddRange(this.faceUVs); - - if (needFaces) - copy.faces.AddRange(this.faces); - if ((copy.calcVertexNormals = this.calcVertexNormals) == true) - { - copy.vertexNormals.AddRange(this.vertexNormals); - copy.faceNormal = this.faceNormal; - copy.cutNormal1 = this.cutNormal1; - copy.cutNormal2 = this.cutNormal2; - copy.us.AddRange(this.us); - copy.faceNumbers.AddRange(this.faceNumbers); - - copy.cut1CoordIndices = new List(this.cut1CoordIndices); - copy.cut2CoordIndices = new List(this.cut2CoordIndices); - copy.hollowCoordIndices = new List(this.hollowCoordIndices); - copy.outerCoordIndices = new List(this.outerCoordIndices); - } - copy.numOuterVerts = this.numOuterVerts; - copy.numHollowVerts = this.numHollowVerts; - - return copy; - } - - internal void AddPos(Coord v) - { - this.AddPos(v.X, v.Y, v.Z); - } - - internal void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - } - - internal void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - if (this.calcVertexNormals) - { - int numNormals = this.vertexNormals.Count; - for (i = 0; i < numNormals; i++) - this.vertexNormals[i] *= q; - - this.faceNormal *= q; - this.cutNormal1 *= q; - this.cutNormal2 *= q; - - } - } - - internal void Scale(float x, float y) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X *= x; - vert.Y *= y; - this.coords[i] = vert; - } - } - - /// - /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices - /// - internal void FlipNormals() - { - int i; - int numFaces = this.faces.Count; - Face tmpFace; - int tmp; - - for (i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmp = tmpFace.v3; - tmpFace.v3 = tmpFace.v1; - tmpFace.v1 = tmp; - this.faces[i] = tmpFace; - } - - if (this.calcVertexNormals) - { - int normalCount = this.vertexNormals.Count; - if (normalCount > 0) - { - Coord n = this.vertexNormals[normalCount - 1]; - n.Z = -n.Z; - this.vertexNormals[normalCount - 1] = n; - } - } - - this.faceNormal.X = -this.faceNormal.X; - this.faceNormal.Y = -this.faceNormal.Y; - this.faceNormal.Z = -this.faceNormal.Z; - - int numfaceUVs = this.faceUVs.Count; - for (i = 0; i < numfaceUVs; i++) - { - UVCoord uv = this.faceUVs[i]; - uv.V = 1.0f - uv.V; - this.faceUVs[i] = uv; - } - } - - internal void AddValue2FaceVertexIndices(int num) - { - int numFaces = this.faces.Count; - Face tmpFace; - for (int i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmpFace.v1 += num; - tmpFace.v2 += num; - tmpFace.v3 += num; - - this.faces[i] = tmpFace; - } - } - - internal void AddValue2FaceNormalIndices(int num) - { - if (this.calcVertexNormals) - { - int numFaces = this.faces.Count; - Face tmpFace; - for (int i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmpFace.n1 += num; - tmpFace.n2 += num; - tmpFace.n3 += num; - - this.faces[i] = tmpFace; - } - } - } - - internal void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } - - public struct PathNode - { - public Coord position; - public Quat rotation; - public float xScale; - public float yScale; - public float percentOfPath; - } - - public enum PathType { Linear = 0, Circular = 1, Flexible = 2 } - - public class Path - { - public List pathNodes = new List(); - - public float twistBegin = 0.0f; - public float twistEnd = 0.0f; - public float topShearX = 0.0f; - public float topShearY = 0.0f; - public float pathCutBegin = 0.0f; - public float pathCutEnd = 1.0f; - public float dimpleBegin = 0.0f; - public float dimpleEnd = 1.0f; - public float skew = 0.0f; - public float holeSizeX = 1.0f; // called pathScaleX in pbs - public float holeSizeY = 0.25f; - public float taperX = 0.0f; - public float taperY = 0.0f; - public float radius = 0.0f; - public float revolutions = 1.0f; - public int stepsPerRevolution = 24; - - private const float twoPi = 2.0f * (float)Math.PI; - - public void Create(PathType pathType, int steps) - { - if (pathType == PathType.Linear || pathType == PathType.Flexible) - { - int step = 0; - - float length = this.pathCutEnd - this.pathCutBegin; - float twistTotal = twistEnd - twistBegin; - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number - - float start = -0.5f; - float stepSize = length / (float)steps; - float percentOfPathMultiplier = stepSize; - float xOffset = 0.0f; - float yOffset = 0.0f; - float zOffset = start; - float xOffsetStepIncrement = this.topShearX / steps; - float yOffsetStepIncrement = this.topShearY / steps; - - float percentOfPath = this.pathCutBegin; - zOffset += percentOfPath; - - // sanity checks - - bool done = false; - - while (!done) - { - PathNode newNode = new PathNode(); - - newNode.xScale = 1.0f; - if (this.taperX == 0.0f) - newNode.xScale = 1.0f; - else if (this.taperX > 0.0f) - newNode.xScale = 1.0f - percentOfPath * this.taperX; - else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX; - - newNode.yScale = 1.0f; - if (this.taperY == 0.0f) - newNode.yScale = 1.0f; - else if (this.taperY > 0.0f) - newNode.yScale = 1.0f - percentOfPath * this.taperY; - else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY; - - float twist = twistBegin + twistTotal * percentOfPath; - - newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); - newNode.position = new Coord(xOffset, yOffset, zOffset); - newNode.percentOfPath = percentOfPath; - - pathNodes.Add(newNode); - - if (step < steps) - { - step += 1; - percentOfPath += percentOfPathMultiplier; - xOffset += xOffsetStepIncrement; - yOffset += yOffsetStepIncrement; - zOffset += stepSize; - if (percentOfPath > this.pathCutEnd) - done = true; - } - else done = true; - } - } // end of linear path code - - else // pathType == Circular - { - float twistTotal = twistEnd - twistBegin; - - // if the profile has a lot of twist, add more layers otherwise the layers may overlap - // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't - // accurately match the viewer - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - { - if (twistTotalAbs > Math.PI * 1.5f) - steps *= 2; - if (twistTotalAbs > Math.PI * 3.0f) - steps *= 2; - } - - float yPathScale = this.holeSizeY * 0.5f; - float pathLength = this.pathCutEnd - this.pathCutBegin; - float totalSkew = this.skew * 2.0f * pathLength; - float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew; - float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY)); - float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f; - - // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end - // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used - // to calculate the sine for generating the path radius appears to approximate it's effects there - // too, but there are some subtle differences in the radius which are noticeable as the prim size - // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on - // the meshes generated with this technique appear nearly identical in shape to the same prims when - // displayed by the viewer. - - float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f; - float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f; - float stepSize = twoPi / this.stepsPerRevolution; - - int step = (int)(startAngle / stepSize); - float angle = startAngle; - - bool done = false; - while (!done) // loop through the length of the path and add the layers - { - PathNode newNode = new PathNode(); - - float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX; - float yProfileScale = this.holeSizeY; - - float percentOfPath = angle / (twoPi * this.revolutions); - float percentOfAngles = (angle - startAngle) / (endAngle - startAngle); - - if (this.taperX > 0.01f) - xProfileScale *= 1.0f - percentOfPath * this.taperX; - else if (this.taperX < -0.01f) - xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX; - - if (this.taperY > 0.01f) - yProfileScale *= 1.0f - percentOfPath * this.taperY; - else if (this.taperY < -0.01f) - yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY; - - newNode.xScale = xProfileScale; - newNode.yScale = yProfileScale; - - float radiusScale = 1.0f; - if (this.radius > 0.001f) - radiusScale = 1.0f - this.radius * percentOfPath; - else if (this.radius < 0.001f) - radiusScale = 1.0f + this.radius * (1.0f - percentOfPath); - - float twist = twistBegin + twistTotal * percentOfPath; - - float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles); - xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor; - - float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale; - - float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale; - - newNode.position = new Coord(xOffset, yOffset, zOffset); - - // now orient the rotation of the profile layer relative to it's position on the path - // adding taperY to the angle used to generate the quat appears to approximate the viewer - - newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY); - - // next apply twist rotation to the profile layer - if (twistTotal != 0.0f || twistBegin != 0.0f) - newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); - - newNode.percentOfPath = percentOfPath; - - pathNodes.Add(newNode); - - // calculate terms for next iteration - // calculate the angle for the next iteration of the loop - - if (angle >= endAngle - 0.01) - done = true; - else - { - step += 1; - angle = stepSize * step; - if (angle > endAngle) - angle = endAngle; - } - } - } - } - } - - public class PrimMesh - { - public string errorMessage = ""; - private const float twoPi = 2.0f * (float)Math.PI; - - public List coords; - public List normals; - public List faces; - - public List viewerFaces; - - private int sides = 4; - private int hollowSides = 4; - private float profileStart = 0.0f; - private float profileEnd = 1.0f; - private float hollow = 0.0f; - public int twistBegin = 0; - public int twistEnd = 0; - public float topShearX = 0.0f; - public float topShearY = 0.0f; - public float pathCutBegin = 0.0f; - public float pathCutEnd = 1.0f; - public float dimpleBegin = 0.0f; - public float dimpleEnd = 1.0f; - public float skew = 0.0f; - public float holeSizeX = 1.0f; // called pathScaleX in pbs - public float holeSizeY = 0.25f; - public float taperX = 0.0f; - public float taperY = 0.0f; - public float radius = 0.0f; - public float revolutions = 1.0f; - public int stepsPerRevolution = 24; - - private bool hasProfileCut = false; - private bool hasHollow = false; - public bool calcVertexNormals = false; - private bool normalsProcessed = false; - public bool viewerMode = false; - - public int numPrimFaces = 0; - - /// - /// Human readable string representation of the parameters used to create a mesh. - /// - /// - public string ParamsToDisplayString() - { - string s = ""; - s += "sides..................: " + this.sides.ToString(); - s += "\nhollowSides..........: " + this.hollowSides.ToString(); - s += "\nprofileStart.........: " + this.profileStart.ToString(); - s += "\nprofileEnd...........: " + this.profileEnd.ToString(); - s += "\nhollow...............: " + this.hollow.ToString(); - s += "\ntwistBegin...........: " + this.twistBegin.ToString(); - s += "\ntwistEnd.............: " + this.twistEnd.ToString(); - s += "\ntopShearX............: " + this.topShearX.ToString(); - s += "\ntopShearY............: " + this.topShearY.ToString(); - s += "\npathCutBegin.........: " + this.pathCutBegin.ToString(); - s += "\npathCutEnd...........: " + this.pathCutEnd.ToString(); - s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString(); - s += "\ndimpleEnd............: " + this.dimpleEnd.ToString(); - s += "\nskew.................: " + this.skew.ToString(); - s += "\nholeSizeX............: " + this.holeSizeX.ToString(); - s += "\nholeSizeY............: " + this.holeSizeY.ToString(); - s += "\ntaperX...............: " + this.taperX.ToString(); - s += "\ntaperY...............: " + this.taperY.ToString(); - s += "\nradius...............: " + this.radius.ToString(); - s += "\nrevolutions..........: " + this.revolutions.ToString(); - s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); - - return s; - } - - /// - /// Constructs a PrimMesh object and creates the profile for extrusion. - /// - /// - /// - /// - /// - /// - public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) - { - this.coords = new List(); - this.faces = new List(); - - this.sides = sides; - this.profileStart = profileStart; - this.profileEnd = profileEnd; - this.hollow = hollow; - this.hollowSides = hollowSides; - - if (sides < 3) - this.sides = 3; - if (hollowSides < 3) - this.hollowSides = 3; - if (profileStart < 0.0f) - this.profileStart = 0.0f; - if (profileEnd > 1.0f) - this.profileEnd = 1.0f; - if (profileEnd < 0.02f) - this.profileEnd = 0.02f; - if (profileStart >= profileEnd) - this.profileStart = profileEnd - 0.02f; - if (hollow > 0.99f) - this.hollow = 0.99f; - if (hollow < 0.0f) - this.hollow = 0.0f; - - this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); - this.hasHollow = (this.hollow > 0.001f); - } - - /// - /// Extrudes a profile along a path. - /// - public void Extrude(PathType pathType) - { - this.coords = new List(); - this.faces = new List(); - - if (this.viewerMode) - { - this.viewerFaces = new List(); - this.calcVertexNormals = true; - } - - if (this.calcVertexNormals) - this.normals = new List(); - - int steps = 1; - - float length = this.pathCutEnd - this.pathCutBegin; - normalsProcessed = false; - - if (this.viewerMode && this.sides == 3) - { - // prisms don't taper well so add some vertical resolution - // other prims may benefit from this but just do prisms for now - if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) - steps = (int)(steps * 4.5 * length); - } - - - float twistBegin = this.twistBegin / 360.0f * twoPi; - float twistEnd = this.twistEnd / 360.0f * twoPi; - float twistTotal = twistEnd - twistBegin; - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number - - float hollow = this.hollow; - - // sanity checks - float initialProfileRot = 0.0f; - if (pathType == PathType.Circular) - { - if (this.sides == 3) - { - initialProfileRot = (float)Math.PI; - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow *= 0.707f; - } - else hollow *= 0.5f; - } - else if (this.sides == 4) - { - initialProfileRot = 0.25f * (float)Math.PI; - if (this.hollowSides != 4) - hollow *= 0.707f; - } - else if (this.sides > 4) - { - initialProfileRot = (float)Math.PI; - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow /= 0.7f; - } - } - } - else - { - if (this.sides == 3) - { - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow *= 0.707f; - } - else hollow *= 0.5f; - } - else if (this.sides == 4) - { - initialProfileRot = 1.25f * (float)Math.PI; - if (this.hollowSides != 4) - hollow *= 0.707f; - } - else if (this.sides == 24 && this.hollowSides == 4) - hollow *= 1.414f; - } - - Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); - this.errorMessage = profile.errorMessage; - - this.numPrimFaces = profile.numPrimFaces; - - int cut1Vert = -1; - int cut2Vert = -1; - if (hasProfileCut) - { - cut1Vert = hasHollow ? profile.coords.Count - 1 : 0; - cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts; - } - - if (initialProfileRot != 0.0f) - { - profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); - if (viewerMode) - profile.MakeFaceUVs(); - } - - Coord lastCutNormal1 = new Coord(); - Coord lastCutNormal2 = new Coord(); - float lastV = 1.0f; - - Path path = new Path(); - path.twistBegin = twistBegin; - path.twistEnd = twistEnd; - path.topShearX = topShearX; - path.topShearY = topShearY; - path.pathCutBegin = pathCutBegin; - path.pathCutEnd = pathCutEnd; - path.dimpleBegin = dimpleBegin; - path.dimpleEnd = dimpleEnd; - path.skew = skew; - path.holeSizeX = holeSizeX; - path.holeSizeY = holeSizeY; - path.taperX = taperX; - path.taperY = taperY; - path.radius = radius; - path.revolutions = revolutions; - path.stepsPerRevolution = stepsPerRevolution; - - path.Create(pathType, steps); - - bool needEndFaces = false; - if (pathType == PathType.Circular) - { - needEndFaces = false; - if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f) - needEndFaces = true; - else if (this.taperX != 0.0f || this.taperY != 0.0f) - needEndFaces = true; - else if (this.skew != 0.0f) - needEndFaces = true; - else if (twistTotal != 0.0f) - needEndFaces = true; - else if (this.radius != 0.0f) - needEndFaces = true; - } - else needEndFaces = true; - - for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) - { - PathNode node = path.pathNodes[nodeIndex]; - Profile newLayer = profile.Copy(); - newLayer.Scale(node.xScale, node.yScale); - - newLayer.AddRot(node.rotation); - newLayer.AddPos(node.position); - - if (needEndFaces && nodeIndex == 0) - { - newLayer.FlipNormals(); - - // add the top faces to the viewerFaces list here - if (this.viewerMode) - { - Coord faceNormal = newLayer.faceNormal; - ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber); - int numFaces = newLayer.faces.Count; - List faces = newLayer.faces; - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - newViewerFace.v1 = newLayer.coords[face.v1]; - newViewerFace.v2 = newLayer.coords[face.v2]; - newViewerFace.v3 = newLayer.coords[face.v3]; - - newViewerFace.coordIndex1 = face.v1; - newViewerFace.coordIndex2 = face.v2; - newViewerFace.coordIndex3 = face.v3; - - newViewerFace.n1 = faceNormal; - newViewerFace.n2 = faceNormal; - newViewerFace.n3 = faceNormal; - - newViewerFace.uv1 = newLayer.faceUVs[face.v1]; - newViewerFace.uv2 = newLayer.faceUVs[face.v2]; - newViewerFace.uv3 = newLayer.faceUVs[face.v3]; - - this.viewerFaces.Add(newViewerFace); - } - } - } // if (nodeIndex == 0) - - // append this layer - - int coordsLen = this.coords.Count; - newLayer.AddValue2FaceVertexIndices(coordsLen); - - this.coords.AddRange(newLayer.coords); - - if (this.calcVertexNormals) - { - newLayer.AddValue2FaceNormalIndices(this.normals.Count); - this.normals.AddRange(newLayer.vertexNormals); - } - - if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f) - this.faces.AddRange(newLayer.faces); - - // fill faces between layers - - int numVerts = newLayer.coords.Count; - Face newFace = new Face(); - - if (nodeIndex > 0) - { - int startVert = coordsLen + 1; - int endVert = this.coords.Count; - - if (sides < 5 || this.hasProfileCut || hollow > 0.0f) - startVert--; - - for (int i = startVert; i < endVert; i++) - { - int iNext = i + 1; - if (i == endVert - 1) - iNext = startVert; - - int whichVert = i - startVert; - - newFace.v1 = i; - newFace.v2 = i - numVerts; - newFace.v3 = iNext - numVerts; - this.faces.Add(newFace); - - newFace.v2 = iNext - numVerts; - newFace.v3 = iNext; - this.faces.Add(newFace); - - if (this.viewerMode) - { - // add the side faces to the list of viewerFaces here - - int primFaceNum = profile.faceNumbers[whichVert]; - if (!needEndFaces) - primFaceNum -= 1; - - ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); - ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); - - float u1 = newLayer.us[whichVert]; - float u2 = 1.0f; - if (whichVert < newLayer.us.Count - 1) - u2 = newLayer.us[whichVert + 1]; - - if (whichVert == cut1Vert || whichVert == cut2Vert) - { - u1 = 0.0f; - u2 = 1.0f; - } - else if (sides < 5) - { - if (whichVert < profile.numOuterVerts) - { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled - // to reflect the entire texture width - u1 *= sides; - u2 *= sides; - u2 -= (int)u1; - u1 -= (int)u1; - if (u2 < 0.1f) - u2 = 1.0f; - } - else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) - { - u1 *= 2.0f; - u2 *= 2.0f; - } - } - - newViewerFace1.uv1.U = u1; - newViewerFace1.uv2.U = u1; - newViewerFace1.uv3.U = u2; - - newViewerFace1.uv1.V = 1.0f - node.percentOfPath; - newViewerFace1.uv2.V = lastV; - newViewerFace1.uv3.V = lastV; - - newViewerFace2.uv1.U = u1; - newViewerFace2.uv2.U = u2; - newViewerFace2.uv3.U = u2; - - newViewerFace2.uv1.V = 1.0f - node.percentOfPath; - newViewerFace2.uv2.V = lastV; - newViewerFace2.uv3.V = 1.0f - node.percentOfPath; - - newViewerFace1.v1 = this.coords[i]; - newViewerFace1.v2 = this.coords[i - numVerts]; - newViewerFace1.v3 = this.coords[iNext - numVerts]; - - newViewerFace2.v1 = this.coords[i]; - newViewerFace2.v2 = this.coords[iNext - numVerts]; - newViewerFace2.v3 = this.coords[iNext]; - - newViewerFace1.coordIndex1 = i; - newViewerFace1.coordIndex2 = i - numVerts; - newViewerFace1.coordIndex3 = iNext - numVerts; - - newViewerFace2.coordIndex1 = i; - newViewerFace2.coordIndex2 = iNext - numVerts; - newViewerFace2.coordIndex3 = iNext; - - // profile cut faces - if (whichVert == cut1Vert) - { - newViewerFace1.n1 = newLayer.cutNormal1; - newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; - - newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; - newViewerFace2.n2 = lastCutNormal1; - } - else if (whichVert == cut2Vert) - { - newViewerFace1.n1 = newLayer.cutNormal2; - newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; - - newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; - newViewerFace2.n2 = lastCutNormal2; - } - - else // outer and hollow faces - { - if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) - { // looks terrible when path is twisted... need vertex normals here - newViewerFace1.CalcSurfaceNormal(); - newViewerFace2.CalcSurfaceNormal(); - } - else - { - newViewerFace1.n1 = this.normals[i]; - newViewerFace1.n2 = this.normals[i - numVerts]; - newViewerFace1.n3 = this.normals[iNext - numVerts]; - - newViewerFace2.n1 = this.normals[i]; - newViewerFace2.n2 = this.normals[iNext - numVerts]; - newViewerFace2.n3 = this.normals[iNext]; - } - } - - this.viewerFaces.Add(newViewerFace1); - this.viewerFaces.Add(newViewerFace2); - - } - } - } - - lastCutNormal1 = newLayer.cutNormal1; - lastCutNormal2 = newLayer.cutNormal2; - lastV = 1.0f - node.percentOfPath; - - if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) - { - // add the top faces to the viewerFaces list here - Coord faceNormal = newLayer.faceNormal; - ViewerFace newViewerFace = new ViewerFace(); - newViewerFace.primFaceNumber = 0; - int numFaces = newLayer.faces.Count; - List faces = newLayer.faces; - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; - newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; - newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; - - newViewerFace.coordIndex1 = face.v1 - coordsLen; - newViewerFace.coordIndex2 = face.v2 - coordsLen; - newViewerFace.coordIndex3 = face.v3 - coordsLen; - - newViewerFace.n1 = faceNormal; - newViewerFace.n2 = faceNormal; - newViewerFace.n3 = faceNormal; - - newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen]; - newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; - newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; - - this.viewerFaces.Add(newViewerFace); - } - } - - - } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) - - } - - - /// - /// DEPRICATED - use Extrude(PathType.Linear) instead - /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. - /// - /// - public void ExtrudeLinear() - { - this.Extrude(PathType.Linear); - } - - - /// - /// DEPRICATED - use Extrude(PathType.Circular) instead - /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. - /// - /// - public void ExtrudeCircular() - { - this.Extrude(PathType.Circular); - } - - - private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3) - { - Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - Coord normal = Coord.Cross(edge1, edge2); - - normal.Normalize(); - - return normal; - } - - private Coord SurfaceNormal(Face face) - { - return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); - } - - /// - /// Calculate the surface normal for a face in the list of faces - /// - /// - /// - public Coord SurfaceNormal(int faceIndex) - { - int numFaces = this.faces.Count; - if (faceIndex < 0 || faceIndex >= numFaces) - throw new Exception("faceIndex out of range"); - - return SurfaceNormal(this.faces[faceIndex]); - } - - /// - /// Duplicates a PrimMesh object. All object properties are copied by value, including lists. - /// - /// - public PrimMesh Copy() - { - PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides); - copy.twistBegin = this.twistBegin; - copy.twistEnd = this.twistEnd; - copy.topShearX = this.topShearX; - copy.topShearY = this.topShearY; - copy.pathCutBegin = this.pathCutBegin; - copy.pathCutEnd = this.pathCutEnd; - copy.dimpleBegin = this.dimpleBegin; - copy.dimpleEnd = this.dimpleEnd; - copy.skew = this.skew; - copy.holeSizeX = this.holeSizeX; - copy.holeSizeY = this.holeSizeY; - copy.taperX = this.taperX; - copy.taperY = this.taperY; - copy.radius = this.radius; - copy.revolutions = this.revolutions; - copy.stepsPerRevolution = this.stepsPerRevolution; - copy.calcVertexNormals = this.calcVertexNormals; - copy.normalsProcessed = this.normalsProcessed; - copy.viewerMode = this.viewerMode; - copy.numPrimFaces = this.numPrimFaces; - copy.errorMessage = this.errorMessage; - - copy.coords = new List(this.coords); - copy.faces = new List(this.faces); - copy.viewerFaces = new List(this.viewerFaces); - copy.normals = new List(this.normals); - - return copy; - } - - /// - /// Calculate surface normals for all of the faces in the list of faces in this mesh - /// - public void CalcNormals() - { - if (normalsProcessed) - return; - - normalsProcessed = true; - - int numFaces = faces.Count; - - if (!this.calcVertexNormals) - this.normals = new List(); - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - - this.normals.Add(SurfaceNormal(i).Normalize()); - - int normIndex = normals.Count - 1; - face.n1 = normIndex; - face.n2 = normIndex; - face.n3 = normIndex; - - this.faces[i] = face; - } - } - - /// - /// Adds a value to each XYZ vertex coordinate in the mesh - /// - /// - /// - /// - public void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.AddPos(x, y, z); - this.viewerFaces[i] = v; - } - } - } - - /// - /// Rotates the mesh - /// - /// - public void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - if (this.normals != null) - { - int numNormals = this.normals.Count; - for (i = 0; i < numNormals; i++) - this.normals[i] *= q; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= q; - v.v2 *= q; - v.v3 *= q; - - v.n1 *= q; - v.n2 *= q; - v.n3 *= q; - this.viewerFaces[i] = v; - } - } - } - -#if VERTEX_INDEXER - public VertexIndexer GetVertexIndexer() - { - if (this.viewerMode && this.viewerFaces.Count > 0) - return new VertexIndexer(this); - return null; - } -#endif - - /// - /// Scales the mesh - /// - /// - /// - /// - public void Scale(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - //Coord vert; - - Coord m = new Coord(x, y, z); - for (i = 0; i < numVerts; i++) - this.coords[i] *= m; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= m; - v.v2 *= m; - v.v3 *= m; - this.viewerFaces[i] = v; - } - - } - - } - - /// - /// Dumps the mesh to a Blender compatible "Raw" format file - /// - /// - /// - /// - public void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } -} +/* + * Copyright (c) Contributors + * 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 OpenSimulator 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.Text; +using System.IO; + +namespace PrimMesher +{ + public struct Quat + { + /// X value + public float X; + /// Y value + public float Y; + /// Z value + public float Z; + /// W value + public float W; + + public Quat(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + public Quat(Coord axis, float angle) + { + axis = axis.Normalize(); + + angle *= 0.5f; + float c = (float)Math.Cos(angle); + float s = (float)Math.Sin(angle); + + X = axis.X * s; + Y = axis.Y * s; + Z = axis.Z * s; + W = c; + + Normalize(); + } + + public float Length() + { + return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); + } + + public Quat Normalize() + { + const float MAG_THRESHOLD = 0.0000001f; + float mag = Length(); + + // Catch very small rounding errors when normalizing + if (mag > MAG_THRESHOLD) + { + float oomag = 1f / mag; + X *= oomag; + Y *= oomag; + Z *= oomag; + W *= oomag; + } + else + { + X = 0f; + Y = 0f; + Z = 0f; + W = 1f; + } + + return this; + } + + public static Quat operator *(Quat q1, Quat q2) + { + float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y; + float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X; + float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W; + float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z; + return new Quat(x, y, z, w); + } + + public override string ToString() + { + return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">"; + } + } + + public struct Coord + { + public float X; + public float Y; + public float Z; + + public Coord(float x, float y, float z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + public float Length() + { + return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); + } + + public Coord Invert() + { + this.X = -this.X; + this.Y = -this.Y; + this.Z = -this.Z; + + return this; + } + + public Coord Normalize() + { + const float MAG_THRESHOLD = 0.0000001f; + float mag = Length(); + + // Catch very small rounding errors when normalizing + if (mag > MAG_THRESHOLD) + { + float oomag = 1.0f / mag; + this.X *= oomag; + this.Y *= oomag; + this.Z *= oomag; + } + else + { + this.X = 0.0f; + this.Y = 0.0f; + this.Z = 0.0f; + } + + return this; + } + + public override string ToString() + { + return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString(); + } + + public static Coord Cross(Coord c1, Coord c2) + { + return new Coord( + c1.Y * c2.Z - c2.Y * c1.Z, + c1.Z * c2.X - c2.Z * c1.X, + c1.X * c2.Y - c2.X * c1.Y + ); + } + + public static Coord operator +(Coord v, Coord a) + { + return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z); + } + + public static Coord operator *(Coord v, Coord m) + { + return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z); + } + + public static Coord operator *(Coord v, Quat q) + { + // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/ + + Coord c2 = new Coord(0.0f, 0.0f, 0.0f); + + c2.X = q.W * q.W * v.X + + 2f * q.Y * q.W * v.Z - + 2f * q.Z * q.W * v.Y + + q.X * q.X * v.X + + 2f * q.Y * q.X * v.Y + + 2f * q.Z * q.X * v.Z - + q.Z * q.Z * v.X - + q.Y * q.Y * v.X; + + c2.Y = + 2f * q.X * q.Y * v.X + + q.Y * q.Y * v.Y + + 2f * q.Z * q.Y * v.Z + + 2f * q.W * q.Z * v.X - + q.Z * q.Z * v.Y + + q.W * q.W * v.Y - + 2f * q.X * q.W * v.Z - + q.X * q.X * v.Y; + + c2.Z = + 2f * q.X * q.Z * v.X + + 2f * q.Y * q.Z * v.Y + + q.Z * q.Z * v.Z - + 2f * q.W * q.Y * v.X - + q.Y * q.Y * v.Z + + 2f * q.W * q.X * v.Y - + q.X * q.X * v.Z + + q.W * q.W * v.Z; + + return c2; + } + } + + public struct UVCoord + { + public float U; + public float V; + + + public UVCoord(float u, float v) + { + this.U = u; + this.V = v; + } + } + + public struct Face + { + public int primFace; + + // vertices + public int v1; + public int v2; + public int v3; + + //normals + public int n1; + public int n2; + public int n3; + + // uvs + public int uv1; + public int uv2; + public int uv3; + + public Face(int v1, int v2, int v3) + { + primFace = 0; + + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + this.n1 = 0; + this.n2 = 0; + this.n3 = 0; + + this.uv1 = 0; + this.uv2 = 0; + this.uv3 = 0; + + } + + public Face(int v1, int v2, int v3, int n1, int n2, int n3) + { + primFace = 0; + + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + this.n1 = n1; + this.n2 = n2; + this.n3 = n3; + + this.uv1 = 0; + this.uv2 = 0; + this.uv3 = 0; + } + + public Coord SurfaceNormal(List coordList) + { + Coord c1 = coordList[this.v1]; + Coord c2 = coordList[this.v2]; + Coord c3 = coordList[this.v3]; + + Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + return Coord.Cross(edge1, edge2).Normalize(); + } + } + + public struct ViewerFace + { + public int primFaceNumber; + + public Coord v1; + public Coord v2; + public Coord v3; + + public int coordIndex1; + public int coordIndex2; + public int coordIndex3; + + public Coord n1; + public Coord n2; + public Coord n3; + + public UVCoord uv1; + public UVCoord uv2; + public UVCoord uv3; + + public ViewerFace(int primFaceNumber) + { + this.primFaceNumber = primFaceNumber; + + this.v1 = new Coord(); + this.v2 = new Coord(); + this.v3 = new Coord(); + + this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet + + this.n1 = new Coord(); + this.n2 = new Coord(); + this.n3 = new Coord(); + + this.uv1 = new UVCoord(); + this.uv2 = new UVCoord(); + this.uv3 = new UVCoord(); + } + + public void Scale(float x, float y, float z) + { + this.v1.X *= x; + this.v1.Y *= y; + this.v1.Z *= z; + + this.v2.X *= x; + this.v2.Y *= y; + this.v2.Z *= z; + + this.v3.X *= x; + this.v3.Y *= y; + this.v3.Z *= z; + } + + public void AddPos(float x, float y, float z) + { + this.v1.X += x; + this.v2.X += x; + this.v3.X += x; + + this.v1.Y += y; + this.v2.Y += y; + this.v3.Y += y; + + this.v1.Z += z; + this.v2.Z += z; + this.v3.Z += z; + } + + public void AddRot(Quat q) + { + this.v1 *= q; + this.v2 *= q; + this.v3 *= q; + + this.n1 *= q; + this.n2 *= q; + this.n3 *= q; + } + + public void CalcSurfaceNormal() + { + + Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z); + Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z); + + this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); + } + } + + internal struct Angle + { + internal float angle; + internal float X; + internal float Y; + + internal Angle(float angle, float x, float y) + { + this.angle = angle; + this.X = x; + this.Y = y; + } + } + + internal class AngleList + { + private float iX, iY; // intersection point + + private static Angle[] angles3 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), + new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private static Coord[] normals3 = + { + new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), + new Coord(-0.5f, 0.0f, 0.0f).Normalize(), + new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), + new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() + }; + + private static Angle[] angles4 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.25f, 0.0f, 1.0f), + new Angle(0.5f, -1.0f, 0.0f), + new Angle(0.75f, 0.0f, -1.0f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private static Coord[] normals4 = + { + new Coord(0.5f, 0.5f, 0.0f).Normalize(), + new Coord(-0.5f, 0.5f, 0.0f).Normalize(), + new Coord(-0.5f, -0.5f, 0.0f).Normalize(), + new Coord(0.5f, -0.5f, 0.0f).Normalize(), + new Coord(0.5f, 0.5f, 0.0f).Normalize() + }; + + private static Angle[] angles24 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f), + new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f), + new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f), + new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f), + new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f), + new Angle(0.25f, 0.0f, 1.0f), + new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f), + new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), + new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f), + new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f), + new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f), + new Angle(0.5f, -1.0f, 0.0f), + new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f), + new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f), + new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f), + new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), + new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f), + new Angle(0.75f, 0.0f, -1.0f), + new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f), + new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f), + new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f), + new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f), + new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private Angle interpolatePoints(float newPoint, Angle p1, Angle p2) + { + float m = (newPoint - p1.angle) / (p2.angle - p1.angle); + return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y)); + } + + private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); + + if (denom != 0.0) + { + double ua = uaNumerator / denom; + iX = (float)(x1 + ua * (x2 - x1)); + iY = (float)(y1 + ua * (y2 - y1)); + } + } + + internal List angles; + internal List normals; + + internal void makeAngles(int sides, float startAngle, float stopAngle) + { + angles = new List(); + normals = new List(); + + double twoPi = System.Math.PI * 2.0; + float twoPiInv = 1.0f / (float)twoPi; + + if (sides < 1) + throw new Exception("number of sides not greater than zero"); + if (stopAngle <= startAngle) + throw new Exception("stopAngle not greater than startAngle"); + + if ((sides == 3 || sides == 4 || sides == 24)) + { + startAngle *= twoPiInv; + stopAngle *= twoPiInv; + + Angle[] sourceAngles; + if (sides == 3) + sourceAngles = angles3; + else if (sides == 4) + sourceAngles = angles4; + else sourceAngles = angles24; + + int startAngleIndex = (int)(startAngle * sides); + int endAngleIndex = sourceAngles.Length - 1; + if (stopAngle < 1.0f) + endAngleIndex = (int)(stopAngle * sides) + 1; + if (endAngleIndex == startAngleIndex) + endAngleIndex++; + + for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) + { + angles.Add(sourceAngles[angleIndex]); + if (sides == 3) + normals.Add(normals3[angleIndex]); + else if (sides == 4) + normals.Add(normals4[angleIndex]); + } + + if (startAngle > 0.0f) + angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); + + if (stopAngle < 1.0f) + { + int lastAngleIndex = angles.Count - 1; + angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]); + } + } + else + { + double stepSize = twoPi / sides; + + int startStep = (int)(startAngle / stepSize); + double angle = stepSize * startStep; + int step = startStep; + double stopAngleTest = stopAngle; + if (stopAngle < twoPi) + { + stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1); + if (stopAngleTest < stopAngle) + stopAngleTest += stepSize; + if (stopAngleTest > twoPi) + stopAngleTest = twoPi; + } + + while (angle <= stopAngleTest) + { + Angle newAngle; + newAngle.angle = (float)angle; + newAngle.X = (float)System.Math.Cos(angle); + newAngle.Y = (float)System.Math.Sin(angle); + angles.Add(newAngle); + step += 1; + angle = stepSize * step; + } + + if (startAngle > angles[0].angle) + { + Angle newAngle; + intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle)); + newAngle.angle = startAngle; + newAngle.X = iX; + newAngle.Y = iY; + angles[0] = newAngle; + } + + int index = angles.Count - 1; + if (stopAngle < angles[index].angle) + { + Angle newAngle; + intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle)); + newAngle.angle = stopAngle; + newAngle.X = iX; + newAngle.Y = iY; + angles[index] = newAngle; + } + } + } + } + + /// + /// generates a profile for extrusion + /// + internal class Profile + { + private const float twoPi = 2.0f * (float)Math.PI; + + internal string errorMessage = null; + + internal List coords; + internal List faces; + internal List vertexNormals; + internal List us; + internal List faceUVs; + internal List faceNumbers; + + // use these for making individual meshes for each prim face + internal List outerCoordIndices = null; + internal List hollowCoordIndices = null; + internal List cut1CoordIndices = null; + internal List cut2CoordIndices = null; + + internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); + internal Coord cutNormal1 = new Coord(); + internal Coord cutNormal2 = new Coord(); + + internal int numOuterVerts = 0; + internal int numHollowVerts = 0; + + internal int outerFaceNumber = -1; + internal int hollowFaceNumber = -1; + + internal bool calcVertexNormals = false; + internal int bottomFaceNumber = 0; + internal int numPrimFaces = 0; + + internal Profile() + { + this.coords = new List(); + this.faces = new List(); + this.vertexNormals = new List(); + this.us = new List(); + this.faceUVs = new List(); + this.faceNumbers = new List(); + } + + internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) + { + this.calcVertexNormals = calcVertexNormals; + this.coords = new List(); + this.faces = new List(); + this.vertexNormals = new List(); + this.us = new List(); + this.faceUVs = new List(); + this.faceNumbers = new List(); + + Coord center = new Coord(0.0f, 0.0f, 0.0f); + //bool hasCenter = false; + + List hollowCoords = new List(); + List hollowNormals = new List(); + List hollowUs = new List(); + + if (calcVertexNormals) + { + this.outerCoordIndices = new List(); + this.hollowCoordIndices = new List(); + this.cut1CoordIndices = new List(); + this.cut2CoordIndices = new List(); + } + + bool hasHollow = (hollow > 0.0f); + + bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); + + AngleList angles = new AngleList(); + AngleList hollowAngles = new AngleList(); + + float xScale = 0.5f; + float yScale = 0.5f; + if (sides == 4) // corners of a square are sqrt(2) from center + { + xScale = 0.707f; + yScale = 0.707f; + } + + float startAngle = profileStart * twoPi; + float stopAngle = profileEnd * twoPi; + + try { angles.makeAngles(sides, startAngle, stopAngle); } + catch (Exception ex) + { + + errorMessage = "makeAngles failed: Exception: " + ex.ToString() + + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); + + return; + } + + this.numOuterVerts = angles.angles.Count; + + // flag to create as few triangles as possible for 3 or 4 side profile + bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut); + + if (hasHollow) + { + if (sides == hollowSides) + hollowAngles = angles; + else + { + try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); } + catch (Exception ex) + { + errorMessage = "makeAngles failed: Exception: " + ex.ToString() + + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); + + return; + } + } + this.numHollowVerts = hollowAngles.angles.Count; + } + else if (!simpleFace) + { + this.coords.Add(center); + //hasCenter = true; + if (this.calcVertexNormals) + this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); + this.us.Add(0.0f); + } + + float z = 0.0f; + + Angle angle; + Coord newVert = new Coord(); + if (hasHollow && hollowSides != sides) + { + int numHollowAngles = hollowAngles.angles.Count; + for (int i = 0; i < numHollowAngles; i++) + { + angle = hollowAngles.angles[i]; + newVert.X = hollow * xScale * angle.X; + newVert.Y = hollow * yScale * angle.Y; + newVert.Z = z; + + hollowCoords.Add(newVert); + if (this.calcVertexNormals) + { + if (hollowSides < 5) + hollowNormals.Add(hollowAngles.normals[i].Invert()); + else + hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); + + hollowUs.Add(angle.angle * hollow); + } + } + } + + int index = 0; + int numAngles = angles.angles.Count; + + for (int i = 0; i < numAngles; i++) + { + angle = angles.angles[i]; + newVert.X = angle.X * xScale; + newVert.Y = angle.Y * yScale; + newVert.Z = z; + this.coords.Add(newVert); + if (this.calcVertexNormals) + { + this.outerCoordIndices.Add(this.coords.Count - 1); + + if (sides < 5) + { + this.vertexNormals.Add(angles.normals[i]); + float u = angle.angle; + this.us.Add(u); + } + else + { + this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); + this.us.Add(angle.angle); + } + } + + if (hasHollow) + { + if (hollowSides == sides) + { + newVert.X *= hollow; + newVert.Y *= hollow; + newVert.Z = z; + hollowCoords.Add(newVert); + if (this.calcVertexNormals) + { + if (sides < 5) + { + hollowNormals.Add(angles.normals[i].Invert()); + } + + else + hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); + + hollowUs.Add(angle.angle * hollow); + } + } + } + else if (!simpleFace && createFaces && angle.angle > 0.0001f) + { + Face newFace = new Face(); + newFace.v1 = 0; + newFace.v2 = index; + newFace.v3 = index + 1; + + this.faces.Add(newFace); + } + index += 1; + } + + if (hasHollow) + { + hollowCoords.Reverse(); + if (this.calcVertexNormals) + { + hollowNormals.Reverse(); + hollowUs.Reverse(); + } + + if (createFaces) + { + //int numOuterVerts = this.coords.Count; + //numOuterVerts = this.coords.Count; + //int numHollowVerts = hollowCoords.Count; + int numTotalVerts = this.numOuterVerts + this.numHollowVerts; + + if (this.numOuterVerts == this.numHollowVerts) + { + Face newFace = new Face(); + + for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++) + { + newFace.v1 = coordIndex; + newFace.v2 = coordIndex + 1; + newFace.v3 = numTotalVerts - coordIndex - 1; + this.faces.Add(newFace); + + newFace.v1 = coordIndex + 1; + newFace.v2 = numTotalVerts - coordIndex - 2; + newFace.v3 = numTotalVerts - coordIndex - 1; + this.faces.Add(newFace); + } + } + else + { + if (this.numOuterVerts < this.numHollowVerts) + { + Face newFace = new Face(); + int j = 0; // j is the index for outer vertices + int maxJ = this.numOuterVerts - 1; + for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices + { + if (j < maxJ) + if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) + { + newFace.v1 = numTotalVerts - i - 1; + newFace.v2 = j; + newFace.v3 = j + 1; + + this.faces.Add(newFace); + j += 1; + } + + newFace.v1 = j; + newFace.v2 = numTotalVerts - i - 2; + newFace.v3 = numTotalVerts - i - 1; + + this.faces.Add(newFace); + } + } + else // numHollowVerts < numOuterVerts + { + Face newFace = new Face(); + int j = 0; // j is the index for inner vertices + int maxJ = this.numHollowVerts - 1; + for (int i = 0; i < this.numOuterVerts; i++) + { + if (j < maxJ) + if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) + { + newFace.v1 = i; + newFace.v2 = numTotalVerts - j - 2; + newFace.v3 = numTotalVerts - j - 1; + + this.faces.Add(newFace); + j += 1; + } + + newFace.v1 = numTotalVerts - j - 1; + newFace.v2 = i; + newFace.v3 = i + 1; + + this.faces.Add(newFace); + } + } + } + } + + if (calcVertexNormals) + { + foreach (Coord hc in hollowCoords) + { + this.coords.Add(hc); + hollowCoordIndices.Add(this.coords.Count - 1); + } + } + else + this.coords.AddRange(hollowCoords); + + if (this.calcVertexNormals) + { + this.vertexNormals.AddRange(hollowNormals); + this.us.AddRange(hollowUs); + + } + } + + if (simpleFace && createFaces) + { + if (sides == 3) + this.faces.Add(new Face(0, 1, 2)); + else if (sides == 4) + { + this.faces.Add(new Face(0, 1, 2)); + this.faces.Add(new Face(0, 2, 3)); + } + } + + if (calcVertexNormals && hasProfileCut) + { + int lastOuterVertIndex = this.numOuterVerts - 1; + + if (hasHollow) + { + this.cut1CoordIndices.Add(0); + this.cut1CoordIndices.Add(this.coords.Count - 1); + + this.cut2CoordIndices.Add(lastOuterVertIndex + 1); + this.cut2CoordIndices.Add(lastOuterVertIndex); + + this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; + this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); + + this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; + this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); + } + + else + { + this.cut1CoordIndices.Add(0); + this.cut1CoordIndices.Add(1); + + this.cut2CoordIndices.Add(lastOuterVertIndex); + this.cut2CoordIndices.Add(0); + + this.cutNormal1.X = this.vertexNormals[1].Y; + this.cutNormal1.Y = -this.vertexNormals[1].X; + + this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; + this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; + + } + this.cutNormal1.Normalize(); + this.cutNormal2.Normalize(); + } + + this.MakeFaceUVs(); + + hollowCoords = null; + hollowNormals = null; + hollowUs = null; + + if (calcVertexNormals) + { // calculate prim face numbers + + // face number order is top, outer, hollow, bottom, start cut, end cut + // I know it's ugly but so is the whole concept of prim face numbers + + int faceNum = 1; // start with outer faces + this.outerFaceNumber = faceNum; + + int startVert = hasProfileCut && !hasHollow ? 1 : 0; + if (startVert > 0) + this.faceNumbers.Add(-1); + for (int i = 0; i < this.numOuterVerts - 1; i++) + //this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); + this.faceNumbers.Add(sides < 5 && i < sides ? faceNum++ : faceNum); + + //if (!hasHollow && !hasProfileCut) + // this.bottomFaceNumber = faceNum++; + + this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); + + if (sides > 4 && (hasHollow || hasProfileCut)) + faceNum++; + + if (sides < 5 && (hasHollow || hasProfileCut) && this.numOuterVerts < sides) + faceNum++; + + if (hasHollow) + { + for (int i = 0; i < this.numHollowVerts; i++) + this.faceNumbers.Add(faceNum); + + this.hollowFaceNumber = faceNum++; + } + //if (hasProfileCut || hasHollow) + // this.bottomFaceNumber = faceNum++; + this.bottomFaceNumber = faceNum++; + + if (hasHollow && hasProfileCut) + this.faceNumbers.Add(faceNum++); + + for (int i = 0; i < this.faceNumbers.Count; i++) + if (this.faceNumbers[i] == -1) + this.faceNumbers[i] = faceNum++; + + this.numPrimFaces = faceNum; + } + + } + + internal void MakeFaceUVs() + { + this.faceUVs = new List(); + foreach (Coord c in this.coords) + this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); + } + + internal Profile Copy() + { + return this.Copy(true); + } + + internal Profile Copy(bool needFaces) + { + Profile copy = new Profile(); + + copy.coords.AddRange(this.coords); + copy.faceUVs.AddRange(this.faceUVs); + + if (needFaces) + copy.faces.AddRange(this.faces); + if ((copy.calcVertexNormals = this.calcVertexNormals) == true) + { + copy.vertexNormals.AddRange(this.vertexNormals); + copy.faceNormal = this.faceNormal; + copy.cutNormal1 = this.cutNormal1; + copy.cutNormal2 = this.cutNormal2; + copy.us.AddRange(this.us); + copy.faceNumbers.AddRange(this.faceNumbers); + + copy.cut1CoordIndices = new List(this.cut1CoordIndices); + copy.cut2CoordIndices = new List(this.cut2CoordIndices); + copy.hollowCoordIndices = new List(this.hollowCoordIndices); + copy.outerCoordIndices = new List(this.outerCoordIndices); + } + copy.numOuterVerts = this.numOuterVerts; + copy.numHollowVerts = this.numHollowVerts; + + return copy; + } + + internal void AddPos(Coord v) + { + this.AddPos(v.X, v.Y, v.Z); + } + + internal void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + } + + internal void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + if (this.calcVertexNormals) + { + int numNormals = this.vertexNormals.Count; + for (i = 0; i < numNormals; i++) + this.vertexNormals[i] *= q; + + this.faceNormal *= q; + this.cutNormal1 *= q; + this.cutNormal2 *= q; + + } + } + + internal void Scale(float x, float y) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X *= x; + vert.Y *= y; + this.coords[i] = vert; + } + } + + /// + /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices + /// + internal void FlipNormals() + { + int i; + int numFaces = this.faces.Count; + Face tmpFace; + int tmp; + + for (i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmp = tmpFace.v3; + tmpFace.v3 = tmpFace.v1; + tmpFace.v1 = tmp; + this.faces[i] = tmpFace; + } + + if (this.calcVertexNormals) + { + int normalCount = this.vertexNormals.Count; + if (normalCount > 0) + { + Coord n = this.vertexNormals[normalCount - 1]; + n.Z = -n.Z; + this.vertexNormals[normalCount - 1] = n; + } + } + + this.faceNormal.X = -this.faceNormal.X; + this.faceNormal.Y = -this.faceNormal.Y; + this.faceNormal.Z = -this.faceNormal.Z; + + int numfaceUVs = this.faceUVs.Count; + for (i = 0; i < numfaceUVs; i++) + { + UVCoord uv = this.faceUVs[i]; + uv.V = 1.0f - uv.V; + this.faceUVs[i] = uv; + } + } + + internal void AddValue2FaceVertexIndices(int num) + { + int numFaces = this.faces.Count; + Face tmpFace; + for (int i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmpFace.v1 += num; + tmpFace.v2 += num; + tmpFace.v3 += num; + + this.faces[i] = tmpFace; + } + } + + internal void AddValue2FaceNormalIndices(int num) + { + if (this.calcVertexNormals) + { + int numFaces = this.faces.Count; + Face tmpFace; + for (int i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmpFace.n1 += num; + tmpFace.n2 += num; + tmpFace.n3 += num; + + this.faces[i] = tmpFace; + } + } + } + + internal void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } + + public struct PathNode + { + public Coord position; + public Quat rotation; + public float xScale; + public float yScale; + public float percentOfPath; + } + + public enum PathType { Linear = 0, Circular = 1, Flexible = 2 } + + public class Path + { + public List pathNodes = new List(); + + public float twistBegin = 0.0f; + public float twistEnd = 0.0f; + public float topShearX = 0.0f; + public float topShearY = 0.0f; + public float pathCutBegin = 0.0f; + public float pathCutEnd = 1.0f; + public float dimpleBegin = 0.0f; + public float dimpleEnd = 1.0f; + public float skew = 0.0f; + public float holeSizeX = 1.0f; // called pathScaleX in pbs + public float holeSizeY = 0.25f; + public float taperX = 0.0f; + public float taperY = 0.0f; + public float radius = 0.0f; + public float revolutions = 1.0f; + public int stepsPerRevolution = 24; + + private const float twoPi = 2.0f * (float)Math.PI; + + public void Create(PathType pathType, int steps) + { + if (pathType == PathType.Linear || pathType == PathType.Flexible) + { + int step = 0; + + float length = this.pathCutEnd - this.pathCutBegin; + float twistTotal = twistEnd - twistBegin; + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number + + float start = -0.5f; + float stepSize = length / (float)steps; + float percentOfPathMultiplier = stepSize; + float xOffset = 0.0f; + float yOffset = 0.0f; + float zOffset = start; + float xOffsetStepIncrement = this.topShearX / steps; + float yOffsetStepIncrement = this.topShearY / steps; + + float percentOfPath = this.pathCutBegin; + zOffset += percentOfPath; + + // sanity checks + + bool done = false; + + while (!done) + { + PathNode newNode = new PathNode(); + + newNode.xScale = 1.0f; + if (this.taperX == 0.0f) + newNode.xScale = 1.0f; + else if (this.taperX > 0.0f) + newNode.xScale = 1.0f - percentOfPath * this.taperX; + else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX; + + newNode.yScale = 1.0f; + if (this.taperY == 0.0f) + newNode.yScale = 1.0f; + else if (this.taperY > 0.0f) + newNode.yScale = 1.0f - percentOfPath * this.taperY; + else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY; + + float twist = twistBegin + twistTotal * percentOfPath; + + newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); + newNode.position = new Coord(xOffset, yOffset, zOffset); + newNode.percentOfPath = percentOfPath; + + pathNodes.Add(newNode); + + if (step < steps) + { + step += 1; + percentOfPath += percentOfPathMultiplier; + xOffset += xOffsetStepIncrement; + yOffset += yOffsetStepIncrement; + zOffset += stepSize; + if (percentOfPath > this.pathCutEnd) + done = true; + } + else done = true; + } + } // end of linear path code + + else // pathType == Circular + { + float twistTotal = twistEnd - twistBegin; + + // if the profile has a lot of twist, add more layers otherwise the layers may overlap + // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't + // accurately match the viewer + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + { + if (twistTotalAbs > Math.PI * 1.5f) + steps *= 2; + if (twistTotalAbs > Math.PI * 3.0f) + steps *= 2; + } + + float yPathScale = this.holeSizeY * 0.5f; + float pathLength = this.pathCutEnd - this.pathCutBegin; + float totalSkew = this.skew * 2.0f * pathLength; + float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew; + float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY)); + float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f; + + // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end + // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used + // to calculate the sine for generating the path radius appears to approximate it's effects there + // too, but there are some subtle differences in the radius which are noticeable as the prim size + // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on + // the meshes generated with this technique appear nearly identical in shape to the same prims when + // displayed by the viewer. + + float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f; + float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f; + float stepSize = twoPi / this.stepsPerRevolution; + + int step = (int)(startAngle / stepSize); + float angle = startAngle; + + bool done = false; + while (!done) // loop through the length of the path and add the layers + { + PathNode newNode = new PathNode(); + + float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX; + float yProfileScale = this.holeSizeY; + + float percentOfPath = angle / (twoPi * this.revolutions); + float percentOfAngles = (angle - startAngle) / (endAngle - startAngle); + + if (this.taperX > 0.01f) + xProfileScale *= 1.0f - percentOfPath * this.taperX; + else if (this.taperX < -0.01f) + xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX; + + if (this.taperY > 0.01f) + yProfileScale *= 1.0f - percentOfPath * this.taperY; + else if (this.taperY < -0.01f) + yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY; + + newNode.xScale = xProfileScale; + newNode.yScale = yProfileScale; + + float radiusScale = 1.0f; + if (this.radius > 0.001f) + radiusScale = 1.0f - this.radius * percentOfPath; + else if (this.radius < 0.001f) + radiusScale = 1.0f + this.radius * (1.0f - percentOfPath); + + float twist = twistBegin + twistTotal * percentOfPath; + + float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles); + xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor; + + float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale; + + float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale; + + newNode.position = new Coord(xOffset, yOffset, zOffset); + + // now orient the rotation of the profile layer relative to it's position on the path + // adding taperY to the angle used to generate the quat appears to approximate the viewer + + newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY); + + // next apply twist rotation to the profile layer + if (twistTotal != 0.0f || twistBegin != 0.0f) + newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); + + newNode.percentOfPath = percentOfPath; + + pathNodes.Add(newNode); + + // calculate terms for next iteration + // calculate the angle for the next iteration of the loop + + if (angle >= endAngle - 0.01) + done = true; + else + { + step += 1; + angle = stepSize * step; + if (angle > endAngle) + angle = endAngle; + } + } + } + } + } + + public class PrimMesh + { + public string errorMessage = ""; + private const float twoPi = 2.0f * (float)Math.PI; + + public List coords; + public List normals; + public List faces; + + public List viewerFaces; + + private int sides = 4; + private int hollowSides = 4; + private float profileStart = 0.0f; + private float profileEnd = 1.0f; + private float hollow = 0.0f; + public int twistBegin = 0; + public int twistEnd = 0; + public float topShearX = 0.0f; + public float topShearY = 0.0f; + public float pathCutBegin = 0.0f; + public float pathCutEnd = 1.0f; + public float dimpleBegin = 0.0f; + public float dimpleEnd = 1.0f; + public float skew = 0.0f; + public float holeSizeX = 1.0f; // called pathScaleX in pbs + public float holeSizeY = 0.25f; + public float taperX = 0.0f; + public float taperY = 0.0f; + public float radius = 0.0f; + public float revolutions = 1.0f; + public int stepsPerRevolution = 24; + + private int profileOuterFaceNumber = -1; + private int profileHollowFaceNumber = -1; + + private bool hasProfileCut = false; + private bool hasHollow = false; + public bool calcVertexNormals = false; + private bool normalsProcessed = false; + public bool viewerMode = false; + public bool sphereMode = false; + + public int numPrimFaces = 0; + + /// + /// Human readable string representation of the parameters used to create a mesh. + /// + /// + public string ParamsToDisplayString() + { + string s = ""; + s += "sides..................: " + this.sides.ToString(); + s += "\nhollowSides..........: " + this.hollowSides.ToString(); + s += "\nprofileStart.........: " + this.profileStart.ToString(); + s += "\nprofileEnd...........: " + this.profileEnd.ToString(); + s += "\nhollow...............: " + this.hollow.ToString(); + s += "\ntwistBegin...........: " + this.twistBegin.ToString(); + s += "\ntwistEnd.............: " + this.twistEnd.ToString(); + s += "\ntopShearX............: " + this.topShearX.ToString(); + s += "\ntopShearY............: " + this.topShearY.ToString(); + s += "\npathCutBegin.........: " + this.pathCutBegin.ToString(); + s += "\npathCutEnd...........: " + this.pathCutEnd.ToString(); + s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString(); + s += "\ndimpleEnd............: " + this.dimpleEnd.ToString(); + s += "\nskew.................: " + this.skew.ToString(); + s += "\nholeSizeX............: " + this.holeSizeX.ToString(); + s += "\nholeSizeY............: " + this.holeSizeY.ToString(); + s += "\ntaperX...............: " + this.taperX.ToString(); + s += "\ntaperY...............: " + this.taperY.ToString(); + s += "\nradius...............: " + this.radius.ToString(); + s += "\nrevolutions..........: " + this.revolutions.ToString(); + s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); + s += "\nsphereMode...........: " + this.sphereMode.ToString(); + s += "\nhasProfileCut........: " + this.hasProfileCut.ToString(); + s += "\nhasHollow............: " + this.hasHollow.ToString(); + s += "\nviewerMode...........: " + this.viewerMode.ToString(); + + return s; + } + + public int ProfileOuterFaceNumber + { + get { return profileOuterFaceNumber; } + } + + public int ProfileHollowFaceNumber + { + get { return profileHollowFaceNumber; } + } + + public bool HasProfileCut + { + get { return hasProfileCut; } + } + + public bool HasHollow + { + get { return hasHollow; } + } + + + /// + /// Constructs a PrimMesh object and creates the profile for extrusion. + /// + /// + /// + /// + /// + /// + public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) + { + this.coords = new List(); + this.faces = new List(); + + this.sides = sides; + this.profileStart = profileStart; + this.profileEnd = profileEnd; + this.hollow = hollow; + this.hollowSides = hollowSides; + + if (sides < 3) + this.sides = 3; + if (hollowSides < 3) + this.hollowSides = 3; + if (profileStart < 0.0f) + this.profileStart = 0.0f; + if (profileEnd > 1.0f) + this.profileEnd = 1.0f; + if (profileEnd < 0.02f) + this.profileEnd = 0.02f; + if (profileStart >= profileEnd) + this.profileStart = profileEnd - 0.02f; + if (hollow > 0.99f) + this.hollow = 0.99f; + if (hollow < 0.0f) + this.hollow = 0.0f; + + //if (sphereMode) + // this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; + //else + // //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); + // this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; + //this.hasHollow = (this.hollow > 0.001f); + } + + /// + /// Extrudes a profile along a path. + /// + public void Extrude(PathType pathType) + { + bool needEndFaces = false; + + this.coords = new List(); + this.faces = new List(); + + if (this.viewerMode) + { + this.viewerFaces = new List(); + this.calcVertexNormals = true; + } + + if (this.calcVertexNormals) + this.normals = new List(); + + int steps = 1; + + float length = this.pathCutEnd - this.pathCutBegin; + normalsProcessed = false; + + if (this.viewerMode && this.sides == 3) + { + // prisms don't taper well so add some vertical resolution + // other prims may benefit from this but just do prisms for now + if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) + steps = (int)(steps * 4.5 * length); + } + + if (sphereMode) + this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; + else + //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); + this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; + this.hasHollow = (this.hollow > 0.001f); + + float twistBegin = this.twistBegin / 360.0f * twoPi; + float twistEnd = this.twistEnd / 360.0f * twoPi; + float twistTotal = twistEnd - twistBegin; + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number + + float hollow = this.hollow; + + // sanity checks + float initialProfileRot = 0.0f; + if (pathType == PathType.Circular) + { + if (this.sides == 3) + { + initialProfileRot = (float)Math.PI; + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow *= 0.707f; + } + else hollow *= 0.5f; + } + else if (this.sides == 4) + { + initialProfileRot = 0.25f * (float)Math.PI; + if (this.hollowSides != 4) + hollow *= 0.707f; + } + else if (this.sides > 4) + { + initialProfileRot = (float)Math.PI; + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow /= 0.7f; + } + } + } + else + { + if (this.sides == 3) + { + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow *= 0.707f; + } + else hollow *= 0.5f; + } + else if (this.sides == 4) + { + initialProfileRot = 1.25f * (float)Math.PI; + if (this.hollowSides != 4) + hollow *= 0.707f; + } + else if (this.sides == 24 && this.hollowSides == 4) + hollow *= 1.414f; + } + + Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); + this.errorMessage = profile.errorMessage; + + this.numPrimFaces = profile.numPrimFaces; + + //profileOuterFaceNumber = profile.faceNumbers[0]; + //if (!needEndFaces) + // profileOuterFaceNumber--; + //profileOuterFaceNumber = needEndFaces ? 1 : 0; + + + //if (hasHollow) + //{ + // if (needEndFaces) + // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts + 1]; + // else + // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts] - 1; + //} + + + profileOuterFaceNumber = profile.outerFaceNumber; + if (!needEndFaces) + profileOuterFaceNumber--; + + if (hasHollow) + { + profileHollowFaceNumber = profile.hollowFaceNumber; + if (!needEndFaces) + profileHollowFaceNumber--; + } + + int cut1Vert = -1; + int cut2Vert = -1; + if (hasProfileCut) + { + cut1Vert = hasHollow ? profile.coords.Count - 1 : 0; + cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts; + } + + if (initialProfileRot != 0.0f) + { + profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); + if (viewerMode) + profile.MakeFaceUVs(); + } + + Coord lastCutNormal1 = new Coord(); + Coord lastCutNormal2 = new Coord(); + float lastV = 1.0f; + + Path path = new Path(); + path.twistBegin = twistBegin; + path.twistEnd = twistEnd; + path.topShearX = topShearX; + path.topShearY = topShearY; + path.pathCutBegin = pathCutBegin; + path.pathCutEnd = pathCutEnd; + path.dimpleBegin = dimpleBegin; + path.dimpleEnd = dimpleEnd; + path.skew = skew; + path.holeSizeX = holeSizeX; + path.holeSizeY = holeSizeY; + path.taperX = taperX; + path.taperY = taperY; + path.radius = radius; + path.revolutions = revolutions; + path.stepsPerRevolution = stepsPerRevolution; + + path.Create(pathType, steps); + + + if (pathType == PathType.Circular) + { + needEndFaces = false; + if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f) + needEndFaces = true; + else if (this.taperX != 0.0f || this.taperY != 0.0f) + needEndFaces = true; + else if (this.skew != 0.0f) + needEndFaces = true; + else if (twistTotal != 0.0f) + needEndFaces = true; + else if (this.radius != 0.0f) + needEndFaces = true; + } + else needEndFaces = true; + + for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) + { + PathNode node = path.pathNodes[nodeIndex]; + Profile newLayer = profile.Copy(); + newLayer.Scale(node.xScale, node.yScale); + + newLayer.AddRot(node.rotation); + newLayer.AddPos(node.position); + + if (needEndFaces && nodeIndex == 0) + { + newLayer.FlipNormals(); + + // add the top faces to the viewerFaces list here + if (this.viewerMode) + { + Coord faceNormal = newLayer.faceNormal; + ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber); + int numFaces = newLayer.faces.Count; + List faces = newLayer.faces; + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + newViewerFace.v1 = newLayer.coords[face.v1]; + newViewerFace.v2 = newLayer.coords[face.v2]; + newViewerFace.v3 = newLayer.coords[face.v3]; + + newViewerFace.coordIndex1 = face.v1; + newViewerFace.coordIndex2 = face.v2; + newViewerFace.coordIndex3 = face.v3; + + newViewerFace.n1 = faceNormal; + newViewerFace.n2 = faceNormal; + newViewerFace.n3 = faceNormal; + + newViewerFace.uv1 = newLayer.faceUVs[face.v1]; + newViewerFace.uv2 = newLayer.faceUVs[face.v2]; + newViewerFace.uv3 = newLayer.faceUVs[face.v3]; + + this.viewerFaces.Add(newViewerFace); + } + } + } // if (nodeIndex == 0) + + // append this layer + + int coordsLen = this.coords.Count; + newLayer.AddValue2FaceVertexIndices(coordsLen); + + this.coords.AddRange(newLayer.coords); + + if (this.calcVertexNormals) + { + newLayer.AddValue2FaceNormalIndices(this.normals.Count); + this.normals.AddRange(newLayer.vertexNormals); + } + + if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f) + this.faces.AddRange(newLayer.faces); + + // fill faces between layers + + int numVerts = newLayer.coords.Count; + Face newFace = new Face(); + + if (nodeIndex > 0) + { + int startVert = coordsLen + 1; + int endVert = this.coords.Count; + + if (sides < 5 || this.hasProfileCut || this.hasHollow) + startVert--; + + for (int i = startVert; i < endVert; i++) + { + int iNext = i + 1; + if (i == endVert - 1) + iNext = startVert; + + int whichVert = i - startVert; + + newFace.v1 = i; + newFace.v2 = i - numVerts; + newFace.v3 = iNext - numVerts; + this.faces.Add(newFace); + + newFace.v2 = iNext - numVerts; + newFace.v3 = iNext; + this.faces.Add(newFace); + + if (this.viewerMode) + { + // add the side faces to the list of viewerFaces here + + int primFaceNum = profile.faceNumbers[whichVert]; + if (!needEndFaces) + primFaceNum -= 1; + + ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); + ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); + + float u1 = newLayer.us[whichVert]; + float u2 = 1.0f; + if (whichVert < newLayer.us.Count - 1) + u2 = newLayer.us[whichVert + 1]; + + if (whichVert == cut1Vert || whichVert == cut2Vert) + { + u1 = 0.0f; + u2 = 1.0f; + } + else if (sides < 5) + { + if (whichVert < profile.numOuterVerts) + { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled + // to reflect the entire texture width + u1 *= sides; + u2 *= sides; + u2 -= (int)u1; + u1 -= (int)u1; + if (u2 < 0.1f) + u2 = 1.0f; + //this.profileOuterFaceNumber = primFaceNum; + } + else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) + { + u1 *= 2.0f; + u2 *= 2.0f; + //this.profileHollowFaceNumber = primFaceNum; + } + } + + newViewerFace1.uv1.U = u1; + newViewerFace1.uv2.U = u1; + newViewerFace1.uv3.U = u2; + + newViewerFace1.uv1.V = 1.0f - node.percentOfPath; + newViewerFace1.uv2.V = lastV; + newViewerFace1.uv3.V = lastV; + + newViewerFace2.uv1.U = u1; + newViewerFace2.uv2.U = u2; + newViewerFace2.uv3.U = u2; + + newViewerFace2.uv1.V = 1.0f - node.percentOfPath; + newViewerFace2.uv2.V = lastV; + newViewerFace2.uv3.V = 1.0f - node.percentOfPath; + + newViewerFace1.v1 = this.coords[i]; + newViewerFace1.v2 = this.coords[i - numVerts]; + newViewerFace1.v3 = this.coords[iNext - numVerts]; + + newViewerFace2.v1 = this.coords[i]; + newViewerFace2.v2 = this.coords[iNext - numVerts]; + newViewerFace2.v3 = this.coords[iNext]; + + newViewerFace1.coordIndex1 = i; + newViewerFace1.coordIndex2 = i - numVerts; + newViewerFace1.coordIndex3 = iNext - numVerts; + + newViewerFace2.coordIndex1 = i; + newViewerFace2.coordIndex2 = iNext - numVerts; + newViewerFace2.coordIndex3 = iNext; + + // profile cut faces + if (whichVert == cut1Vert) + { + newViewerFace1.n1 = newLayer.cutNormal1; + newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; + + newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; + newViewerFace2.n2 = lastCutNormal1; + } + else if (whichVert == cut2Vert) + { + newViewerFace1.n1 = newLayer.cutNormal2; + newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; + + newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; + newViewerFace2.n2 = lastCutNormal2; + } + + else // outer and hollow faces + { + if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) + { // looks terrible when path is twisted... need vertex normals here + newViewerFace1.CalcSurfaceNormal(); + newViewerFace2.CalcSurfaceNormal(); + } + else + { + newViewerFace1.n1 = this.normals[i]; + newViewerFace1.n2 = this.normals[i - numVerts]; + newViewerFace1.n3 = this.normals[iNext - numVerts]; + + newViewerFace2.n1 = this.normals[i]; + newViewerFace2.n2 = this.normals[iNext - numVerts]; + newViewerFace2.n3 = this.normals[iNext]; + } + } + + this.viewerFaces.Add(newViewerFace1); + this.viewerFaces.Add(newViewerFace2); + + } + } + } + + lastCutNormal1 = newLayer.cutNormal1; + lastCutNormal2 = newLayer.cutNormal2; + lastV = 1.0f - node.percentOfPath; + + if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) + { + // add the top faces to the viewerFaces list here + Coord faceNormal = newLayer.faceNormal; + ViewerFace newViewerFace = new ViewerFace(); + newViewerFace.primFaceNumber = 0; + int numFaces = newLayer.faces.Count; + List faces = newLayer.faces; + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; + newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; + newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; + + newViewerFace.coordIndex1 = face.v1 - coordsLen; + newViewerFace.coordIndex2 = face.v2 - coordsLen; + newViewerFace.coordIndex3 = face.v3 - coordsLen; + + newViewerFace.n1 = faceNormal; + newViewerFace.n2 = faceNormal; + newViewerFace.n3 = faceNormal; + + newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen]; + newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; + newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; + + this.viewerFaces.Add(newViewerFace); + } + } + + + } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) + + } + + + /// + /// DEPRICATED - use Extrude(PathType.Linear) instead + /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. + /// + /// + public void ExtrudeLinear() + { + this.Extrude(PathType.Linear); + } + + + /// + /// DEPRICATED - use Extrude(PathType.Circular) instead + /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. + /// + /// + public void ExtrudeCircular() + { + this.Extrude(PathType.Circular); + } + + + private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3) + { + Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + Coord normal = Coord.Cross(edge1, edge2); + + normal.Normalize(); + + return normal; + } + + private Coord SurfaceNormal(Face face) + { + return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); + } + + /// + /// Calculate the surface normal for a face in the list of faces + /// + /// + /// + public Coord SurfaceNormal(int faceIndex) + { + int numFaces = this.faces.Count; + if (faceIndex < 0 || faceIndex >= numFaces) + throw new Exception("faceIndex out of range"); + + return SurfaceNormal(this.faces[faceIndex]); + } + + /// + /// Duplicates a PrimMesh object. All object properties are copied by value, including lists. + /// + /// + public PrimMesh Copy() + { + PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides); + copy.twistBegin = this.twistBegin; + copy.twistEnd = this.twistEnd; + copy.topShearX = this.topShearX; + copy.topShearY = this.topShearY; + copy.pathCutBegin = this.pathCutBegin; + copy.pathCutEnd = this.pathCutEnd; + copy.dimpleBegin = this.dimpleBegin; + copy.dimpleEnd = this.dimpleEnd; + copy.skew = this.skew; + copy.holeSizeX = this.holeSizeX; + copy.holeSizeY = this.holeSizeY; + copy.taperX = this.taperX; + copy.taperY = this.taperY; + copy.radius = this.radius; + copy.revolutions = this.revolutions; + copy.stepsPerRevolution = this.stepsPerRevolution; + copy.calcVertexNormals = this.calcVertexNormals; + copy.normalsProcessed = this.normalsProcessed; + copy.viewerMode = this.viewerMode; + copy.numPrimFaces = this.numPrimFaces; + copy.errorMessage = this.errorMessage; + + copy.coords = new List(this.coords); + copy.faces = new List(this.faces); + copy.viewerFaces = new List(this.viewerFaces); + copy.normals = new List(this.normals); + + return copy; + } + + /// + /// Calculate surface normals for all of the faces in the list of faces in this mesh + /// + public void CalcNormals() + { + if (normalsProcessed) + return; + + normalsProcessed = true; + + int numFaces = faces.Count; + + if (!this.calcVertexNormals) + this.normals = new List(); + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + + this.normals.Add(SurfaceNormal(i).Normalize()); + + int normIndex = normals.Count - 1; + face.n1 = normIndex; + face.n2 = normIndex; + face.n3 = normIndex; + + this.faces[i] = face; + } + } + + /// + /// Adds a value to each XYZ vertex coordinate in the mesh + /// + /// + /// + /// + public void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.AddPos(x, y, z); + this.viewerFaces[i] = v; + } + } + } + + /// + /// Rotates the mesh + /// + /// + public void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + if (this.normals != null) + { + int numNormals = this.normals.Count; + for (i = 0; i < numNormals; i++) + this.normals[i] *= q; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= q; + v.v2 *= q; + v.v3 *= q; + + v.n1 *= q; + v.n2 *= q; + v.n3 *= q; + this.viewerFaces[i] = v; + } + } + } + +#if VERTEX_INDEXER + public VertexIndexer GetVertexIndexer() + { + if (this.viewerMode && this.viewerFaces.Count > 0) + return new VertexIndexer(this); + return null; + } +#endif + + /// + /// Scales the mesh + /// + /// + /// + /// + public void Scale(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + //Coord vert; + + Coord m = new Coord(x, y, z); + for (i = 0; i < numVerts; i++) + this.coords[i] *= m; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= m; + v.v2 *= m; + v.v3 *= m; + this.viewerFaces[i] = v; + } + + } + + } + + /// + /// Dumps the mesh to a Blender compatible "Raw" format file + /// + /// + /// + /// + public void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } +} From eb6d63ab8ee1c37b10fb885e4f0c672af90ce1ec Mon Sep 17 00:00:00 2001 From: dahlia Date: Thu, 6 May 2010 23:02:24 -0700 Subject: [PATCH 097/260] improve handling of undersize sculpt textures --- OpenSim/Region/Physics/Meshing/SculptMap.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/Physics/Meshing/SculptMap.cs b/OpenSim/Region/Physics/Meshing/SculptMap.cs index 4d3f82b22b..4906cf6a33 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMap.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMap.cs @@ -60,21 +60,23 @@ namespace PrimMesher int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image + bool needsScaling = false; + width = bmW; height = bmH; while (width * height > numLodPixels) { width >>= 1; height >>= 1; + needsScaling = true; } - width >>= 1; - height >>= 1; + try { - if (!(bmW == width * 2 && bmH == height * 2)) - bm = ScaleImage(bm, width * 2, height * 2, + if (needsScaling) + bm = ScaleImage(bm, width, height, System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor); } @@ -83,6 +85,11 @@ namespace PrimMesher throw new Exception("Exception in ScaleImage(): e: " + e.ToString()); } + if (width * height > lod * lod) + { + width >>= 1; + height >>= 1; + } int numBytes = (width + 1) * (height + 1); redBytes = new byte[numBytes]; From a58859a0d4206c194c9c56212218e2cafc2cc373 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 7 May 2010 21:29:56 -0700 Subject: [PATCH 098/260] GridUserService in place. Replaces the contrived concept of storing user's home and position info in the presence service. WARNING: I violated a taboo by deleting 2 migration files and simplifying the original table creation for Presence. This should not cause any problems to anyone, though. Things will work with the new simplified table, as well as with the previous contrived one. If there are any problems, solving them is as easy as dropping the presence table and deleting its row in the migrations table. The presence info only exists during a user's session anyway. BTW, the Meshing files want to be committed too -- EOFs. --- .../RemoteController/RemoteAdminPlugin.cs | 2 +- OpenSim/Data/IGridUserData.cs | 9 +- OpenSim/Data/IPresenceData.cs | 4 +- OpenSim/Data/MSSQL/MSSQLGridUserData.cs | 8 +- OpenSim/Data/MSSQL/MSSQLPresenceData.cs | 72 +- OpenSim/Data/MySQL/MySQLGridUserData.cs | 9 +- OpenSim/Data/MySQL/MySQLPresenceData.cs | 66 +- OpenSim/Data/MySQL/Resources/001_Presence.sql | 10 +- OpenSim/Data/MySQL/Resources/002_Presence.sql | 7 - OpenSim/Data/MySQL/Resources/003_Presence.sql | 6 - OpenSim/Data/Null/NullPresenceData.cs | 62 +- .../EntityTransfer/EntityTransferModule.cs | 9 +- .../EntityTransfer/HGEntityTransferModule.cs | 9 +- .../Resources/CoreModulePlugin.addin.xml | 4 +- .../GridUser/LocalGridUserServiceConnector.cs | 71 +- .../Presence/LocalPresenceServiceConnector.cs | 13 +- .../Presence/PresenceDetector.cs | 12 +- .../RemotePresenceServiceConnector.cs | 13 +- .../Presence/Tests/PresenceConnectorsTests.cs | 8 +- OpenSim/Region/Framework/Scenes/Scene.cs | 21 +- OpenSim/Region/Physics/Meshing/PrimMesher.cs | 4568 ++++++++--------- OpenSim/Region/Physics/Meshing/SculptMap.cs | 352 +- OpenSim/Region/Physics/Meshing/SculptMesh.cs | 1282 ++--- .../Presence/PresenceServerPostHandler.cs | 45 +- .../GridUser/GridUserServiceConnector.cs | 196 +- .../Presence/PresenceServiceConnector.cs | 54 +- .../SimianPresenceServiceConnector.cs | 88 +- .../HypergridService/UserAgentService.cs | 21 +- .../Services/Interfaces/IGridUserService.cs | 42 +- .../Services/Interfaces/IPresenceService.cs | 24 +- .../LLLoginService/LLLoginResponse.cs | 4 +- .../Services/LLLoginService/LLLoginService.cs | 63 +- .../PresenceService/PresenceService.cs | 79 +- .../UserAccountService/GridUserService.cs | 95 +- .../UserAccountService/UserAccountService.cs | 10 +- .../Tests/Clients/Presence/PresenceClient.cs | 19 +- bin/Robust.HG.ini.example | 9 +- bin/Robust.ini.example | 7 +- bin/config-include/Grid.ini | 33 +- bin/config-include/GridCommon.ini.example | 6 + bin/config-include/GridHypergrid.ini | 34 +- bin/config-include/Standalone.ini | 8 +- bin/config-include/StandaloneHypergrid.ini | 65 +- .../storage/SQLiteStandalone.ini | 3 + 44 files changed, 3798 insertions(+), 3724 deletions(-) delete mode 100644 OpenSim/Data/MySQL/Resources/002_Presence.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_Presence.sql diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index 5a23c90185..13f9c9f04f 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -1611,7 +1611,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController GridRegion home = m_app.SceneManager.CurrentOrFirstScene.GridService.GetRegionByPosition(scopeID, (int)(regX * Constants.RegionSize), (int)(regY * Constants.RegionSize)); if (home != null) - m_app.SceneManager.CurrentOrFirstScene.PresenceService.SetHomeLocation(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + m_app.SceneManager.CurrentOrFirstScene.GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); } else { diff --git a/OpenSim/Data/IGridUserData.cs b/OpenSim/Data/IGridUserData.cs index bd7a435391..e15a1f88a5 100644 --- a/OpenSim/Data/IGridUserData.cs +++ b/OpenSim/Data/IGridUserData.cs @@ -37,6 +37,11 @@ namespace OpenSim.Data { public string UserID; public Dictionary Data; + + public GridUserData() + { + Data = new Dictionary(); + } } /// @@ -44,7 +49,7 @@ namespace OpenSim.Data /// public interface IGridUserData { - GridUserData GetGridUserData(string userID); - bool StoreGridUserData(GridUserData data); + GridUserData Get(string userID); + bool Store(GridUserData data); } } \ No newline at end of file diff --git a/OpenSim/Data/IPresenceData.cs b/OpenSim/Data/IPresenceData.cs index 71d0e31a1f..b871f5636c 100644 --- a/OpenSim/Data/IPresenceData.cs +++ b/OpenSim/Data/IPresenceData.cs @@ -50,10 +50,8 @@ namespace OpenSim.Data PresenceData Get(UUID sessionID); void LogoutRegionAgents(UUID regionID); - bool ReportAgent(UUID sessionID, UUID regionID, string position, string lookAt); - bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt); + bool ReportAgent(UUID sessionID, UUID regionID); PresenceData[] Get(string field, string data); - void Prune(string userID); bool Delete(string field, string val); } } diff --git a/OpenSim/Data/MSSQL/MSSQLGridUserData.cs b/OpenSim/Data/MSSQL/MSSQLGridUserData.cs index 9993720a04..1870273616 100644 --- a/OpenSim/Data/MSSQL/MSSQLGridUserData.cs +++ b/OpenSim/Data/MSSQL/MSSQLGridUserData.cs @@ -46,11 +46,11 @@ namespace OpenSim.Data.MSSQL private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public MSSQLGridUserData(string connectionString, string realm) : - base(connectionString, realm, "UserGrid") + base(connectionString, realm, "GridUserStore") { } - public GridUserData GetGridUserData(string userID) + public GridUserData Get(string userID) { GridUserData[] ret = Get("UserID", userID); @@ -60,9 +60,5 @@ namespace OpenSim.Data.MSSQL return ret[0]; } - public bool StoreGridUserData(GridUserData data) - { - return Store(data); - } } } diff --git a/OpenSim/Data/MSSQL/MSSQLPresenceData.cs b/OpenSim/Data/MSSQL/MSSQLPresenceData.cs index 5a4ad3a358..e7b3d9c367 100644 --- a/OpenSim/Data/MSSQL/MSSQLPresenceData.cs +++ b/OpenSim/Data/MSSQL/MSSQLPresenceData.cs @@ -67,7 +67,7 @@ namespace OpenSim.Data.MSSQL using (SqlCommand cmd = new SqlCommand()) { - cmd.CommandText = String.Format("UPDATE {0} SET Online='false' WHERE [RegionID]=@RegionID", m_Realm); + cmd.CommandText = String.Format("DELETE FROM {0} WHERE [RegionID]=@RegionID", m_Realm); cmd.Parameters.Add(m_database.CreateParameter("@RegionID", regionID.ToString())); cmd.Connection = conn; @@ -76,8 +76,7 @@ namespace OpenSim.Data.MSSQL } } - public bool ReportAgent(UUID sessionID, UUID regionID, string position, - string lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { PresenceData[] pd = Get("SessionID", sessionID.ToString()); if (pd.Length == 0) @@ -88,16 +87,11 @@ namespace OpenSim.Data.MSSQL { cmd.CommandText = String.Format(@"UPDATE {0} SET - [RegionID] = @RegionID, - [Position] = @Position, - [LookAt] = @LookAt, - [Online] = 'true' + [RegionID] = @RegionID WHERE [SessionID] = @SessionID", m_Realm); cmd.Parameters.Add(m_database.CreateParameter("@SessionID", sessionID.ToString())); cmd.Parameters.Add(m_database.CreateParameter("@RegionID", regionID.ToString())); - cmd.Parameters.Add(m_database.CreateParameter("@Position", position.ToString())); - cmd.Parameters.Add(m_database.CreateParameter("@LookAt", lookAt.ToString())); cmd.Connection = conn; conn.Open(); if (cmd.ExecuteNonQuery() == 0) @@ -106,65 +100,5 @@ namespace OpenSim.Data.MSSQL return true; } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - PresenceData[] pd = Get("UserID", userID); - if (pd.Length == 0) - return false; - - using (SqlConnection conn = new SqlConnection(m_ConnectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - - cmd.CommandText = String.Format(@"UPDATE {0} SET - [HomeRegionID] = @HomeRegionID, - [HomePosition] = @HomePosition, - [HomeLookAt] = @HomeLookAt - WHERE [UserID] = @UserID", m_Realm); - - cmd.Parameters.Add(m_database.CreateParameter("@UserID", userID)); - cmd.Parameters.Add(m_database.CreateParameter("@HomeRegionID", regionID.ToString())); - cmd.Parameters.Add(m_database.CreateParameter("@HomePosition", position)); - cmd.Parameters.Add(m_database.CreateParameter("@HomeLookAt", lookAt)); - cmd.Connection = conn; - conn.Open(); - if (cmd.ExecuteNonQuery() == 0) - return false; - } - return true; - } - - public void Prune(string userID) - { - using (SqlConnection conn = new SqlConnection(m_ConnectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.CommandText = String.Format("SELECT * from {0} WHERE [UserID] = @UserID", m_Realm); - - cmd.Parameters.Add(m_database.CreateParameter("@UserID", userID)); - cmd.Connection = conn; - conn.Open(); - - using (SqlDataReader reader = cmd.ExecuteReader()) - { - List deleteSessions = new List(); - int online = 0; - - while (reader.Read()) - { - if (bool.Parse(reader["Online"].ToString())) - online++; - else - deleteSessions.Add(new UUID(reader["SessionID"].ToString())); - } - - if (online == 0 && deleteSessions.Count > 0) - deleteSessions.RemoveAt(0); - - foreach (UUID s in deleteSessions) - Delete("SessionID", s.ToString()); - } - } - } } } diff --git a/OpenSim/Data/MySQL/MySQLGridUserData.cs b/OpenSim/Data/MySQL/MySQLGridUserData.cs index df29ecdc2d..a9ce94d33c 100644 --- a/OpenSim/Data/MySQL/MySQLGridUserData.cs +++ b/OpenSim/Data/MySQL/MySQLGridUserData.cs @@ -44,9 +44,9 @@ namespace OpenSim.Data.MySQL { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public MySQLGridUserData(string connectionString, string realm) : base(connectionString, realm, "UserGrid") {} + public MySQLGridUserData(string connectionString, string realm) : base(connectionString, realm, "GridUserStore") {} - public GridUserData GetGridUserData(string userID) + public GridUserData Get(string userID) { GridUserData[] ret = Get("UserID", userID); @@ -56,9 +56,6 @@ namespace OpenSim.Data.MySQL return ret[0]; } - public bool StoreGridUserData(GridUserData data) - { - return Store(data); - } + } } \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLPresenceData.cs b/OpenSim/Data/MySQL/MySQLPresenceData.cs index 143dbe38cb..71caa1aa2a 100644 --- a/OpenSim/Data/MySQL/MySQLPresenceData.cs +++ b/OpenSim/Data/MySQL/MySQLPresenceData.cs @@ -65,15 +65,14 @@ namespace OpenSim.Data.MySQL { MySqlCommand cmd = new MySqlCommand(); - cmd.CommandText = String.Format("update {0} set Online='false' where `RegionID`=?RegionID", m_Realm); + cmd.CommandText = String.Format("delete from {0} where `RegionID`=?RegionID", m_Realm); cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); ExecuteNonQuery(cmd); } - public bool ReportAgent(UUID sessionID, UUID regionID, string position, - string lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { PresenceData[] pd = Get("SessionID", sessionID.ToString()); if (pd.Length == 0) @@ -81,12 +80,10 @@ namespace OpenSim.Data.MySQL MySqlCommand cmd = new MySqlCommand(); - cmd.CommandText = String.Format("update {0} set RegionID=?RegionID, Position=?Position, LookAt=?LookAt, Online='true' where `SessionID`=?SessionID", m_Realm); + cmd.CommandText = String.Format("update {0} set RegionID=?RegionID where `SessionID`=?SessionID", m_Realm); cmd.Parameters.AddWithValue("?SessionID", sessionID.ToString()); cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); - cmd.Parameters.AddWithValue("?Position", position.ToString()); - cmd.Parameters.AddWithValue("?LookAt", lookAt.ToString()); if (ExecuteNonQuery(cmd) == 0) return false; @@ -94,62 +91,5 @@ namespace OpenSim.Data.MySQL return true; } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - PresenceData[] pd = Get("UserID", userID); - if (pd.Length == 0) - return false; - - MySqlCommand cmd = new MySqlCommand(); - - cmd.CommandText = String.Format("update {0} set HomeRegionID=?HomeRegionID, HomePosition=?HomePosition, HomeLookAt=?HomeLookAt where UserID=?UserID", m_Realm); - - cmd.Parameters.AddWithValue("?UserID", userID); - cmd.Parameters.AddWithValue("?HomeRegionID", regionID.ToString()); - cmd.Parameters.AddWithValue("?HomePosition", position); - cmd.Parameters.AddWithValue("?HomeLookAt", lookAt); - - if (ExecuteNonQuery(cmd) == 0) - return false; - - return true; - } - - public void Prune(string userID) - { - MySqlCommand cmd = new MySqlCommand(); - - cmd.CommandText = String.Format("select * from {0} where UserID=?UserID", m_Realm); - - cmd.Parameters.AddWithValue("?UserID", userID); - - using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) - { - dbcon.Open(); - - cmd.Connection = dbcon; - - using (IDataReader reader = cmd.ExecuteReader()) - { - List deleteSessions = new List(); - int online = 0; - - while (reader.Read()) - { - if (bool.Parse(reader["Online"].ToString())) - online++; - else - deleteSessions.Add(new UUID(reader["SessionID"].ToString())); - } - - // Leave one session behind so that we can pick up details such as home location - if (online == 0 && deleteSessions.Count > 0) - deleteSessions.RemoveAt(0); - - foreach (UUID s in deleteSessions) - Delete("SessionID", s.ToString()); - } - } - } } } diff --git a/OpenSim/Data/MySQL/Resources/001_Presence.sql b/OpenSim/Data/MySQL/Resources/001_Presence.sql index b8abaf7dc7..84fa05794c 100644 --- a/OpenSim/Data/MySQL/Resources/001_Presence.sql +++ b/OpenSim/Data/MySQL/Resources/001_Presence.sql @@ -4,12 +4,10 @@ CREATE TABLE `Presence` ( `UserID` VARCHAR(255) NOT NULL, `RegionID` CHAR(36) NOT NULL, `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', - `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', - `Online` CHAR(5) NOT NULL DEFAULT 'false', - `Login` CHAR(16) NOT NULL DEFAULT '0', - `Logout` CHAR(16) NOT NULL DEFAULT '0', - `Position` CHAR(64) NOT NULL DEFAULT '<0,0,0>', - `LookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>' + `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' ) ENGINE=InnoDB; +CREATE UNIQUE INDEX SessionID ON Presence(SessionID); +CREATE INDEX UserID ON Presence(UserID); + COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_Presence.sql b/OpenSim/Data/MySQL/Resources/002_Presence.sql deleted file mode 100644 index e65f105477..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_Presence.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE Presence ADD COLUMN `HomeRegionID` CHAR(36) NOT NULL; -ALTER TABLE Presence ADD COLUMN `HomePosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; -ALTER TABLE Presence ADD COLUMN `HomeLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_Presence.sql b/OpenSim/Data/MySQL/Resources/003_Presence.sql deleted file mode 100644 index 0efefa81bf..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_Presence.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -CREATE UNIQUE INDEX SessionID ON Presence(SessionID); -CREATE INDEX UserID ON Presence(UserID); - -COMMIT; diff --git a/OpenSim/Data/Null/NullPresenceData.cs b/OpenSim/Data/Null/NullPresenceData.cs index b98b5c92b5..91f1cc561e 100644 --- a/OpenSim/Data/Null/NullPresenceData.cs +++ b/OpenSim/Data/Null/NullPresenceData.cs @@ -96,45 +96,20 @@ namespace OpenSim.Data.Null m_presenceData.Remove(u); } - public bool ReportAgent(UUID sessionID, UUID regionID, string position, string lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { if (Instance != this) - return Instance.ReportAgent(sessionID, regionID, position, lookAt); + return Instance.ReportAgent(sessionID, regionID); if (m_presenceData.ContainsKey(sessionID)) { m_presenceData[sessionID].RegionID = regionID; - m_presenceData[sessionID].Data["Position"] = position; - m_presenceData[sessionID].Data["LookAt"] = lookAt; return true; } return false; } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - if (Instance != this) - return Instance.SetHomeLocation(userID, regionID, position, lookAt); - - bool foundone = false; - foreach (PresenceData p in m_presenceData.Values) - { - if (p.UserID == userID) - { -// m_log.DebugFormat( -// "[NULL PRESENCE DATA]: Setting home location {0} {1} {2} for {3}", -// regionID, position, lookAt, p.UserID); - - p.Data["HomeRegionID"] = regionID.ToString(); - p.Data["HomePosition"] = position.ToString(); - p.Data["HomeLookAt"] = lookAt.ToString(); - foundone = true; - } - } - - return foundone; - } public PresenceData[] Get(string field, string data) { @@ -193,39 +168,6 @@ namespace OpenSim.Data.Null return presences.ToArray(); } - public void Prune(string userID) - { - if (Instance != this) - { - Instance.Prune(userID); - return; - } - -// m_log.DebugFormat("[NULL PRESENCE DATA]: Prune called for {0}", userID); - - List deleteSessions = new List(); - int online = 0; - - foreach (KeyValuePair kvp in m_presenceData) - { -// m_log.DebugFormat("Online: {0}", kvp.Value.Data["Online"]); - - bool on = false; - if (bool.TryParse(kvp.Value.Data["Online"], out on) && on) - online++; - else - deleteSessions.Add(kvp.Key); - } - -// m_log.DebugFormat("[NULL PRESENCE DATA]: online [{0}], deleteSession.Count [{1}]", online, deleteSessions.Count); - - // Leave one session behind so that we can pick up details such as home location - if (online == 0 && deleteSessions.Count > 0) - deleteSessions.RemoveAt(0); - - foreach (UUID s in deleteSessions) - m_presenceData.Remove(s); - } public bool Delete(string field, string data) { diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index f2b03e4177..ef37f63622 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -526,11 +526,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer { m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Request to teleport {0} {1} home", client.FirstName, client.LastName); - OpenSim.Services.Interfaces.PresenceInfo pinfo = m_aScene.PresenceService.GetAgent(client.SessionId); + //OpenSim.Services.Interfaces.PresenceInfo pinfo = m_aScene.PresenceService.GetAgent(client.SessionId); + GridUserInfo uinfo = m_aScene.GridUserService.GetGridUserInfo(client.AgentId.ToString()); - if (pinfo != null) + if (uinfo != null) { - GridRegion regionInfo = m_aScene.GridService.GetRegionByUUID(UUID.Zero, pinfo.HomeRegionID); + GridRegion regionInfo = m_aScene.GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); if (regionInfo == null) { // can't find the Home region: Tell viewer and abort @@ -539,7 +540,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer } // a little eekie that this goes back to Scene and with a forced cast, will fix that at some point... ((Scene)(client.Scene)).RequestTeleportLocation( - client, regionInfo.RegionHandle, pinfo.HomePosition, pinfo.HomeLookAt, + client, regionInfo.RegionHandle, uinfo.HomePosition, uinfo.HomeLookAt, (uint)(Constants.TeleportFlags.SetLastToTarget | Constants.TeleportFlags.ViaHome)); } } diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index 28593fc8d8..137dfec13b 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs @@ -154,7 +154,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer bool success = connector.LoginAgentToGrid(agentCircuit, reg, finalDestination, out reason); if (success) // Log them out of this grid - m_aScene.PresenceService.LogoutAgent(agentCircuit.SessionID, sp.AbsolutePosition, sp.Lookat); + m_aScene.PresenceService.LogoutAgent(agentCircuit.SessionID); return success; } @@ -238,6 +238,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer { if (obj.IsLoggingOut) { + object sp = null; + if (obj.Scene.TryGetScenePresence(obj.AgentId, out sp)) + { + if (((ScenePresence)sp).IsChildAgent) + return; + } + AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml index 0a5ff3f982..ee070756fd 100644 --- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml +++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml @@ -59,9 +59,11 @@ - + + + diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs index d5fae232d6..d914a576e1 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs @@ -41,13 +41,19 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser { public class LocalGridUserServicesConnector : ISharedRegionModule, IGridUserService { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); - private IGridUserService m_service; + private IGridUserService m_GridUserService; + + private ActivityDetector m_ActivityDetector; private bool m_Enabled = false; - public Type ReplaceableInterface + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } @@ -68,7 +74,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser IConfig userConfig = source.Configs["GridUserService"]; if (userConfig == null) { - m_log.Error("[LOCAL GRID USER SERVICE CONNECTOR]: GridUserService missing from ini files"); + m_log.Error("[LOCAL GRID USER SERVICE CONNECTOR]: GridUserService missing from OpenSim.ini"); return; } @@ -81,15 +87,20 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser } Object[] args = new Object[] { source }; - m_service = ServerUtils.LoadPlugin(serviceDll, args); + m_GridUserService = ServerUtils.LoadPlugin(serviceDll, args); - if (m_service == null) + if (m_GridUserService == null) { - m_log.Error("[LOCAL GRID USER SERVICE CONNECTOR]: Can't load GridUser service"); + m_log.ErrorFormat( + "[LOCAL GRID USER SERVICE CONNECTOR]: Cannot load user account service specified as {0}", serviceDll); return; } + + m_ActivityDetector = new ActivityDetector(this); + m_Enabled = true; - m_log.Info("[LOCAL GRID USER SERVICE CONNECTOR]: Local GridUser connector enabled"); + + m_log.Info("[LOCAL GRID USER SERVICE CONNECTOR]: Local grid user connector enabled"); } } } @@ -111,29 +122,57 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser if (!m_Enabled) return; - scene.RegisterModuleInterface(m_service); + scene.RegisterModuleInterface(m_GridUserService); + m_ActivityDetector.AddRegion(scene); } public void RemoveRegion(Scene scene) { if (!m_Enabled) return; + + scene.UnregisterModuleInterface(this); + m_ActivityDetector.RemoveRegion(scene); } public void RegionLoaded(Scene scene) { if (!m_Enabled) return; + + m_log.InfoFormat("[LOCAL GRID USER SERVICE CONNECTOR]: Enabled local grid user for region {0}", scene.RegionInfo.RegionName); + } + + #endregion + + #region IGridUserService + + public GridUserInfo LoggedIn(string userID) + { + return m_GridUserService.LoggedIn(userID); + } + + public bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + return m_GridUserService.LoggedOut(userID, regionID, lastPosition, lastLookAt); + } + + public bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt) + { + return m_GridUserService.SetHome(userID, homeID, homePosition, homeLookAt); + } + + public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + return m_GridUserService.SetLastPosition(userID, regionID, lastPosition, lastLookAt); } public GridUserInfo GetGridUserInfo(string userID) { - return m_service.GetGridUserInfo(userID); - } - - public bool StoreGridUserInfo(GridUserInfo info) - { - return m_service.StoreGridUserInfo(info); + return m_GridUserService.GetGridUserInfo(userID); } + + #endregion + } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/LocalPresenceServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/LocalPresenceServiceConnector.cs index c402a3face..49dd633be4 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/LocalPresenceServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/LocalPresenceServiceConnector.cs @@ -167,9 +167,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence return false; } - public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookat) + public bool LogoutAgent(UUID sessionID) { - return m_PresenceService.LogoutAgent(sessionID, position, lookat); + return m_PresenceService.LogoutAgent(sessionID); } @@ -178,9 +178,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence return m_PresenceService.LogoutRegionAgents(regionID); } - public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { - return m_PresenceService.ReportAgent(sessionID, regionID, position, lookAt); + return m_PresenceService.ReportAgent(sessionID, regionID); } public PresenceInfo GetAgent(UUID sessionID) @@ -193,11 +193,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence return m_PresenceService.GetAgents(userIDs); } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - return m_PresenceService.SetHomeLocation(userID, regionID, position, lookAt); - } - #endregion } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs index 7a75a89f4c..62b8278b22 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs @@ -72,7 +72,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence public void OnMakeRootAgent(ScenePresence sp) { m_log.DebugFormat("[PRESENCE DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); - m_PresenceService.ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + m_PresenceService.ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID); } public void OnNewClient(IClientAPI client) @@ -85,19 +85,17 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence if (client.IsLoggingOut) { object sp = null; - Vector3 position = new Vector3(128, 128, 0); - Vector3 lookat = new Vector3(0, 1, 0); - if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) { if (sp is ScenePresence) { - position = ((ScenePresence)sp).AbsolutePosition; - lookat = ((ScenePresence)sp).Lookat; + if (((ScenePresence)sp).IsChildAgent) + return; } } - m_PresenceService.LogoutAgent(client.SessionId, position, lookat); + m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); + m_PresenceService.LogoutAgent(client.SessionId); } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs index 5f3666e323..bf4e9abf0d 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/RemotePresenceServiceConnector.cs @@ -127,9 +127,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence return false; } - public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookat) + public bool LogoutAgent(UUID sessionID) { - return m_RemoteConnector.LogoutAgent(sessionID, position, lookat); + return m_RemoteConnector.LogoutAgent(sessionID); } @@ -138,9 +138,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence return m_RemoteConnector.LogoutRegionAgents(regionID); } - public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { - return m_RemoteConnector.ReportAgent(sessionID, regionID, position, lookAt); + return m_RemoteConnector.ReportAgent(sessionID, regionID); } public PresenceInfo GetAgent(UUID sessionID) @@ -153,11 +153,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence return m_RemoteConnector.GetAgents(userIDs); } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - return m_RemoteConnector.SetHomeLocation(userID, regionID, position, lookAt); - } - #endregion } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs index 63a28fc255..e5ded5b5c0 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs @@ -93,24 +93,24 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests Assert.IsTrue(result.Online, "Agent just logged in but is offline"); UUID region1 = UUID.Random(); - bool r = m_LocalConnector.ReportAgent(session1, region1, Vector3.Zero, Vector3.Zero); + bool r = m_LocalConnector.ReportAgent(session1, region1); Assert.IsTrue(r, "First ReportAgent returned false"); result = m_LocalConnector.GetAgent(session1); Assert.That(result.RegionID, Is.EqualTo(region1), "Agent is not in the right region (region1)"); UUID region2 = UUID.Random(); - r = m_LocalConnector.ReportAgent(session1, region2, Vector3.Zero, Vector3.Zero); + r = m_LocalConnector.ReportAgent(session1, region2); Assert.IsTrue(r, "Second ReportAgent returned false"); result = m_LocalConnector.GetAgent(session1); Assert.That(result.RegionID, Is.EqualTo(region2), "Agent is not in the right region (region2)"); - r = m_LocalConnector.LogoutAgent(session1, Vector3.Zero, Vector3.UnitY); + r = m_LocalConnector.LogoutAgent(session1); Assert.IsTrue(r, "LogoutAgent returned false"); result = m_LocalConnector.GetAgent(session1); Assert.IsNotNull(result, "Agent session disappeared from storage after logout"); Assert.IsFalse(result.Online, "Agent is reported to be Online after logout"); - r = m_LocalConnector.ReportAgent(session1, region1, Vector3.Zero, Vector3.Zero); + r = m_LocalConnector.ReportAgent(session1, region1); Assert.IsFalse(r, "ReportAgent of non-logged in user returned true"); } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 1a46837d60..c0fa7b4289 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -304,7 +304,18 @@ namespace OpenSim.Region.Framework.Scenes return m_AvatarService; } } - + + protected IGridUserService m_GridUserService; + public IGridUserService GridUserService + { + get + { + if (m_GridUserService == null) + m_GridUserService = RequestModuleInterface(); + return m_GridUserService; + } + } + protected IXMLRPC m_xmlrpcModule; protected IWorldComm m_worldCommModule; public IAttachmentsModule AttachmentsModule { get; set; } @@ -1306,8 +1317,8 @@ namespace OpenSim.Region.Framework.Scenes if (defaultRegions != null && defaultRegions.Count >= 1) home = defaultRegions[0]; - if (PresenceService != null && home != null) - PresenceService.SetHomeLocation(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + if (GridUserService != null && home != null) + GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); else m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to set home for account {0} {1}.", first, last); @@ -3095,7 +3106,7 @@ namespace OpenSim.Region.Framework.Scenes /// public virtual void SetHomeRezPoint(IClientAPI remoteClient, ulong regionHandle, Vector3 position, Vector3 lookAt, uint flags) { - if (PresenceService.SetHomeLocation(remoteClient.AgentId.ToString(), RegionInfo.RegionID, position, lookAt)) + if (GridUserService != null && GridUserService.SetHome(remoteClient.AgentId.ToString(), RegionInfo.RegionID, position, lookAt)) // FUBAR ALERT: this needs to be "Home position set." so the viewer saves a home-screenshot. m_dialogModule.SendAlertToUser(remoteClient, "Home position set."); else @@ -3543,7 +3554,7 @@ namespace OpenSim.Region.Framework.Scenes OpenSim.Services.Interfaces.PresenceInfo pinfo = presence.GetAgent(agent.SessionID); - if (pinfo == null || (pinfo != null && pinfo.Online == false)) + if (pinfo == null) { reason = String.Format("Failed to verify user {0} {1}, access denied to region {2}.", agent.firstname, agent.lastname, RegionInfo.RegionName); return false; diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index b4e101a445..53022ad6b4 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs @@ -1,2284 +1,2284 @@ -/* - * Copyright (c) Contributors - * 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 OpenSimulator 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.Text; -using System.IO; - -namespace PrimMesher -{ - public struct Quat - { - /// X value - public float X; - /// Y value - public float Y; - /// Z value - public float Z; - /// W value - public float W; - - public Quat(float x, float y, float z, float w) - { - X = x; - Y = y; - Z = z; - W = w; - } - - public Quat(Coord axis, float angle) - { - axis = axis.Normalize(); - - angle *= 0.5f; - float c = (float)Math.Cos(angle); - float s = (float)Math.Sin(angle); - - X = axis.X * s; - Y = axis.Y * s; - Z = axis.Z * s; - W = c; - - Normalize(); - } - - public float Length() - { - return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); - } - - public Quat Normalize() - { - const float MAG_THRESHOLD = 0.0000001f; - float mag = Length(); - - // Catch very small rounding errors when normalizing - if (mag > MAG_THRESHOLD) - { - float oomag = 1f / mag; - X *= oomag; - Y *= oomag; - Z *= oomag; - W *= oomag; - } - else - { - X = 0f; - Y = 0f; - Z = 0f; - W = 1f; - } - - return this; - } - - public static Quat operator *(Quat q1, Quat q2) - { - float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y; - float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X; - float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W; - float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z; - return new Quat(x, y, z, w); - } - - public override string ToString() - { - return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">"; - } - } - - public struct Coord - { - public float X; - public float Y; - public float Z; - - public Coord(float x, float y, float z) - { - this.X = x; - this.Y = y; - this.Z = z; - } - - public float Length() - { - return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); - } - - public Coord Invert() - { - this.X = -this.X; - this.Y = -this.Y; - this.Z = -this.Z; - - return this; - } - - public Coord Normalize() - { - const float MAG_THRESHOLD = 0.0000001f; - float mag = Length(); - - // Catch very small rounding errors when normalizing - if (mag > MAG_THRESHOLD) - { - float oomag = 1.0f / mag; - this.X *= oomag; - this.Y *= oomag; - this.Z *= oomag; - } - else - { - this.X = 0.0f; - this.Y = 0.0f; - this.Z = 0.0f; - } - - return this; - } - - public override string ToString() - { - return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString(); - } - - public static Coord Cross(Coord c1, Coord c2) - { - return new Coord( - c1.Y * c2.Z - c2.Y * c1.Z, - c1.Z * c2.X - c2.Z * c1.X, - c1.X * c2.Y - c2.X * c1.Y - ); - } - - public static Coord operator +(Coord v, Coord a) - { - return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z); - } - - public static Coord operator *(Coord v, Coord m) - { - return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z); - } - - public static Coord operator *(Coord v, Quat q) - { - // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/ - - Coord c2 = new Coord(0.0f, 0.0f, 0.0f); - - c2.X = q.W * q.W * v.X + - 2f * q.Y * q.W * v.Z - - 2f * q.Z * q.W * v.Y + - q.X * q.X * v.X + - 2f * q.Y * q.X * v.Y + - 2f * q.Z * q.X * v.Z - - q.Z * q.Z * v.X - - q.Y * q.Y * v.X; - - c2.Y = - 2f * q.X * q.Y * v.X + - q.Y * q.Y * v.Y + - 2f * q.Z * q.Y * v.Z + - 2f * q.W * q.Z * v.X - - q.Z * q.Z * v.Y + - q.W * q.W * v.Y - - 2f * q.X * q.W * v.Z - - q.X * q.X * v.Y; - - c2.Z = - 2f * q.X * q.Z * v.X + - 2f * q.Y * q.Z * v.Y + - q.Z * q.Z * v.Z - - 2f * q.W * q.Y * v.X - - q.Y * q.Y * v.Z + - 2f * q.W * q.X * v.Y - - q.X * q.X * v.Z + - q.W * q.W * v.Z; - - return c2; - } - } - - public struct UVCoord - { - public float U; - public float V; - - - public UVCoord(float u, float v) - { - this.U = u; - this.V = v; - } - } - - public struct Face - { - public int primFace; - - // vertices - public int v1; - public int v2; - public int v3; - - //normals - public int n1; - public int n2; - public int n3; - - // uvs - public int uv1; - public int uv2; - public int uv3; - - public Face(int v1, int v2, int v3) - { - primFace = 0; - - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - this.n1 = 0; - this.n2 = 0; - this.n3 = 0; - - this.uv1 = 0; - this.uv2 = 0; - this.uv3 = 0; - - } - - public Face(int v1, int v2, int v3, int n1, int n2, int n3) - { - primFace = 0; - - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - this.n1 = n1; - this.n2 = n2; - this.n3 = n3; - - this.uv1 = 0; - this.uv2 = 0; - this.uv3 = 0; - } - - public Coord SurfaceNormal(List coordList) - { - Coord c1 = coordList[this.v1]; - Coord c2 = coordList[this.v2]; - Coord c3 = coordList[this.v3]; - - Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - return Coord.Cross(edge1, edge2).Normalize(); - } - } - - public struct ViewerFace - { - public int primFaceNumber; - - public Coord v1; - public Coord v2; - public Coord v3; - - public int coordIndex1; - public int coordIndex2; - public int coordIndex3; - - public Coord n1; - public Coord n2; - public Coord n3; - - public UVCoord uv1; - public UVCoord uv2; - public UVCoord uv3; - - public ViewerFace(int primFaceNumber) - { - this.primFaceNumber = primFaceNumber; - - this.v1 = new Coord(); - this.v2 = new Coord(); - this.v3 = new Coord(); - - this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet - - this.n1 = new Coord(); - this.n2 = new Coord(); - this.n3 = new Coord(); - - this.uv1 = new UVCoord(); - this.uv2 = new UVCoord(); - this.uv3 = new UVCoord(); - } - - public void Scale(float x, float y, float z) - { - this.v1.X *= x; - this.v1.Y *= y; - this.v1.Z *= z; - - this.v2.X *= x; - this.v2.Y *= y; - this.v2.Z *= z; - - this.v3.X *= x; - this.v3.Y *= y; - this.v3.Z *= z; - } - - public void AddPos(float x, float y, float z) - { - this.v1.X += x; - this.v2.X += x; - this.v3.X += x; - - this.v1.Y += y; - this.v2.Y += y; - this.v3.Y += y; - - this.v1.Z += z; - this.v2.Z += z; - this.v3.Z += z; - } - - public void AddRot(Quat q) - { - this.v1 *= q; - this.v2 *= q; - this.v3 *= q; - - this.n1 *= q; - this.n2 *= q; - this.n3 *= q; - } - - public void CalcSurfaceNormal() - { - - Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z); - Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z); - - this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); - } - } - - internal struct Angle - { - internal float angle; - internal float X; - internal float Y; - - internal Angle(float angle, float x, float y) - { - this.angle = angle; - this.X = x; - this.Y = y; - } - } - - internal class AngleList - { - private float iX, iY; // intersection point - - private static Angle[] angles3 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), - new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private static Coord[] normals3 = - { - new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), - new Coord(-0.5f, 0.0f, 0.0f).Normalize(), - new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), - new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() - }; - - private static Angle[] angles4 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.25f, 0.0f, 1.0f), - new Angle(0.5f, -1.0f, 0.0f), - new Angle(0.75f, 0.0f, -1.0f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private static Coord[] normals4 = - { - new Coord(0.5f, 0.5f, 0.0f).Normalize(), - new Coord(-0.5f, 0.5f, 0.0f).Normalize(), - new Coord(-0.5f, -0.5f, 0.0f).Normalize(), - new Coord(0.5f, -0.5f, 0.0f).Normalize(), - new Coord(0.5f, 0.5f, 0.0f).Normalize() - }; - - private static Angle[] angles24 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f), - new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f), - new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f), - new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f), - new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f), - new Angle(0.25f, 0.0f, 1.0f), - new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f), - new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), - new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f), - new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f), - new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f), - new Angle(0.5f, -1.0f, 0.0f), - new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f), - new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f), - new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f), - new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), - new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f), - new Angle(0.75f, 0.0f, -1.0f), - new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f), - new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f), - new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f), - new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f), - new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private Angle interpolatePoints(float newPoint, Angle p1, Angle p2) - { - float m = (newPoint - p1.angle) / (p2.angle - p1.angle); - return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y)); - } - - private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) - { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ - double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); - double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); - - if (denom != 0.0) - { - double ua = uaNumerator / denom; - iX = (float)(x1 + ua * (x2 - x1)); - iY = (float)(y1 + ua * (y2 - y1)); - } - } - - internal List angles; - internal List normals; - - internal void makeAngles(int sides, float startAngle, float stopAngle) - { - angles = new List(); - normals = new List(); - - double twoPi = System.Math.PI * 2.0; - float twoPiInv = 1.0f / (float)twoPi; - - if (sides < 1) - throw new Exception("number of sides not greater than zero"); - if (stopAngle <= startAngle) - throw new Exception("stopAngle not greater than startAngle"); - - if ((sides == 3 || sides == 4 || sides == 24)) - { - startAngle *= twoPiInv; - stopAngle *= twoPiInv; - - Angle[] sourceAngles; - if (sides == 3) - sourceAngles = angles3; - else if (sides == 4) - sourceAngles = angles4; - else sourceAngles = angles24; - - int startAngleIndex = (int)(startAngle * sides); - int endAngleIndex = sourceAngles.Length - 1; - if (stopAngle < 1.0f) - endAngleIndex = (int)(stopAngle * sides) + 1; - if (endAngleIndex == startAngleIndex) - endAngleIndex++; - - for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) - { - angles.Add(sourceAngles[angleIndex]); - if (sides == 3) - normals.Add(normals3[angleIndex]); - else if (sides == 4) - normals.Add(normals4[angleIndex]); - } - - if (startAngle > 0.0f) - angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); - - if (stopAngle < 1.0f) - { - int lastAngleIndex = angles.Count - 1; - angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]); - } - } - else - { - double stepSize = twoPi / sides; - - int startStep = (int)(startAngle / stepSize); - double angle = stepSize * startStep; - int step = startStep; - double stopAngleTest = stopAngle; - if (stopAngle < twoPi) - { - stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1); - if (stopAngleTest < stopAngle) - stopAngleTest += stepSize; - if (stopAngleTest > twoPi) - stopAngleTest = twoPi; - } - - while (angle <= stopAngleTest) - { - Angle newAngle; - newAngle.angle = (float)angle; - newAngle.X = (float)System.Math.Cos(angle); - newAngle.Y = (float)System.Math.Sin(angle); - angles.Add(newAngle); - step += 1; - angle = stepSize * step; - } - - if (startAngle > angles[0].angle) - { - Angle newAngle; - intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle)); - newAngle.angle = startAngle; - newAngle.X = iX; - newAngle.Y = iY; - angles[0] = newAngle; - } - - int index = angles.Count - 1; - if (stopAngle < angles[index].angle) - { - Angle newAngle; - intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle)); - newAngle.angle = stopAngle; - newAngle.X = iX; - newAngle.Y = iY; - angles[index] = newAngle; - } - } - } - } - - /// - /// generates a profile for extrusion - /// - internal class Profile - { - private const float twoPi = 2.0f * (float)Math.PI; - - internal string errorMessage = null; - - internal List coords; - internal List faces; - internal List vertexNormals; - internal List us; - internal List faceUVs; - internal List faceNumbers; - - // use these for making individual meshes for each prim face - internal List outerCoordIndices = null; - internal List hollowCoordIndices = null; - internal List cut1CoordIndices = null; - internal List cut2CoordIndices = null; - - internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); - internal Coord cutNormal1 = new Coord(); - internal Coord cutNormal2 = new Coord(); - - internal int numOuterVerts = 0; - internal int numHollowVerts = 0; - - internal int outerFaceNumber = -1; - internal int hollowFaceNumber = -1; - - internal bool calcVertexNormals = false; - internal int bottomFaceNumber = 0; - internal int numPrimFaces = 0; - - internal Profile() - { - this.coords = new List(); - this.faces = new List(); - this.vertexNormals = new List(); - this.us = new List(); - this.faceUVs = new List(); - this.faceNumbers = new List(); - } - - internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) - { - this.calcVertexNormals = calcVertexNormals; - this.coords = new List(); - this.faces = new List(); - this.vertexNormals = new List(); - this.us = new List(); - this.faceUVs = new List(); - this.faceNumbers = new List(); - - Coord center = new Coord(0.0f, 0.0f, 0.0f); - //bool hasCenter = false; - - List hollowCoords = new List(); - List hollowNormals = new List(); - List hollowUs = new List(); - - if (calcVertexNormals) - { - this.outerCoordIndices = new List(); - this.hollowCoordIndices = new List(); - this.cut1CoordIndices = new List(); - this.cut2CoordIndices = new List(); - } - - bool hasHollow = (hollow > 0.0f); - - bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); - - AngleList angles = new AngleList(); - AngleList hollowAngles = new AngleList(); - - float xScale = 0.5f; - float yScale = 0.5f; - if (sides == 4) // corners of a square are sqrt(2) from center - { - xScale = 0.707f; - yScale = 0.707f; - } - - float startAngle = profileStart * twoPi; - float stopAngle = profileEnd * twoPi; - - try { angles.makeAngles(sides, startAngle, stopAngle); } - catch (Exception ex) - { - - errorMessage = "makeAngles failed: Exception: " + ex.ToString() - + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); - - return; - } - - this.numOuterVerts = angles.angles.Count; - - // flag to create as few triangles as possible for 3 or 4 side profile - bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut); - - if (hasHollow) - { - if (sides == hollowSides) - hollowAngles = angles; - else - { - try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); } - catch (Exception ex) - { - errorMessage = "makeAngles failed: Exception: " + ex.ToString() - + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); - - return; - } - } - this.numHollowVerts = hollowAngles.angles.Count; - } - else if (!simpleFace) - { - this.coords.Add(center); - //hasCenter = true; - if (this.calcVertexNormals) - this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); - this.us.Add(0.0f); - } - - float z = 0.0f; - - Angle angle; - Coord newVert = new Coord(); - if (hasHollow && hollowSides != sides) - { - int numHollowAngles = hollowAngles.angles.Count; - for (int i = 0; i < numHollowAngles; i++) - { - angle = hollowAngles.angles[i]; - newVert.X = hollow * xScale * angle.X; - newVert.Y = hollow * yScale * angle.Y; - newVert.Z = z; - - hollowCoords.Add(newVert); - if (this.calcVertexNormals) - { - if (hollowSides < 5) - hollowNormals.Add(hollowAngles.normals[i].Invert()); - else - hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); - - hollowUs.Add(angle.angle * hollow); - } - } - } - - int index = 0; - int numAngles = angles.angles.Count; - - for (int i = 0; i < numAngles; i++) - { - angle = angles.angles[i]; - newVert.X = angle.X * xScale; - newVert.Y = angle.Y * yScale; - newVert.Z = z; - this.coords.Add(newVert); - if (this.calcVertexNormals) - { - this.outerCoordIndices.Add(this.coords.Count - 1); - - if (sides < 5) - { - this.vertexNormals.Add(angles.normals[i]); - float u = angle.angle; - this.us.Add(u); - } - else - { - this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); - this.us.Add(angle.angle); - } - } - - if (hasHollow) - { - if (hollowSides == sides) - { - newVert.X *= hollow; - newVert.Y *= hollow; - newVert.Z = z; - hollowCoords.Add(newVert); - if (this.calcVertexNormals) - { - if (sides < 5) - { - hollowNormals.Add(angles.normals[i].Invert()); - } - - else - hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); - - hollowUs.Add(angle.angle * hollow); - } - } - } - else if (!simpleFace && createFaces && angle.angle > 0.0001f) - { - Face newFace = new Face(); - newFace.v1 = 0; - newFace.v2 = index; - newFace.v3 = index + 1; - - this.faces.Add(newFace); - } - index += 1; - } - - if (hasHollow) - { - hollowCoords.Reverse(); - if (this.calcVertexNormals) - { - hollowNormals.Reverse(); - hollowUs.Reverse(); - } - - if (createFaces) - { - //int numOuterVerts = this.coords.Count; - //numOuterVerts = this.coords.Count; - //int numHollowVerts = hollowCoords.Count; - int numTotalVerts = this.numOuterVerts + this.numHollowVerts; - - if (this.numOuterVerts == this.numHollowVerts) - { - Face newFace = new Face(); - - for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++) - { - newFace.v1 = coordIndex; - newFace.v2 = coordIndex + 1; - newFace.v3 = numTotalVerts - coordIndex - 1; - this.faces.Add(newFace); - - newFace.v1 = coordIndex + 1; - newFace.v2 = numTotalVerts - coordIndex - 2; - newFace.v3 = numTotalVerts - coordIndex - 1; - this.faces.Add(newFace); - } - } - else - { - if (this.numOuterVerts < this.numHollowVerts) - { - Face newFace = new Face(); - int j = 0; // j is the index for outer vertices - int maxJ = this.numOuterVerts - 1; - for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices - { - if (j < maxJ) - if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) - { - newFace.v1 = numTotalVerts - i - 1; - newFace.v2 = j; - newFace.v3 = j + 1; - - this.faces.Add(newFace); - j += 1; - } - - newFace.v1 = j; - newFace.v2 = numTotalVerts - i - 2; - newFace.v3 = numTotalVerts - i - 1; - - this.faces.Add(newFace); - } - } - else // numHollowVerts < numOuterVerts - { - Face newFace = new Face(); - int j = 0; // j is the index for inner vertices - int maxJ = this.numHollowVerts - 1; - for (int i = 0; i < this.numOuterVerts; i++) - { - if (j < maxJ) - if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) - { - newFace.v1 = i; - newFace.v2 = numTotalVerts - j - 2; - newFace.v3 = numTotalVerts - j - 1; - - this.faces.Add(newFace); - j += 1; - } - - newFace.v1 = numTotalVerts - j - 1; - newFace.v2 = i; - newFace.v3 = i + 1; - - this.faces.Add(newFace); - } - } - } - } - - if (calcVertexNormals) - { - foreach (Coord hc in hollowCoords) - { - this.coords.Add(hc); - hollowCoordIndices.Add(this.coords.Count - 1); - } - } - else - this.coords.AddRange(hollowCoords); - - if (this.calcVertexNormals) - { - this.vertexNormals.AddRange(hollowNormals); - this.us.AddRange(hollowUs); - - } - } - - if (simpleFace && createFaces) - { - if (sides == 3) - this.faces.Add(new Face(0, 1, 2)); - else if (sides == 4) - { - this.faces.Add(new Face(0, 1, 2)); - this.faces.Add(new Face(0, 2, 3)); - } - } - - if (calcVertexNormals && hasProfileCut) - { - int lastOuterVertIndex = this.numOuterVerts - 1; - - if (hasHollow) - { - this.cut1CoordIndices.Add(0); - this.cut1CoordIndices.Add(this.coords.Count - 1); - - this.cut2CoordIndices.Add(lastOuterVertIndex + 1); - this.cut2CoordIndices.Add(lastOuterVertIndex); - - this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; - this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); - - this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; - this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); - } - - else - { - this.cut1CoordIndices.Add(0); - this.cut1CoordIndices.Add(1); - - this.cut2CoordIndices.Add(lastOuterVertIndex); - this.cut2CoordIndices.Add(0); - - this.cutNormal1.X = this.vertexNormals[1].Y; - this.cutNormal1.Y = -this.vertexNormals[1].X; - - this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; - this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; - - } - this.cutNormal1.Normalize(); - this.cutNormal2.Normalize(); - } - - this.MakeFaceUVs(); - - hollowCoords = null; - hollowNormals = null; - hollowUs = null; - - if (calcVertexNormals) - { // calculate prim face numbers - - // face number order is top, outer, hollow, bottom, start cut, end cut - // I know it's ugly but so is the whole concept of prim face numbers - - int faceNum = 1; // start with outer faces - this.outerFaceNumber = faceNum; - - int startVert = hasProfileCut && !hasHollow ? 1 : 0; - if (startVert > 0) - this.faceNumbers.Add(-1); - for (int i = 0; i < this.numOuterVerts - 1; i++) - //this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); - this.faceNumbers.Add(sides < 5 && i < sides ? faceNum++ : faceNum); - - //if (!hasHollow && !hasProfileCut) - // this.bottomFaceNumber = faceNum++; - - this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); - - if (sides > 4 && (hasHollow || hasProfileCut)) - faceNum++; - - if (sides < 5 && (hasHollow || hasProfileCut) && this.numOuterVerts < sides) - faceNum++; - - if (hasHollow) - { - for (int i = 0; i < this.numHollowVerts; i++) - this.faceNumbers.Add(faceNum); - - this.hollowFaceNumber = faceNum++; - } - //if (hasProfileCut || hasHollow) - // this.bottomFaceNumber = faceNum++; - this.bottomFaceNumber = faceNum++; - - if (hasHollow && hasProfileCut) - this.faceNumbers.Add(faceNum++); - - for (int i = 0; i < this.faceNumbers.Count; i++) - if (this.faceNumbers[i] == -1) - this.faceNumbers[i] = faceNum++; - - this.numPrimFaces = faceNum; - } - - } - - internal void MakeFaceUVs() - { - this.faceUVs = new List(); - foreach (Coord c in this.coords) - this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); - } - - internal Profile Copy() - { - return this.Copy(true); - } - - internal Profile Copy(bool needFaces) - { - Profile copy = new Profile(); - - copy.coords.AddRange(this.coords); - copy.faceUVs.AddRange(this.faceUVs); - - if (needFaces) - copy.faces.AddRange(this.faces); - if ((copy.calcVertexNormals = this.calcVertexNormals) == true) - { - copy.vertexNormals.AddRange(this.vertexNormals); - copy.faceNormal = this.faceNormal; - copy.cutNormal1 = this.cutNormal1; - copy.cutNormal2 = this.cutNormal2; - copy.us.AddRange(this.us); - copy.faceNumbers.AddRange(this.faceNumbers); - - copy.cut1CoordIndices = new List(this.cut1CoordIndices); - copy.cut2CoordIndices = new List(this.cut2CoordIndices); - copy.hollowCoordIndices = new List(this.hollowCoordIndices); - copy.outerCoordIndices = new List(this.outerCoordIndices); - } - copy.numOuterVerts = this.numOuterVerts; - copy.numHollowVerts = this.numHollowVerts; - - return copy; - } - - internal void AddPos(Coord v) - { - this.AddPos(v.X, v.Y, v.Z); - } - - internal void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - } - - internal void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - if (this.calcVertexNormals) - { - int numNormals = this.vertexNormals.Count; - for (i = 0; i < numNormals; i++) - this.vertexNormals[i] *= q; - - this.faceNormal *= q; - this.cutNormal1 *= q; - this.cutNormal2 *= q; - - } - } - - internal void Scale(float x, float y) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X *= x; - vert.Y *= y; - this.coords[i] = vert; - } - } - - /// - /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices - /// - internal void FlipNormals() - { - int i; - int numFaces = this.faces.Count; - Face tmpFace; - int tmp; - - for (i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmp = tmpFace.v3; - tmpFace.v3 = tmpFace.v1; - tmpFace.v1 = tmp; - this.faces[i] = tmpFace; - } - - if (this.calcVertexNormals) - { - int normalCount = this.vertexNormals.Count; - if (normalCount > 0) - { - Coord n = this.vertexNormals[normalCount - 1]; - n.Z = -n.Z; - this.vertexNormals[normalCount - 1] = n; - } - } - - this.faceNormal.X = -this.faceNormal.X; - this.faceNormal.Y = -this.faceNormal.Y; - this.faceNormal.Z = -this.faceNormal.Z; - - int numfaceUVs = this.faceUVs.Count; - for (i = 0; i < numfaceUVs; i++) - { - UVCoord uv = this.faceUVs[i]; - uv.V = 1.0f - uv.V; - this.faceUVs[i] = uv; - } - } - - internal void AddValue2FaceVertexIndices(int num) - { - int numFaces = this.faces.Count; - Face tmpFace; - for (int i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmpFace.v1 += num; - tmpFace.v2 += num; - tmpFace.v3 += num; - - this.faces[i] = tmpFace; - } - } - - internal void AddValue2FaceNormalIndices(int num) - { - if (this.calcVertexNormals) - { - int numFaces = this.faces.Count; - Face tmpFace; - for (int i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmpFace.n1 += num; - tmpFace.n2 += num; - tmpFace.n3 += num; - - this.faces[i] = tmpFace; - } - } - } - - internal void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } - - public struct PathNode - { - public Coord position; - public Quat rotation; - public float xScale; - public float yScale; - public float percentOfPath; - } - - public enum PathType { Linear = 0, Circular = 1, Flexible = 2 } - - public class Path - { - public List pathNodes = new List(); - - public float twistBegin = 0.0f; - public float twistEnd = 0.0f; - public float topShearX = 0.0f; - public float topShearY = 0.0f; - public float pathCutBegin = 0.0f; - public float pathCutEnd = 1.0f; - public float dimpleBegin = 0.0f; - public float dimpleEnd = 1.0f; - public float skew = 0.0f; - public float holeSizeX = 1.0f; // called pathScaleX in pbs - public float holeSizeY = 0.25f; - public float taperX = 0.0f; - public float taperY = 0.0f; - public float radius = 0.0f; - public float revolutions = 1.0f; - public int stepsPerRevolution = 24; - - private const float twoPi = 2.0f * (float)Math.PI; - - public void Create(PathType pathType, int steps) - { - if (pathType == PathType.Linear || pathType == PathType.Flexible) - { - int step = 0; - - float length = this.pathCutEnd - this.pathCutBegin; - float twistTotal = twistEnd - twistBegin; - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number - - float start = -0.5f; - float stepSize = length / (float)steps; - float percentOfPathMultiplier = stepSize; - float xOffset = 0.0f; - float yOffset = 0.0f; - float zOffset = start; - float xOffsetStepIncrement = this.topShearX / steps; - float yOffsetStepIncrement = this.topShearY / steps; - - float percentOfPath = this.pathCutBegin; - zOffset += percentOfPath; - - // sanity checks - - bool done = false; - - while (!done) - { - PathNode newNode = new PathNode(); - - newNode.xScale = 1.0f; - if (this.taperX == 0.0f) - newNode.xScale = 1.0f; - else if (this.taperX > 0.0f) - newNode.xScale = 1.0f - percentOfPath * this.taperX; - else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX; - - newNode.yScale = 1.0f; - if (this.taperY == 0.0f) - newNode.yScale = 1.0f; - else if (this.taperY > 0.0f) - newNode.yScale = 1.0f - percentOfPath * this.taperY; - else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY; - - float twist = twistBegin + twistTotal * percentOfPath; - - newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); - newNode.position = new Coord(xOffset, yOffset, zOffset); - newNode.percentOfPath = percentOfPath; - - pathNodes.Add(newNode); - - if (step < steps) - { - step += 1; - percentOfPath += percentOfPathMultiplier; - xOffset += xOffsetStepIncrement; - yOffset += yOffsetStepIncrement; - zOffset += stepSize; - if (percentOfPath > this.pathCutEnd) - done = true; - } - else done = true; - } - } // end of linear path code - - else // pathType == Circular - { - float twistTotal = twistEnd - twistBegin; - - // if the profile has a lot of twist, add more layers otherwise the layers may overlap - // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't - // accurately match the viewer - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - { - if (twistTotalAbs > Math.PI * 1.5f) - steps *= 2; - if (twistTotalAbs > Math.PI * 3.0f) - steps *= 2; - } - - float yPathScale = this.holeSizeY * 0.5f; - float pathLength = this.pathCutEnd - this.pathCutBegin; - float totalSkew = this.skew * 2.0f * pathLength; - float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew; - float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY)); - float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f; - - // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end - // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used - // to calculate the sine for generating the path radius appears to approximate it's effects there - // too, but there are some subtle differences in the radius which are noticeable as the prim size - // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on - // the meshes generated with this technique appear nearly identical in shape to the same prims when - // displayed by the viewer. - - float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f; - float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f; - float stepSize = twoPi / this.stepsPerRevolution; - - int step = (int)(startAngle / stepSize); - float angle = startAngle; - - bool done = false; - while (!done) // loop through the length of the path and add the layers - { - PathNode newNode = new PathNode(); - - float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX; - float yProfileScale = this.holeSizeY; - - float percentOfPath = angle / (twoPi * this.revolutions); - float percentOfAngles = (angle - startAngle) / (endAngle - startAngle); - - if (this.taperX > 0.01f) - xProfileScale *= 1.0f - percentOfPath * this.taperX; - else if (this.taperX < -0.01f) - xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX; - - if (this.taperY > 0.01f) - yProfileScale *= 1.0f - percentOfPath * this.taperY; - else if (this.taperY < -0.01f) - yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY; - - newNode.xScale = xProfileScale; - newNode.yScale = yProfileScale; - - float radiusScale = 1.0f; - if (this.radius > 0.001f) - radiusScale = 1.0f - this.radius * percentOfPath; - else if (this.radius < 0.001f) - radiusScale = 1.0f + this.radius * (1.0f - percentOfPath); - - float twist = twistBegin + twistTotal * percentOfPath; - - float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles); - xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor; - - float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale; - - float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale; - - newNode.position = new Coord(xOffset, yOffset, zOffset); - - // now orient the rotation of the profile layer relative to it's position on the path - // adding taperY to the angle used to generate the quat appears to approximate the viewer - - newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY); - - // next apply twist rotation to the profile layer - if (twistTotal != 0.0f || twistBegin != 0.0f) - newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); - - newNode.percentOfPath = percentOfPath; - - pathNodes.Add(newNode); - - // calculate terms for next iteration - // calculate the angle for the next iteration of the loop - - if (angle >= endAngle - 0.01) - done = true; - else - { - step += 1; - angle = stepSize * step; - if (angle > endAngle) - angle = endAngle; - } - } - } - } - } - - public class PrimMesh - { - public string errorMessage = ""; - private const float twoPi = 2.0f * (float)Math.PI; - - public List coords; - public List normals; - public List faces; - - public List viewerFaces; - - private int sides = 4; - private int hollowSides = 4; - private float profileStart = 0.0f; - private float profileEnd = 1.0f; - private float hollow = 0.0f; - public int twistBegin = 0; - public int twistEnd = 0; - public float topShearX = 0.0f; - public float topShearY = 0.0f; - public float pathCutBegin = 0.0f; - public float pathCutEnd = 1.0f; - public float dimpleBegin = 0.0f; - public float dimpleEnd = 1.0f; - public float skew = 0.0f; - public float holeSizeX = 1.0f; // called pathScaleX in pbs - public float holeSizeY = 0.25f; - public float taperX = 0.0f; - public float taperY = 0.0f; - public float radius = 0.0f; - public float revolutions = 1.0f; - public int stepsPerRevolution = 24; - - private int profileOuterFaceNumber = -1; - private int profileHollowFaceNumber = -1; - - private bool hasProfileCut = false; - private bool hasHollow = false; - public bool calcVertexNormals = false; - private bool normalsProcessed = false; - public bool viewerMode = false; - public bool sphereMode = false; - - public int numPrimFaces = 0; - - /// - /// Human readable string representation of the parameters used to create a mesh. - /// - /// - public string ParamsToDisplayString() - { - string s = ""; - s += "sides..................: " + this.sides.ToString(); - s += "\nhollowSides..........: " + this.hollowSides.ToString(); - s += "\nprofileStart.........: " + this.profileStart.ToString(); - s += "\nprofileEnd...........: " + this.profileEnd.ToString(); - s += "\nhollow...............: " + this.hollow.ToString(); - s += "\ntwistBegin...........: " + this.twistBegin.ToString(); - s += "\ntwistEnd.............: " + this.twistEnd.ToString(); - s += "\ntopShearX............: " + this.topShearX.ToString(); - s += "\ntopShearY............: " + this.topShearY.ToString(); - s += "\npathCutBegin.........: " + this.pathCutBegin.ToString(); - s += "\npathCutEnd...........: " + this.pathCutEnd.ToString(); - s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString(); - s += "\ndimpleEnd............: " + this.dimpleEnd.ToString(); - s += "\nskew.................: " + this.skew.ToString(); - s += "\nholeSizeX............: " + this.holeSizeX.ToString(); - s += "\nholeSizeY............: " + this.holeSizeY.ToString(); - s += "\ntaperX...............: " + this.taperX.ToString(); - s += "\ntaperY...............: " + this.taperY.ToString(); - s += "\nradius...............: " + this.radius.ToString(); - s += "\nrevolutions..........: " + this.revolutions.ToString(); - s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); - s += "\nsphereMode...........: " + this.sphereMode.ToString(); - s += "\nhasProfileCut........: " + this.hasProfileCut.ToString(); - s += "\nhasHollow............: " + this.hasHollow.ToString(); - s += "\nviewerMode...........: " + this.viewerMode.ToString(); - - return s; - } - - public int ProfileOuterFaceNumber - { - get { return profileOuterFaceNumber; } - } - - public int ProfileHollowFaceNumber - { - get { return profileHollowFaceNumber; } - } - - public bool HasProfileCut - { - get { return hasProfileCut; } - } - - public bool HasHollow - { - get { return hasHollow; } - } - - - /// - /// Constructs a PrimMesh object and creates the profile for extrusion. - /// - /// - /// - /// - /// - /// - public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) - { - this.coords = new List(); - this.faces = new List(); - - this.sides = sides; - this.profileStart = profileStart; - this.profileEnd = profileEnd; - this.hollow = hollow; - this.hollowSides = hollowSides; - - if (sides < 3) - this.sides = 3; - if (hollowSides < 3) - this.hollowSides = 3; - if (profileStart < 0.0f) - this.profileStart = 0.0f; - if (profileEnd > 1.0f) - this.profileEnd = 1.0f; - if (profileEnd < 0.02f) - this.profileEnd = 0.02f; - if (profileStart >= profileEnd) - this.profileStart = profileEnd - 0.02f; - if (hollow > 0.99f) - this.hollow = 0.99f; - if (hollow < 0.0f) - this.hollow = 0.0f; - - //if (sphereMode) - // this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; - //else - // //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); - // this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; - //this.hasHollow = (this.hollow > 0.001f); - } - - /// - /// Extrudes a profile along a path. - /// - public void Extrude(PathType pathType) - { - bool needEndFaces = false; - - this.coords = new List(); - this.faces = new List(); - - if (this.viewerMode) - { - this.viewerFaces = new List(); - this.calcVertexNormals = true; - } - - if (this.calcVertexNormals) - this.normals = new List(); - - int steps = 1; - - float length = this.pathCutEnd - this.pathCutBegin; - normalsProcessed = false; - - if (this.viewerMode && this.sides == 3) - { - // prisms don't taper well so add some vertical resolution - // other prims may benefit from this but just do prisms for now - if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) - steps = (int)(steps * 4.5 * length); - } - - if (sphereMode) - this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; - else - //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); - this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; - this.hasHollow = (this.hollow > 0.001f); - - float twistBegin = this.twistBegin / 360.0f * twoPi; - float twistEnd = this.twistEnd / 360.0f * twoPi; - float twistTotal = twistEnd - twistBegin; - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number - - float hollow = this.hollow; - - // sanity checks - float initialProfileRot = 0.0f; - if (pathType == PathType.Circular) - { - if (this.sides == 3) - { - initialProfileRot = (float)Math.PI; - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow *= 0.707f; - } - else hollow *= 0.5f; - } - else if (this.sides == 4) - { - initialProfileRot = 0.25f * (float)Math.PI; - if (this.hollowSides != 4) - hollow *= 0.707f; - } - else if (this.sides > 4) - { - initialProfileRot = (float)Math.PI; - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow /= 0.7f; - } - } - } - else - { - if (this.sides == 3) - { - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow *= 0.707f; - } - else hollow *= 0.5f; - } - else if (this.sides == 4) - { - initialProfileRot = 1.25f * (float)Math.PI; - if (this.hollowSides != 4) - hollow *= 0.707f; - } - else if (this.sides == 24 && this.hollowSides == 4) - hollow *= 1.414f; - } - - Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); - this.errorMessage = profile.errorMessage; - - this.numPrimFaces = profile.numPrimFaces; - - //profileOuterFaceNumber = profile.faceNumbers[0]; - //if (!needEndFaces) - // profileOuterFaceNumber--; - //profileOuterFaceNumber = needEndFaces ? 1 : 0; - - - //if (hasHollow) - //{ - // if (needEndFaces) - // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts + 1]; - // else - // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts] - 1; - //} - - - profileOuterFaceNumber = profile.outerFaceNumber; - if (!needEndFaces) - profileOuterFaceNumber--; - - if (hasHollow) - { - profileHollowFaceNumber = profile.hollowFaceNumber; - if (!needEndFaces) - profileHollowFaceNumber--; - } - - int cut1Vert = -1; - int cut2Vert = -1; - if (hasProfileCut) - { - cut1Vert = hasHollow ? profile.coords.Count - 1 : 0; - cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts; - } - - if (initialProfileRot != 0.0f) - { - profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); - if (viewerMode) - profile.MakeFaceUVs(); - } - - Coord lastCutNormal1 = new Coord(); - Coord lastCutNormal2 = new Coord(); - float lastV = 1.0f; - - Path path = new Path(); - path.twistBegin = twistBegin; - path.twistEnd = twistEnd; - path.topShearX = topShearX; - path.topShearY = topShearY; - path.pathCutBegin = pathCutBegin; - path.pathCutEnd = pathCutEnd; - path.dimpleBegin = dimpleBegin; - path.dimpleEnd = dimpleEnd; - path.skew = skew; - path.holeSizeX = holeSizeX; - path.holeSizeY = holeSizeY; - path.taperX = taperX; - path.taperY = taperY; - path.radius = radius; - path.revolutions = revolutions; - path.stepsPerRevolution = stepsPerRevolution; - - path.Create(pathType, steps); - - - if (pathType == PathType.Circular) - { - needEndFaces = false; - if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f) - needEndFaces = true; - else if (this.taperX != 0.0f || this.taperY != 0.0f) - needEndFaces = true; - else if (this.skew != 0.0f) - needEndFaces = true; - else if (twistTotal != 0.0f) - needEndFaces = true; - else if (this.radius != 0.0f) - needEndFaces = true; - } - else needEndFaces = true; - - for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) - { - PathNode node = path.pathNodes[nodeIndex]; - Profile newLayer = profile.Copy(); - newLayer.Scale(node.xScale, node.yScale); - - newLayer.AddRot(node.rotation); - newLayer.AddPos(node.position); - - if (needEndFaces && nodeIndex == 0) - { - newLayer.FlipNormals(); - - // add the top faces to the viewerFaces list here - if (this.viewerMode) - { - Coord faceNormal = newLayer.faceNormal; - ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber); - int numFaces = newLayer.faces.Count; - List faces = newLayer.faces; - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - newViewerFace.v1 = newLayer.coords[face.v1]; - newViewerFace.v2 = newLayer.coords[face.v2]; - newViewerFace.v3 = newLayer.coords[face.v3]; - - newViewerFace.coordIndex1 = face.v1; - newViewerFace.coordIndex2 = face.v2; - newViewerFace.coordIndex3 = face.v3; - - newViewerFace.n1 = faceNormal; - newViewerFace.n2 = faceNormal; - newViewerFace.n3 = faceNormal; - - newViewerFace.uv1 = newLayer.faceUVs[face.v1]; - newViewerFace.uv2 = newLayer.faceUVs[face.v2]; - newViewerFace.uv3 = newLayer.faceUVs[face.v3]; - - this.viewerFaces.Add(newViewerFace); - } - } - } // if (nodeIndex == 0) - - // append this layer - - int coordsLen = this.coords.Count; - newLayer.AddValue2FaceVertexIndices(coordsLen); - - this.coords.AddRange(newLayer.coords); - - if (this.calcVertexNormals) - { - newLayer.AddValue2FaceNormalIndices(this.normals.Count); - this.normals.AddRange(newLayer.vertexNormals); - } - - if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f) - this.faces.AddRange(newLayer.faces); - - // fill faces between layers - - int numVerts = newLayer.coords.Count; - Face newFace = new Face(); - - if (nodeIndex > 0) - { - int startVert = coordsLen + 1; - int endVert = this.coords.Count; - - if (sides < 5 || this.hasProfileCut || this.hasHollow) - startVert--; - - for (int i = startVert; i < endVert; i++) - { - int iNext = i + 1; - if (i == endVert - 1) - iNext = startVert; - - int whichVert = i - startVert; - - newFace.v1 = i; - newFace.v2 = i - numVerts; - newFace.v3 = iNext - numVerts; - this.faces.Add(newFace); - - newFace.v2 = iNext - numVerts; - newFace.v3 = iNext; - this.faces.Add(newFace); - - if (this.viewerMode) - { - // add the side faces to the list of viewerFaces here - - int primFaceNum = profile.faceNumbers[whichVert]; - if (!needEndFaces) - primFaceNum -= 1; - - ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); - ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); - - float u1 = newLayer.us[whichVert]; - float u2 = 1.0f; - if (whichVert < newLayer.us.Count - 1) - u2 = newLayer.us[whichVert + 1]; - - if (whichVert == cut1Vert || whichVert == cut2Vert) - { - u1 = 0.0f; - u2 = 1.0f; - } - else if (sides < 5) - { - if (whichVert < profile.numOuterVerts) - { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled - // to reflect the entire texture width - u1 *= sides; - u2 *= sides; - u2 -= (int)u1; - u1 -= (int)u1; - if (u2 < 0.1f) - u2 = 1.0f; - //this.profileOuterFaceNumber = primFaceNum; - } - else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) - { - u1 *= 2.0f; - u2 *= 2.0f; - //this.profileHollowFaceNumber = primFaceNum; - } - } - - newViewerFace1.uv1.U = u1; - newViewerFace1.uv2.U = u1; - newViewerFace1.uv3.U = u2; - - newViewerFace1.uv1.V = 1.0f - node.percentOfPath; - newViewerFace1.uv2.V = lastV; - newViewerFace1.uv3.V = lastV; - - newViewerFace2.uv1.U = u1; - newViewerFace2.uv2.U = u2; - newViewerFace2.uv3.U = u2; - - newViewerFace2.uv1.V = 1.0f - node.percentOfPath; - newViewerFace2.uv2.V = lastV; - newViewerFace2.uv3.V = 1.0f - node.percentOfPath; - - newViewerFace1.v1 = this.coords[i]; - newViewerFace1.v2 = this.coords[i - numVerts]; - newViewerFace1.v3 = this.coords[iNext - numVerts]; - - newViewerFace2.v1 = this.coords[i]; - newViewerFace2.v2 = this.coords[iNext - numVerts]; - newViewerFace2.v3 = this.coords[iNext]; - - newViewerFace1.coordIndex1 = i; - newViewerFace1.coordIndex2 = i - numVerts; - newViewerFace1.coordIndex3 = iNext - numVerts; - - newViewerFace2.coordIndex1 = i; - newViewerFace2.coordIndex2 = iNext - numVerts; - newViewerFace2.coordIndex3 = iNext; - - // profile cut faces - if (whichVert == cut1Vert) - { - newViewerFace1.n1 = newLayer.cutNormal1; - newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; - - newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; - newViewerFace2.n2 = lastCutNormal1; - } - else if (whichVert == cut2Vert) - { - newViewerFace1.n1 = newLayer.cutNormal2; - newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; - - newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; - newViewerFace2.n2 = lastCutNormal2; - } - - else // outer and hollow faces - { - if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) - { // looks terrible when path is twisted... need vertex normals here - newViewerFace1.CalcSurfaceNormal(); - newViewerFace2.CalcSurfaceNormal(); - } - else - { - newViewerFace1.n1 = this.normals[i]; - newViewerFace1.n2 = this.normals[i - numVerts]; - newViewerFace1.n3 = this.normals[iNext - numVerts]; - - newViewerFace2.n1 = this.normals[i]; - newViewerFace2.n2 = this.normals[iNext - numVerts]; - newViewerFace2.n3 = this.normals[iNext]; - } - } - - this.viewerFaces.Add(newViewerFace1); - this.viewerFaces.Add(newViewerFace2); - - } - } - } - - lastCutNormal1 = newLayer.cutNormal1; - lastCutNormal2 = newLayer.cutNormal2; - lastV = 1.0f - node.percentOfPath; - - if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) - { - // add the top faces to the viewerFaces list here - Coord faceNormal = newLayer.faceNormal; - ViewerFace newViewerFace = new ViewerFace(); - newViewerFace.primFaceNumber = 0; - int numFaces = newLayer.faces.Count; - List faces = newLayer.faces; - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; - newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; - newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; - - newViewerFace.coordIndex1 = face.v1 - coordsLen; - newViewerFace.coordIndex2 = face.v2 - coordsLen; - newViewerFace.coordIndex3 = face.v3 - coordsLen; - - newViewerFace.n1 = faceNormal; - newViewerFace.n2 = faceNormal; - newViewerFace.n3 = faceNormal; - - newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen]; - newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; - newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; - - this.viewerFaces.Add(newViewerFace); - } - } - - - } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) - - } - - - /// - /// DEPRICATED - use Extrude(PathType.Linear) instead - /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. - /// - /// - public void ExtrudeLinear() - { - this.Extrude(PathType.Linear); - } - - - /// - /// DEPRICATED - use Extrude(PathType.Circular) instead - /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. - /// - /// - public void ExtrudeCircular() - { - this.Extrude(PathType.Circular); - } - - - private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3) - { - Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - Coord normal = Coord.Cross(edge1, edge2); - - normal.Normalize(); - - return normal; - } - - private Coord SurfaceNormal(Face face) - { - return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); - } - - /// - /// Calculate the surface normal for a face in the list of faces - /// - /// - /// - public Coord SurfaceNormal(int faceIndex) - { - int numFaces = this.faces.Count; - if (faceIndex < 0 || faceIndex >= numFaces) - throw new Exception("faceIndex out of range"); - - return SurfaceNormal(this.faces[faceIndex]); - } - - /// - /// Duplicates a PrimMesh object. All object properties are copied by value, including lists. - /// - /// - public PrimMesh Copy() - { - PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides); - copy.twistBegin = this.twistBegin; - copy.twistEnd = this.twistEnd; - copy.topShearX = this.topShearX; - copy.topShearY = this.topShearY; - copy.pathCutBegin = this.pathCutBegin; - copy.pathCutEnd = this.pathCutEnd; - copy.dimpleBegin = this.dimpleBegin; - copy.dimpleEnd = this.dimpleEnd; - copy.skew = this.skew; - copy.holeSizeX = this.holeSizeX; - copy.holeSizeY = this.holeSizeY; - copy.taperX = this.taperX; - copy.taperY = this.taperY; - copy.radius = this.radius; - copy.revolutions = this.revolutions; - copy.stepsPerRevolution = this.stepsPerRevolution; - copy.calcVertexNormals = this.calcVertexNormals; - copy.normalsProcessed = this.normalsProcessed; - copy.viewerMode = this.viewerMode; - copy.numPrimFaces = this.numPrimFaces; - copy.errorMessage = this.errorMessage; - - copy.coords = new List(this.coords); - copy.faces = new List(this.faces); - copy.viewerFaces = new List(this.viewerFaces); - copy.normals = new List(this.normals); - - return copy; - } - - /// - /// Calculate surface normals for all of the faces in the list of faces in this mesh - /// - public void CalcNormals() - { - if (normalsProcessed) - return; - - normalsProcessed = true; - - int numFaces = faces.Count; - - if (!this.calcVertexNormals) - this.normals = new List(); - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - - this.normals.Add(SurfaceNormal(i).Normalize()); - - int normIndex = normals.Count - 1; - face.n1 = normIndex; - face.n2 = normIndex; - face.n3 = normIndex; - - this.faces[i] = face; - } - } - - /// - /// Adds a value to each XYZ vertex coordinate in the mesh - /// - /// - /// - /// - public void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.AddPos(x, y, z); - this.viewerFaces[i] = v; - } - } - } - - /// - /// Rotates the mesh - /// - /// - public void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - if (this.normals != null) - { - int numNormals = this.normals.Count; - for (i = 0; i < numNormals; i++) - this.normals[i] *= q; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= q; - v.v2 *= q; - v.v3 *= q; - - v.n1 *= q; - v.n2 *= q; - v.n3 *= q; - this.viewerFaces[i] = v; - } - } - } - -#if VERTEX_INDEXER - public VertexIndexer GetVertexIndexer() - { - if (this.viewerMode && this.viewerFaces.Count > 0) - return new VertexIndexer(this); - return null; - } -#endif - - /// - /// Scales the mesh - /// - /// - /// - /// - public void Scale(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - //Coord vert; - - Coord m = new Coord(x, y, z); - for (i = 0; i < numVerts; i++) - this.coords[i] *= m; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= m; - v.v2 *= m; - v.v3 *= m; - this.viewerFaces[i] = v; - } - - } - - } - - /// - /// Dumps the mesh to a Blender compatible "Raw" format file - /// - /// - /// - /// - public void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } -} +/* + * Copyright (c) Contributors + * 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 OpenSimulator 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.Text; +using System.IO; + +namespace PrimMesher +{ + public struct Quat + { + /// X value + public float X; + /// Y value + public float Y; + /// Z value + public float Z; + /// W value + public float W; + + public Quat(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + public Quat(Coord axis, float angle) + { + axis = axis.Normalize(); + + angle *= 0.5f; + float c = (float)Math.Cos(angle); + float s = (float)Math.Sin(angle); + + X = axis.X * s; + Y = axis.Y * s; + Z = axis.Z * s; + W = c; + + Normalize(); + } + + public float Length() + { + return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); + } + + public Quat Normalize() + { + const float MAG_THRESHOLD = 0.0000001f; + float mag = Length(); + + // Catch very small rounding errors when normalizing + if (mag > MAG_THRESHOLD) + { + float oomag = 1f / mag; + X *= oomag; + Y *= oomag; + Z *= oomag; + W *= oomag; + } + else + { + X = 0f; + Y = 0f; + Z = 0f; + W = 1f; + } + + return this; + } + + public static Quat operator *(Quat q1, Quat q2) + { + float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y; + float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X; + float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W; + float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z; + return new Quat(x, y, z, w); + } + + public override string ToString() + { + return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">"; + } + } + + public struct Coord + { + public float X; + public float Y; + public float Z; + + public Coord(float x, float y, float z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + public float Length() + { + return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); + } + + public Coord Invert() + { + this.X = -this.X; + this.Y = -this.Y; + this.Z = -this.Z; + + return this; + } + + public Coord Normalize() + { + const float MAG_THRESHOLD = 0.0000001f; + float mag = Length(); + + // Catch very small rounding errors when normalizing + if (mag > MAG_THRESHOLD) + { + float oomag = 1.0f / mag; + this.X *= oomag; + this.Y *= oomag; + this.Z *= oomag; + } + else + { + this.X = 0.0f; + this.Y = 0.0f; + this.Z = 0.0f; + } + + return this; + } + + public override string ToString() + { + return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString(); + } + + public static Coord Cross(Coord c1, Coord c2) + { + return new Coord( + c1.Y * c2.Z - c2.Y * c1.Z, + c1.Z * c2.X - c2.Z * c1.X, + c1.X * c2.Y - c2.X * c1.Y + ); + } + + public static Coord operator +(Coord v, Coord a) + { + return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z); + } + + public static Coord operator *(Coord v, Coord m) + { + return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z); + } + + public static Coord operator *(Coord v, Quat q) + { + // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/ + + Coord c2 = new Coord(0.0f, 0.0f, 0.0f); + + c2.X = q.W * q.W * v.X + + 2f * q.Y * q.W * v.Z - + 2f * q.Z * q.W * v.Y + + q.X * q.X * v.X + + 2f * q.Y * q.X * v.Y + + 2f * q.Z * q.X * v.Z - + q.Z * q.Z * v.X - + q.Y * q.Y * v.X; + + c2.Y = + 2f * q.X * q.Y * v.X + + q.Y * q.Y * v.Y + + 2f * q.Z * q.Y * v.Z + + 2f * q.W * q.Z * v.X - + q.Z * q.Z * v.Y + + q.W * q.W * v.Y - + 2f * q.X * q.W * v.Z - + q.X * q.X * v.Y; + + c2.Z = + 2f * q.X * q.Z * v.X + + 2f * q.Y * q.Z * v.Y + + q.Z * q.Z * v.Z - + 2f * q.W * q.Y * v.X - + q.Y * q.Y * v.Z + + 2f * q.W * q.X * v.Y - + q.X * q.X * v.Z + + q.W * q.W * v.Z; + + return c2; + } + } + + public struct UVCoord + { + public float U; + public float V; + + + public UVCoord(float u, float v) + { + this.U = u; + this.V = v; + } + } + + public struct Face + { + public int primFace; + + // vertices + public int v1; + public int v2; + public int v3; + + //normals + public int n1; + public int n2; + public int n3; + + // uvs + public int uv1; + public int uv2; + public int uv3; + + public Face(int v1, int v2, int v3) + { + primFace = 0; + + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + this.n1 = 0; + this.n2 = 0; + this.n3 = 0; + + this.uv1 = 0; + this.uv2 = 0; + this.uv3 = 0; + + } + + public Face(int v1, int v2, int v3, int n1, int n2, int n3) + { + primFace = 0; + + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + this.n1 = n1; + this.n2 = n2; + this.n3 = n3; + + this.uv1 = 0; + this.uv2 = 0; + this.uv3 = 0; + } + + public Coord SurfaceNormal(List coordList) + { + Coord c1 = coordList[this.v1]; + Coord c2 = coordList[this.v2]; + Coord c3 = coordList[this.v3]; + + Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + return Coord.Cross(edge1, edge2).Normalize(); + } + } + + public struct ViewerFace + { + public int primFaceNumber; + + public Coord v1; + public Coord v2; + public Coord v3; + + public int coordIndex1; + public int coordIndex2; + public int coordIndex3; + + public Coord n1; + public Coord n2; + public Coord n3; + + public UVCoord uv1; + public UVCoord uv2; + public UVCoord uv3; + + public ViewerFace(int primFaceNumber) + { + this.primFaceNumber = primFaceNumber; + + this.v1 = new Coord(); + this.v2 = new Coord(); + this.v3 = new Coord(); + + this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet + + this.n1 = new Coord(); + this.n2 = new Coord(); + this.n3 = new Coord(); + + this.uv1 = new UVCoord(); + this.uv2 = new UVCoord(); + this.uv3 = new UVCoord(); + } + + public void Scale(float x, float y, float z) + { + this.v1.X *= x; + this.v1.Y *= y; + this.v1.Z *= z; + + this.v2.X *= x; + this.v2.Y *= y; + this.v2.Z *= z; + + this.v3.X *= x; + this.v3.Y *= y; + this.v3.Z *= z; + } + + public void AddPos(float x, float y, float z) + { + this.v1.X += x; + this.v2.X += x; + this.v3.X += x; + + this.v1.Y += y; + this.v2.Y += y; + this.v3.Y += y; + + this.v1.Z += z; + this.v2.Z += z; + this.v3.Z += z; + } + + public void AddRot(Quat q) + { + this.v1 *= q; + this.v2 *= q; + this.v3 *= q; + + this.n1 *= q; + this.n2 *= q; + this.n3 *= q; + } + + public void CalcSurfaceNormal() + { + + Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z); + Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z); + + this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); + } + } + + internal struct Angle + { + internal float angle; + internal float X; + internal float Y; + + internal Angle(float angle, float x, float y) + { + this.angle = angle; + this.X = x; + this.Y = y; + } + } + + internal class AngleList + { + private float iX, iY; // intersection point + + private static Angle[] angles3 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), + new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private static Coord[] normals3 = + { + new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), + new Coord(-0.5f, 0.0f, 0.0f).Normalize(), + new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), + new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() + }; + + private static Angle[] angles4 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.25f, 0.0f, 1.0f), + new Angle(0.5f, -1.0f, 0.0f), + new Angle(0.75f, 0.0f, -1.0f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private static Coord[] normals4 = + { + new Coord(0.5f, 0.5f, 0.0f).Normalize(), + new Coord(-0.5f, 0.5f, 0.0f).Normalize(), + new Coord(-0.5f, -0.5f, 0.0f).Normalize(), + new Coord(0.5f, -0.5f, 0.0f).Normalize(), + new Coord(0.5f, 0.5f, 0.0f).Normalize() + }; + + private static Angle[] angles24 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f), + new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f), + new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f), + new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f), + new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f), + new Angle(0.25f, 0.0f, 1.0f), + new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f), + new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), + new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f), + new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f), + new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f), + new Angle(0.5f, -1.0f, 0.0f), + new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f), + new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f), + new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f), + new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), + new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f), + new Angle(0.75f, 0.0f, -1.0f), + new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f), + new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f), + new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f), + new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f), + new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private Angle interpolatePoints(float newPoint, Angle p1, Angle p2) + { + float m = (newPoint - p1.angle) / (p2.angle - p1.angle); + return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y)); + } + + private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); + + if (denom != 0.0) + { + double ua = uaNumerator / denom; + iX = (float)(x1 + ua * (x2 - x1)); + iY = (float)(y1 + ua * (y2 - y1)); + } + } + + internal List angles; + internal List normals; + + internal void makeAngles(int sides, float startAngle, float stopAngle) + { + angles = new List(); + normals = new List(); + + double twoPi = System.Math.PI * 2.0; + float twoPiInv = 1.0f / (float)twoPi; + + if (sides < 1) + throw new Exception("number of sides not greater than zero"); + if (stopAngle <= startAngle) + throw new Exception("stopAngle not greater than startAngle"); + + if ((sides == 3 || sides == 4 || sides == 24)) + { + startAngle *= twoPiInv; + stopAngle *= twoPiInv; + + Angle[] sourceAngles; + if (sides == 3) + sourceAngles = angles3; + else if (sides == 4) + sourceAngles = angles4; + else sourceAngles = angles24; + + int startAngleIndex = (int)(startAngle * sides); + int endAngleIndex = sourceAngles.Length - 1; + if (stopAngle < 1.0f) + endAngleIndex = (int)(stopAngle * sides) + 1; + if (endAngleIndex == startAngleIndex) + endAngleIndex++; + + for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) + { + angles.Add(sourceAngles[angleIndex]); + if (sides == 3) + normals.Add(normals3[angleIndex]); + else if (sides == 4) + normals.Add(normals4[angleIndex]); + } + + if (startAngle > 0.0f) + angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); + + if (stopAngle < 1.0f) + { + int lastAngleIndex = angles.Count - 1; + angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]); + } + } + else + { + double stepSize = twoPi / sides; + + int startStep = (int)(startAngle / stepSize); + double angle = stepSize * startStep; + int step = startStep; + double stopAngleTest = stopAngle; + if (stopAngle < twoPi) + { + stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1); + if (stopAngleTest < stopAngle) + stopAngleTest += stepSize; + if (stopAngleTest > twoPi) + stopAngleTest = twoPi; + } + + while (angle <= stopAngleTest) + { + Angle newAngle; + newAngle.angle = (float)angle; + newAngle.X = (float)System.Math.Cos(angle); + newAngle.Y = (float)System.Math.Sin(angle); + angles.Add(newAngle); + step += 1; + angle = stepSize * step; + } + + if (startAngle > angles[0].angle) + { + Angle newAngle; + intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle)); + newAngle.angle = startAngle; + newAngle.X = iX; + newAngle.Y = iY; + angles[0] = newAngle; + } + + int index = angles.Count - 1; + if (stopAngle < angles[index].angle) + { + Angle newAngle; + intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle)); + newAngle.angle = stopAngle; + newAngle.X = iX; + newAngle.Y = iY; + angles[index] = newAngle; + } + } + } + } + + /// + /// generates a profile for extrusion + /// + internal class Profile + { + private const float twoPi = 2.0f * (float)Math.PI; + + internal string errorMessage = null; + + internal List coords; + internal List faces; + internal List vertexNormals; + internal List us; + internal List faceUVs; + internal List faceNumbers; + + // use these for making individual meshes for each prim face + internal List outerCoordIndices = null; + internal List hollowCoordIndices = null; + internal List cut1CoordIndices = null; + internal List cut2CoordIndices = null; + + internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); + internal Coord cutNormal1 = new Coord(); + internal Coord cutNormal2 = new Coord(); + + internal int numOuterVerts = 0; + internal int numHollowVerts = 0; + + internal int outerFaceNumber = -1; + internal int hollowFaceNumber = -1; + + internal bool calcVertexNormals = false; + internal int bottomFaceNumber = 0; + internal int numPrimFaces = 0; + + internal Profile() + { + this.coords = new List(); + this.faces = new List(); + this.vertexNormals = new List(); + this.us = new List(); + this.faceUVs = new List(); + this.faceNumbers = new List(); + } + + internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) + { + this.calcVertexNormals = calcVertexNormals; + this.coords = new List(); + this.faces = new List(); + this.vertexNormals = new List(); + this.us = new List(); + this.faceUVs = new List(); + this.faceNumbers = new List(); + + Coord center = new Coord(0.0f, 0.0f, 0.0f); + //bool hasCenter = false; + + List hollowCoords = new List(); + List hollowNormals = new List(); + List hollowUs = new List(); + + if (calcVertexNormals) + { + this.outerCoordIndices = new List(); + this.hollowCoordIndices = new List(); + this.cut1CoordIndices = new List(); + this.cut2CoordIndices = new List(); + } + + bool hasHollow = (hollow > 0.0f); + + bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); + + AngleList angles = new AngleList(); + AngleList hollowAngles = new AngleList(); + + float xScale = 0.5f; + float yScale = 0.5f; + if (sides == 4) // corners of a square are sqrt(2) from center + { + xScale = 0.707f; + yScale = 0.707f; + } + + float startAngle = profileStart * twoPi; + float stopAngle = profileEnd * twoPi; + + try { angles.makeAngles(sides, startAngle, stopAngle); } + catch (Exception ex) + { + + errorMessage = "makeAngles failed: Exception: " + ex.ToString() + + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); + + return; + } + + this.numOuterVerts = angles.angles.Count; + + // flag to create as few triangles as possible for 3 or 4 side profile + bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut); + + if (hasHollow) + { + if (sides == hollowSides) + hollowAngles = angles; + else + { + try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); } + catch (Exception ex) + { + errorMessage = "makeAngles failed: Exception: " + ex.ToString() + + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); + + return; + } + } + this.numHollowVerts = hollowAngles.angles.Count; + } + else if (!simpleFace) + { + this.coords.Add(center); + //hasCenter = true; + if (this.calcVertexNormals) + this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); + this.us.Add(0.0f); + } + + float z = 0.0f; + + Angle angle; + Coord newVert = new Coord(); + if (hasHollow && hollowSides != sides) + { + int numHollowAngles = hollowAngles.angles.Count; + for (int i = 0; i < numHollowAngles; i++) + { + angle = hollowAngles.angles[i]; + newVert.X = hollow * xScale * angle.X; + newVert.Y = hollow * yScale * angle.Y; + newVert.Z = z; + + hollowCoords.Add(newVert); + if (this.calcVertexNormals) + { + if (hollowSides < 5) + hollowNormals.Add(hollowAngles.normals[i].Invert()); + else + hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); + + hollowUs.Add(angle.angle * hollow); + } + } + } + + int index = 0; + int numAngles = angles.angles.Count; + + for (int i = 0; i < numAngles; i++) + { + angle = angles.angles[i]; + newVert.X = angle.X * xScale; + newVert.Y = angle.Y * yScale; + newVert.Z = z; + this.coords.Add(newVert); + if (this.calcVertexNormals) + { + this.outerCoordIndices.Add(this.coords.Count - 1); + + if (sides < 5) + { + this.vertexNormals.Add(angles.normals[i]); + float u = angle.angle; + this.us.Add(u); + } + else + { + this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); + this.us.Add(angle.angle); + } + } + + if (hasHollow) + { + if (hollowSides == sides) + { + newVert.X *= hollow; + newVert.Y *= hollow; + newVert.Z = z; + hollowCoords.Add(newVert); + if (this.calcVertexNormals) + { + if (sides < 5) + { + hollowNormals.Add(angles.normals[i].Invert()); + } + + else + hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); + + hollowUs.Add(angle.angle * hollow); + } + } + } + else if (!simpleFace && createFaces && angle.angle > 0.0001f) + { + Face newFace = new Face(); + newFace.v1 = 0; + newFace.v2 = index; + newFace.v3 = index + 1; + + this.faces.Add(newFace); + } + index += 1; + } + + if (hasHollow) + { + hollowCoords.Reverse(); + if (this.calcVertexNormals) + { + hollowNormals.Reverse(); + hollowUs.Reverse(); + } + + if (createFaces) + { + //int numOuterVerts = this.coords.Count; + //numOuterVerts = this.coords.Count; + //int numHollowVerts = hollowCoords.Count; + int numTotalVerts = this.numOuterVerts + this.numHollowVerts; + + if (this.numOuterVerts == this.numHollowVerts) + { + Face newFace = new Face(); + + for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++) + { + newFace.v1 = coordIndex; + newFace.v2 = coordIndex + 1; + newFace.v3 = numTotalVerts - coordIndex - 1; + this.faces.Add(newFace); + + newFace.v1 = coordIndex + 1; + newFace.v2 = numTotalVerts - coordIndex - 2; + newFace.v3 = numTotalVerts - coordIndex - 1; + this.faces.Add(newFace); + } + } + else + { + if (this.numOuterVerts < this.numHollowVerts) + { + Face newFace = new Face(); + int j = 0; // j is the index for outer vertices + int maxJ = this.numOuterVerts - 1; + for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices + { + if (j < maxJ) + if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) + { + newFace.v1 = numTotalVerts - i - 1; + newFace.v2 = j; + newFace.v3 = j + 1; + + this.faces.Add(newFace); + j += 1; + } + + newFace.v1 = j; + newFace.v2 = numTotalVerts - i - 2; + newFace.v3 = numTotalVerts - i - 1; + + this.faces.Add(newFace); + } + } + else // numHollowVerts < numOuterVerts + { + Face newFace = new Face(); + int j = 0; // j is the index for inner vertices + int maxJ = this.numHollowVerts - 1; + for (int i = 0; i < this.numOuterVerts; i++) + { + if (j < maxJ) + if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) + { + newFace.v1 = i; + newFace.v2 = numTotalVerts - j - 2; + newFace.v3 = numTotalVerts - j - 1; + + this.faces.Add(newFace); + j += 1; + } + + newFace.v1 = numTotalVerts - j - 1; + newFace.v2 = i; + newFace.v3 = i + 1; + + this.faces.Add(newFace); + } + } + } + } + + if (calcVertexNormals) + { + foreach (Coord hc in hollowCoords) + { + this.coords.Add(hc); + hollowCoordIndices.Add(this.coords.Count - 1); + } + } + else + this.coords.AddRange(hollowCoords); + + if (this.calcVertexNormals) + { + this.vertexNormals.AddRange(hollowNormals); + this.us.AddRange(hollowUs); + + } + } + + if (simpleFace && createFaces) + { + if (sides == 3) + this.faces.Add(new Face(0, 1, 2)); + else if (sides == 4) + { + this.faces.Add(new Face(0, 1, 2)); + this.faces.Add(new Face(0, 2, 3)); + } + } + + if (calcVertexNormals && hasProfileCut) + { + int lastOuterVertIndex = this.numOuterVerts - 1; + + if (hasHollow) + { + this.cut1CoordIndices.Add(0); + this.cut1CoordIndices.Add(this.coords.Count - 1); + + this.cut2CoordIndices.Add(lastOuterVertIndex + 1); + this.cut2CoordIndices.Add(lastOuterVertIndex); + + this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; + this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); + + this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; + this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); + } + + else + { + this.cut1CoordIndices.Add(0); + this.cut1CoordIndices.Add(1); + + this.cut2CoordIndices.Add(lastOuterVertIndex); + this.cut2CoordIndices.Add(0); + + this.cutNormal1.X = this.vertexNormals[1].Y; + this.cutNormal1.Y = -this.vertexNormals[1].X; + + this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; + this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; + + } + this.cutNormal1.Normalize(); + this.cutNormal2.Normalize(); + } + + this.MakeFaceUVs(); + + hollowCoords = null; + hollowNormals = null; + hollowUs = null; + + if (calcVertexNormals) + { // calculate prim face numbers + + // face number order is top, outer, hollow, bottom, start cut, end cut + // I know it's ugly but so is the whole concept of prim face numbers + + int faceNum = 1; // start with outer faces + this.outerFaceNumber = faceNum; + + int startVert = hasProfileCut && !hasHollow ? 1 : 0; + if (startVert > 0) + this.faceNumbers.Add(-1); + for (int i = 0; i < this.numOuterVerts - 1; i++) + //this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); + this.faceNumbers.Add(sides < 5 && i < sides ? faceNum++ : faceNum); + + //if (!hasHollow && !hasProfileCut) + // this.bottomFaceNumber = faceNum++; + + this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); + + if (sides > 4 && (hasHollow || hasProfileCut)) + faceNum++; + + if (sides < 5 && (hasHollow || hasProfileCut) && this.numOuterVerts < sides) + faceNum++; + + if (hasHollow) + { + for (int i = 0; i < this.numHollowVerts; i++) + this.faceNumbers.Add(faceNum); + + this.hollowFaceNumber = faceNum++; + } + //if (hasProfileCut || hasHollow) + // this.bottomFaceNumber = faceNum++; + this.bottomFaceNumber = faceNum++; + + if (hasHollow && hasProfileCut) + this.faceNumbers.Add(faceNum++); + + for (int i = 0; i < this.faceNumbers.Count; i++) + if (this.faceNumbers[i] == -1) + this.faceNumbers[i] = faceNum++; + + this.numPrimFaces = faceNum; + } + + } + + internal void MakeFaceUVs() + { + this.faceUVs = new List(); + foreach (Coord c in this.coords) + this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); + } + + internal Profile Copy() + { + return this.Copy(true); + } + + internal Profile Copy(bool needFaces) + { + Profile copy = new Profile(); + + copy.coords.AddRange(this.coords); + copy.faceUVs.AddRange(this.faceUVs); + + if (needFaces) + copy.faces.AddRange(this.faces); + if ((copy.calcVertexNormals = this.calcVertexNormals) == true) + { + copy.vertexNormals.AddRange(this.vertexNormals); + copy.faceNormal = this.faceNormal; + copy.cutNormal1 = this.cutNormal1; + copy.cutNormal2 = this.cutNormal2; + copy.us.AddRange(this.us); + copy.faceNumbers.AddRange(this.faceNumbers); + + copy.cut1CoordIndices = new List(this.cut1CoordIndices); + copy.cut2CoordIndices = new List(this.cut2CoordIndices); + copy.hollowCoordIndices = new List(this.hollowCoordIndices); + copy.outerCoordIndices = new List(this.outerCoordIndices); + } + copy.numOuterVerts = this.numOuterVerts; + copy.numHollowVerts = this.numHollowVerts; + + return copy; + } + + internal void AddPos(Coord v) + { + this.AddPos(v.X, v.Y, v.Z); + } + + internal void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + } + + internal void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + if (this.calcVertexNormals) + { + int numNormals = this.vertexNormals.Count; + for (i = 0; i < numNormals; i++) + this.vertexNormals[i] *= q; + + this.faceNormal *= q; + this.cutNormal1 *= q; + this.cutNormal2 *= q; + + } + } + + internal void Scale(float x, float y) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X *= x; + vert.Y *= y; + this.coords[i] = vert; + } + } + + /// + /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices + /// + internal void FlipNormals() + { + int i; + int numFaces = this.faces.Count; + Face tmpFace; + int tmp; + + for (i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmp = tmpFace.v3; + tmpFace.v3 = tmpFace.v1; + tmpFace.v1 = tmp; + this.faces[i] = tmpFace; + } + + if (this.calcVertexNormals) + { + int normalCount = this.vertexNormals.Count; + if (normalCount > 0) + { + Coord n = this.vertexNormals[normalCount - 1]; + n.Z = -n.Z; + this.vertexNormals[normalCount - 1] = n; + } + } + + this.faceNormal.X = -this.faceNormal.X; + this.faceNormal.Y = -this.faceNormal.Y; + this.faceNormal.Z = -this.faceNormal.Z; + + int numfaceUVs = this.faceUVs.Count; + for (i = 0; i < numfaceUVs; i++) + { + UVCoord uv = this.faceUVs[i]; + uv.V = 1.0f - uv.V; + this.faceUVs[i] = uv; + } + } + + internal void AddValue2FaceVertexIndices(int num) + { + int numFaces = this.faces.Count; + Face tmpFace; + for (int i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmpFace.v1 += num; + tmpFace.v2 += num; + tmpFace.v3 += num; + + this.faces[i] = tmpFace; + } + } + + internal void AddValue2FaceNormalIndices(int num) + { + if (this.calcVertexNormals) + { + int numFaces = this.faces.Count; + Face tmpFace; + for (int i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmpFace.n1 += num; + tmpFace.n2 += num; + tmpFace.n3 += num; + + this.faces[i] = tmpFace; + } + } + } + + internal void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } + + public struct PathNode + { + public Coord position; + public Quat rotation; + public float xScale; + public float yScale; + public float percentOfPath; + } + + public enum PathType { Linear = 0, Circular = 1, Flexible = 2 } + + public class Path + { + public List pathNodes = new List(); + + public float twistBegin = 0.0f; + public float twistEnd = 0.0f; + public float topShearX = 0.0f; + public float topShearY = 0.0f; + public float pathCutBegin = 0.0f; + public float pathCutEnd = 1.0f; + public float dimpleBegin = 0.0f; + public float dimpleEnd = 1.0f; + public float skew = 0.0f; + public float holeSizeX = 1.0f; // called pathScaleX in pbs + public float holeSizeY = 0.25f; + public float taperX = 0.0f; + public float taperY = 0.0f; + public float radius = 0.0f; + public float revolutions = 1.0f; + public int stepsPerRevolution = 24; + + private const float twoPi = 2.0f * (float)Math.PI; + + public void Create(PathType pathType, int steps) + { + if (pathType == PathType.Linear || pathType == PathType.Flexible) + { + int step = 0; + + float length = this.pathCutEnd - this.pathCutBegin; + float twistTotal = twistEnd - twistBegin; + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number + + float start = -0.5f; + float stepSize = length / (float)steps; + float percentOfPathMultiplier = stepSize; + float xOffset = 0.0f; + float yOffset = 0.0f; + float zOffset = start; + float xOffsetStepIncrement = this.topShearX / steps; + float yOffsetStepIncrement = this.topShearY / steps; + + float percentOfPath = this.pathCutBegin; + zOffset += percentOfPath; + + // sanity checks + + bool done = false; + + while (!done) + { + PathNode newNode = new PathNode(); + + newNode.xScale = 1.0f; + if (this.taperX == 0.0f) + newNode.xScale = 1.0f; + else if (this.taperX > 0.0f) + newNode.xScale = 1.0f - percentOfPath * this.taperX; + else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX; + + newNode.yScale = 1.0f; + if (this.taperY == 0.0f) + newNode.yScale = 1.0f; + else if (this.taperY > 0.0f) + newNode.yScale = 1.0f - percentOfPath * this.taperY; + else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY; + + float twist = twistBegin + twistTotal * percentOfPath; + + newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); + newNode.position = new Coord(xOffset, yOffset, zOffset); + newNode.percentOfPath = percentOfPath; + + pathNodes.Add(newNode); + + if (step < steps) + { + step += 1; + percentOfPath += percentOfPathMultiplier; + xOffset += xOffsetStepIncrement; + yOffset += yOffsetStepIncrement; + zOffset += stepSize; + if (percentOfPath > this.pathCutEnd) + done = true; + } + else done = true; + } + } // end of linear path code + + else // pathType == Circular + { + float twistTotal = twistEnd - twistBegin; + + // if the profile has a lot of twist, add more layers otherwise the layers may overlap + // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't + // accurately match the viewer + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + { + if (twistTotalAbs > Math.PI * 1.5f) + steps *= 2; + if (twistTotalAbs > Math.PI * 3.0f) + steps *= 2; + } + + float yPathScale = this.holeSizeY * 0.5f; + float pathLength = this.pathCutEnd - this.pathCutBegin; + float totalSkew = this.skew * 2.0f * pathLength; + float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew; + float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY)); + float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f; + + // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end + // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used + // to calculate the sine for generating the path radius appears to approximate it's effects there + // too, but there are some subtle differences in the radius which are noticeable as the prim size + // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on + // the meshes generated with this technique appear nearly identical in shape to the same prims when + // displayed by the viewer. + + float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f; + float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f; + float stepSize = twoPi / this.stepsPerRevolution; + + int step = (int)(startAngle / stepSize); + float angle = startAngle; + + bool done = false; + while (!done) // loop through the length of the path and add the layers + { + PathNode newNode = new PathNode(); + + float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX; + float yProfileScale = this.holeSizeY; + + float percentOfPath = angle / (twoPi * this.revolutions); + float percentOfAngles = (angle - startAngle) / (endAngle - startAngle); + + if (this.taperX > 0.01f) + xProfileScale *= 1.0f - percentOfPath * this.taperX; + else if (this.taperX < -0.01f) + xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX; + + if (this.taperY > 0.01f) + yProfileScale *= 1.0f - percentOfPath * this.taperY; + else if (this.taperY < -0.01f) + yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY; + + newNode.xScale = xProfileScale; + newNode.yScale = yProfileScale; + + float radiusScale = 1.0f; + if (this.radius > 0.001f) + radiusScale = 1.0f - this.radius * percentOfPath; + else if (this.radius < 0.001f) + radiusScale = 1.0f + this.radius * (1.0f - percentOfPath); + + float twist = twistBegin + twistTotal * percentOfPath; + + float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles); + xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor; + + float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale; + + float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale; + + newNode.position = new Coord(xOffset, yOffset, zOffset); + + // now orient the rotation of the profile layer relative to it's position on the path + // adding taperY to the angle used to generate the quat appears to approximate the viewer + + newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY); + + // next apply twist rotation to the profile layer + if (twistTotal != 0.0f || twistBegin != 0.0f) + newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); + + newNode.percentOfPath = percentOfPath; + + pathNodes.Add(newNode); + + // calculate terms for next iteration + // calculate the angle for the next iteration of the loop + + if (angle >= endAngle - 0.01) + done = true; + else + { + step += 1; + angle = stepSize * step; + if (angle > endAngle) + angle = endAngle; + } + } + } + } + } + + public class PrimMesh + { + public string errorMessage = ""; + private const float twoPi = 2.0f * (float)Math.PI; + + public List coords; + public List normals; + public List faces; + + public List viewerFaces; + + private int sides = 4; + private int hollowSides = 4; + private float profileStart = 0.0f; + private float profileEnd = 1.0f; + private float hollow = 0.0f; + public int twistBegin = 0; + public int twistEnd = 0; + public float topShearX = 0.0f; + public float topShearY = 0.0f; + public float pathCutBegin = 0.0f; + public float pathCutEnd = 1.0f; + public float dimpleBegin = 0.0f; + public float dimpleEnd = 1.0f; + public float skew = 0.0f; + public float holeSizeX = 1.0f; // called pathScaleX in pbs + public float holeSizeY = 0.25f; + public float taperX = 0.0f; + public float taperY = 0.0f; + public float radius = 0.0f; + public float revolutions = 1.0f; + public int stepsPerRevolution = 24; + + private int profileOuterFaceNumber = -1; + private int profileHollowFaceNumber = -1; + + private bool hasProfileCut = false; + private bool hasHollow = false; + public bool calcVertexNormals = false; + private bool normalsProcessed = false; + public bool viewerMode = false; + public bool sphereMode = false; + + public int numPrimFaces = 0; + + /// + /// Human readable string representation of the parameters used to create a mesh. + /// + /// + public string ParamsToDisplayString() + { + string s = ""; + s += "sides..................: " + this.sides.ToString(); + s += "\nhollowSides..........: " + this.hollowSides.ToString(); + s += "\nprofileStart.........: " + this.profileStart.ToString(); + s += "\nprofileEnd...........: " + this.profileEnd.ToString(); + s += "\nhollow...............: " + this.hollow.ToString(); + s += "\ntwistBegin...........: " + this.twistBegin.ToString(); + s += "\ntwistEnd.............: " + this.twistEnd.ToString(); + s += "\ntopShearX............: " + this.topShearX.ToString(); + s += "\ntopShearY............: " + this.topShearY.ToString(); + s += "\npathCutBegin.........: " + this.pathCutBegin.ToString(); + s += "\npathCutEnd...........: " + this.pathCutEnd.ToString(); + s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString(); + s += "\ndimpleEnd............: " + this.dimpleEnd.ToString(); + s += "\nskew.................: " + this.skew.ToString(); + s += "\nholeSizeX............: " + this.holeSizeX.ToString(); + s += "\nholeSizeY............: " + this.holeSizeY.ToString(); + s += "\ntaperX...............: " + this.taperX.ToString(); + s += "\ntaperY...............: " + this.taperY.ToString(); + s += "\nradius...............: " + this.radius.ToString(); + s += "\nrevolutions..........: " + this.revolutions.ToString(); + s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); + s += "\nsphereMode...........: " + this.sphereMode.ToString(); + s += "\nhasProfileCut........: " + this.hasProfileCut.ToString(); + s += "\nhasHollow............: " + this.hasHollow.ToString(); + s += "\nviewerMode...........: " + this.viewerMode.ToString(); + + return s; + } + + public int ProfileOuterFaceNumber + { + get { return profileOuterFaceNumber; } + } + + public int ProfileHollowFaceNumber + { + get { return profileHollowFaceNumber; } + } + + public bool HasProfileCut + { + get { return hasProfileCut; } + } + + public bool HasHollow + { + get { return hasHollow; } + } + + + /// + /// Constructs a PrimMesh object and creates the profile for extrusion. + /// + /// + /// + /// + /// + /// + public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) + { + this.coords = new List(); + this.faces = new List(); + + this.sides = sides; + this.profileStart = profileStart; + this.profileEnd = profileEnd; + this.hollow = hollow; + this.hollowSides = hollowSides; + + if (sides < 3) + this.sides = 3; + if (hollowSides < 3) + this.hollowSides = 3; + if (profileStart < 0.0f) + this.profileStart = 0.0f; + if (profileEnd > 1.0f) + this.profileEnd = 1.0f; + if (profileEnd < 0.02f) + this.profileEnd = 0.02f; + if (profileStart >= profileEnd) + this.profileStart = profileEnd - 0.02f; + if (hollow > 0.99f) + this.hollow = 0.99f; + if (hollow < 0.0f) + this.hollow = 0.0f; + + //if (sphereMode) + // this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; + //else + // //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); + // this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; + //this.hasHollow = (this.hollow > 0.001f); + } + + /// + /// Extrudes a profile along a path. + /// + public void Extrude(PathType pathType) + { + bool needEndFaces = false; + + this.coords = new List(); + this.faces = new List(); + + if (this.viewerMode) + { + this.viewerFaces = new List(); + this.calcVertexNormals = true; + } + + if (this.calcVertexNormals) + this.normals = new List(); + + int steps = 1; + + float length = this.pathCutEnd - this.pathCutBegin; + normalsProcessed = false; + + if (this.viewerMode && this.sides == 3) + { + // prisms don't taper well so add some vertical resolution + // other prims may benefit from this but just do prisms for now + if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) + steps = (int)(steps * 4.5 * length); + } + + if (sphereMode) + this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f; + else + //this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); + this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f; + this.hasHollow = (this.hollow > 0.001f); + + float twistBegin = this.twistBegin / 360.0f * twoPi; + float twistEnd = this.twistEnd / 360.0f * twoPi; + float twistTotal = twistEnd - twistBegin; + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number + + float hollow = this.hollow; + + // sanity checks + float initialProfileRot = 0.0f; + if (pathType == PathType.Circular) + { + if (this.sides == 3) + { + initialProfileRot = (float)Math.PI; + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow *= 0.707f; + } + else hollow *= 0.5f; + } + else if (this.sides == 4) + { + initialProfileRot = 0.25f * (float)Math.PI; + if (this.hollowSides != 4) + hollow *= 0.707f; + } + else if (this.sides > 4) + { + initialProfileRot = (float)Math.PI; + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow /= 0.7f; + } + } + } + else + { + if (this.sides == 3) + { + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow *= 0.707f; + } + else hollow *= 0.5f; + } + else if (this.sides == 4) + { + initialProfileRot = 1.25f * (float)Math.PI; + if (this.hollowSides != 4) + hollow *= 0.707f; + } + else if (this.sides == 24 && this.hollowSides == 4) + hollow *= 1.414f; + } + + Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); + this.errorMessage = profile.errorMessage; + + this.numPrimFaces = profile.numPrimFaces; + + //profileOuterFaceNumber = profile.faceNumbers[0]; + //if (!needEndFaces) + // profileOuterFaceNumber--; + //profileOuterFaceNumber = needEndFaces ? 1 : 0; + + + //if (hasHollow) + //{ + // if (needEndFaces) + // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts + 1]; + // else + // profileHollowFaceNumber = profile.faceNumbers[profile.numOuterVerts] - 1; + //} + + + profileOuterFaceNumber = profile.outerFaceNumber; + if (!needEndFaces) + profileOuterFaceNumber--; + + if (hasHollow) + { + profileHollowFaceNumber = profile.hollowFaceNumber; + if (!needEndFaces) + profileHollowFaceNumber--; + } + + int cut1Vert = -1; + int cut2Vert = -1; + if (hasProfileCut) + { + cut1Vert = hasHollow ? profile.coords.Count - 1 : 0; + cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts; + } + + if (initialProfileRot != 0.0f) + { + profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); + if (viewerMode) + profile.MakeFaceUVs(); + } + + Coord lastCutNormal1 = new Coord(); + Coord lastCutNormal2 = new Coord(); + float lastV = 1.0f; + + Path path = new Path(); + path.twistBegin = twistBegin; + path.twistEnd = twistEnd; + path.topShearX = topShearX; + path.topShearY = topShearY; + path.pathCutBegin = pathCutBegin; + path.pathCutEnd = pathCutEnd; + path.dimpleBegin = dimpleBegin; + path.dimpleEnd = dimpleEnd; + path.skew = skew; + path.holeSizeX = holeSizeX; + path.holeSizeY = holeSizeY; + path.taperX = taperX; + path.taperY = taperY; + path.radius = radius; + path.revolutions = revolutions; + path.stepsPerRevolution = stepsPerRevolution; + + path.Create(pathType, steps); + + + if (pathType == PathType.Circular) + { + needEndFaces = false; + if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f) + needEndFaces = true; + else if (this.taperX != 0.0f || this.taperY != 0.0f) + needEndFaces = true; + else if (this.skew != 0.0f) + needEndFaces = true; + else if (twistTotal != 0.0f) + needEndFaces = true; + else if (this.radius != 0.0f) + needEndFaces = true; + } + else needEndFaces = true; + + for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) + { + PathNode node = path.pathNodes[nodeIndex]; + Profile newLayer = profile.Copy(); + newLayer.Scale(node.xScale, node.yScale); + + newLayer.AddRot(node.rotation); + newLayer.AddPos(node.position); + + if (needEndFaces && nodeIndex == 0) + { + newLayer.FlipNormals(); + + // add the top faces to the viewerFaces list here + if (this.viewerMode) + { + Coord faceNormal = newLayer.faceNormal; + ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber); + int numFaces = newLayer.faces.Count; + List faces = newLayer.faces; + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + newViewerFace.v1 = newLayer.coords[face.v1]; + newViewerFace.v2 = newLayer.coords[face.v2]; + newViewerFace.v3 = newLayer.coords[face.v3]; + + newViewerFace.coordIndex1 = face.v1; + newViewerFace.coordIndex2 = face.v2; + newViewerFace.coordIndex3 = face.v3; + + newViewerFace.n1 = faceNormal; + newViewerFace.n2 = faceNormal; + newViewerFace.n3 = faceNormal; + + newViewerFace.uv1 = newLayer.faceUVs[face.v1]; + newViewerFace.uv2 = newLayer.faceUVs[face.v2]; + newViewerFace.uv3 = newLayer.faceUVs[face.v3]; + + this.viewerFaces.Add(newViewerFace); + } + } + } // if (nodeIndex == 0) + + // append this layer + + int coordsLen = this.coords.Count; + newLayer.AddValue2FaceVertexIndices(coordsLen); + + this.coords.AddRange(newLayer.coords); + + if (this.calcVertexNormals) + { + newLayer.AddValue2FaceNormalIndices(this.normals.Count); + this.normals.AddRange(newLayer.vertexNormals); + } + + if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f) + this.faces.AddRange(newLayer.faces); + + // fill faces between layers + + int numVerts = newLayer.coords.Count; + Face newFace = new Face(); + + if (nodeIndex > 0) + { + int startVert = coordsLen + 1; + int endVert = this.coords.Count; + + if (sides < 5 || this.hasProfileCut || this.hasHollow) + startVert--; + + for (int i = startVert; i < endVert; i++) + { + int iNext = i + 1; + if (i == endVert - 1) + iNext = startVert; + + int whichVert = i - startVert; + + newFace.v1 = i; + newFace.v2 = i - numVerts; + newFace.v3 = iNext - numVerts; + this.faces.Add(newFace); + + newFace.v2 = iNext - numVerts; + newFace.v3 = iNext; + this.faces.Add(newFace); + + if (this.viewerMode) + { + // add the side faces to the list of viewerFaces here + + int primFaceNum = profile.faceNumbers[whichVert]; + if (!needEndFaces) + primFaceNum -= 1; + + ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); + ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); + + float u1 = newLayer.us[whichVert]; + float u2 = 1.0f; + if (whichVert < newLayer.us.Count - 1) + u2 = newLayer.us[whichVert + 1]; + + if (whichVert == cut1Vert || whichVert == cut2Vert) + { + u1 = 0.0f; + u2 = 1.0f; + } + else if (sides < 5) + { + if (whichVert < profile.numOuterVerts) + { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled + // to reflect the entire texture width + u1 *= sides; + u2 *= sides; + u2 -= (int)u1; + u1 -= (int)u1; + if (u2 < 0.1f) + u2 = 1.0f; + //this.profileOuterFaceNumber = primFaceNum; + } + else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) + { + u1 *= 2.0f; + u2 *= 2.0f; + //this.profileHollowFaceNumber = primFaceNum; + } + } + + newViewerFace1.uv1.U = u1; + newViewerFace1.uv2.U = u1; + newViewerFace1.uv3.U = u2; + + newViewerFace1.uv1.V = 1.0f - node.percentOfPath; + newViewerFace1.uv2.V = lastV; + newViewerFace1.uv3.V = lastV; + + newViewerFace2.uv1.U = u1; + newViewerFace2.uv2.U = u2; + newViewerFace2.uv3.U = u2; + + newViewerFace2.uv1.V = 1.0f - node.percentOfPath; + newViewerFace2.uv2.V = lastV; + newViewerFace2.uv3.V = 1.0f - node.percentOfPath; + + newViewerFace1.v1 = this.coords[i]; + newViewerFace1.v2 = this.coords[i - numVerts]; + newViewerFace1.v3 = this.coords[iNext - numVerts]; + + newViewerFace2.v1 = this.coords[i]; + newViewerFace2.v2 = this.coords[iNext - numVerts]; + newViewerFace2.v3 = this.coords[iNext]; + + newViewerFace1.coordIndex1 = i; + newViewerFace1.coordIndex2 = i - numVerts; + newViewerFace1.coordIndex3 = iNext - numVerts; + + newViewerFace2.coordIndex1 = i; + newViewerFace2.coordIndex2 = iNext - numVerts; + newViewerFace2.coordIndex3 = iNext; + + // profile cut faces + if (whichVert == cut1Vert) + { + newViewerFace1.n1 = newLayer.cutNormal1; + newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; + + newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; + newViewerFace2.n2 = lastCutNormal1; + } + else if (whichVert == cut2Vert) + { + newViewerFace1.n1 = newLayer.cutNormal2; + newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; + + newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; + newViewerFace2.n2 = lastCutNormal2; + } + + else // outer and hollow faces + { + if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) + { // looks terrible when path is twisted... need vertex normals here + newViewerFace1.CalcSurfaceNormal(); + newViewerFace2.CalcSurfaceNormal(); + } + else + { + newViewerFace1.n1 = this.normals[i]; + newViewerFace1.n2 = this.normals[i - numVerts]; + newViewerFace1.n3 = this.normals[iNext - numVerts]; + + newViewerFace2.n1 = this.normals[i]; + newViewerFace2.n2 = this.normals[iNext - numVerts]; + newViewerFace2.n3 = this.normals[iNext]; + } + } + + this.viewerFaces.Add(newViewerFace1); + this.viewerFaces.Add(newViewerFace2); + + } + } + } + + lastCutNormal1 = newLayer.cutNormal1; + lastCutNormal2 = newLayer.cutNormal2; + lastV = 1.0f - node.percentOfPath; + + if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) + { + // add the top faces to the viewerFaces list here + Coord faceNormal = newLayer.faceNormal; + ViewerFace newViewerFace = new ViewerFace(); + newViewerFace.primFaceNumber = 0; + int numFaces = newLayer.faces.Count; + List faces = newLayer.faces; + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; + newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; + newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; + + newViewerFace.coordIndex1 = face.v1 - coordsLen; + newViewerFace.coordIndex2 = face.v2 - coordsLen; + newViewerFace.coordIndex3 = face.v3 - coordsLen; + + newViewerFace.n1 = faceNormal; + newViewerFace.n2 = faceNormal; + newViewerFace.n3 = faceNormal; + + newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen]; + newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; + newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; + + this.viewerFaces.Add(newViewerFace); + } + } + + + } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) + + } + + + /// + /// DEPRICATED - use Extrude(PathType.Linear) instead + /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. + /// + /// + public void ExtrudeLinear() + { + this.Extrude(PathType.Linear); + } + + + /// + /// DEPRICATED - use Extrude(PathType.Circular) instead + /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. + /// + /// + public void ExtrudeCircular() + { + this.Extrude(PathType.Circular); + } + + + private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3) + { + Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + Coord normal = Coord.Cross(edge1, edge2); + + normal.Normalize(); + + return normal; + } + + private Coord SurfaceNormal(Face face) + { + return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); + } + + /// + /// Calculate the surface normal for a face in the list of faces + /// + /// + /// + public Coord SurfaceNormal(int faceIndex) + { + int numFaces = this.faces.Count; + if (faceIndex < 0 || faceIndex >= numFaces) + throw new Exception("faceIndex out of range"); + + return SurfaceNormal(this.faces[faceIndex]); + } + + /// + /// Duplicates a PrimMesh object. All object properties are copied by value, including lists. + /// + /// + public PrimMesh Copy() + { + PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides); + copy.twistBegin = this.twistBegin; + copy.twistEnd = this.twistEnd; + copy.topShearX = this.topShearX; + copy.topShearY = this.topShearY; + copy.pathCutBegin = this.pathCutBegin; + copy.pathCutEnd = this.pathCutEnd; + copy.dimpleBegin = this.dimpleBegin; + copy.dimpleEnd = this.dimpleEnd; + copy.skew = this.skew; + copy.holeSizeX = this.holeSizeX; + copy.holeSizeY = this.holeSizeY; + copy.taperX = this.taperX; + copy.taperY = this.taperY; + copy.radius = this.radius; + copy.revolutions = this.revolutions; + copy.stepsPerRevolution = this.stepsPerRevolution; + copy.calcVertexNormals = this.calcVertexNormals; + copy.normalsProcessed = this.normalsProcessed; + copy.viewerMode = this.viewerMode; + copy.numPrimFaces = this.numPrimFaces; + copy.errorMessage = this.errorMessage; + + copy.coords = new List(this.coords); + copy.faces = new List(this.faces); + copy.viewerFaces = new List(this.viewerFaces); + copy.normals = new List(this.normals); + + return copy; + } + + /// + /// Calculate surface normals for all of the faces in the list of faces in this mesh + /// + public void CalcNormals() + { + if (normalsProcessed) + return; + + normalsProcessed = true; + + int numFaces = faces.Count; + + if (!this.calcVertexNormals) + this.normals = new List(); + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + + this.normals.Add(SurfaceNormal(i).Normalize()); + + int normIndex = normals.Count - 1; + face.n1 = normIndex; + face.n2 = normIndex; + face.n3 = normIndex; + + this.faces[i] = face; + } + } + + /// + /// Adds a value to each XYZ vertex coordinate in the mesh + /// + /// + /// + /// + public void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.AddPos(x, y, z); + this.viewerFaces[i] = v; + } + } + } + + /// + /// Rotates the mesh + /// + /// + public void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + if (this.normals != null) + { + int numNormals = this.normals.Count; + for (i = 0; i < numNormals; i++) + this.normals[i] *= q; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= q; + v.v2 *= q; + v.v3 *= q; + + v.n1 *= q; + v.n2 *= q; + v.n3 *= q; + this.viewerFaces[i] = v; + } + } + } + +#if VERTEX_INDEXER + public VertexIndexer GetVertexIndexer() + { + if (this.viewerMode && this.viewerFaces.Count > 0) + return new VertexIndexer(this); + return null; + } +#endif + + /// + /// Scales the mesh + /// + /// + /// + /// + public void Scale(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + //Coord vert; + + Coord m = new Coord(x, y, z); + for (i = 0; i < numVerts; i++) + this.coords[i] *= m; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= m; + v.v2 *= m; + v.v3 *= m; + this.viewerFaces[i] = v; + } + + } + + } + + /// + /// Dumps the mesh to a Blender compatible "Raw" format file + /// + /// + /// + /// + public void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } +} diff --git a/OpenSim/Region/Physics/Meshing/SculptMap.cs b/OpenSim/Region/Physics/Meshing/SculptMap.cs index 4906cf6a33..d2d71deade 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMap.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMap.cs @@ -1,176 +1,176 @@ -/* - * Copyright (c) Contributors - * 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 OpenSimulator 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. - */ - -// to build without references to System.Drawing, comment this out -#define SYSTEM_DRAWING - -using System; -using System.Collections.Generic; -using System.Text; - -#if SYSTEM_DRAWING -using System.Drawing; -using System.Drawing.Imaging; - -namespace PrimMesher -{ - public class SculptMap - { - public int width; - public int height; - public byte[] redBytes; - public byte[] greenBytes; - public byte[] blueBytes; - - public SculptMap() - { - } - - public SculptMap(Bitmap bm, int lod) - { - int bmW = bm.Width; - int bmH = bm.Height; - - if (bmW == 0 || bmH == 0) - throw new Exception("SculptMap: bitmap has no data"); - - int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image - - bool needsScaling = false; - - width = bmW; - height = bmH; - while (width * height > numLodPixels) - { - width >>= 1; - height >>= 1; - needsScaling = true; - } - - - - try - { - if (needsScaling) - bm = ScaleImage(bm, width, height, - System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor); - } - - catch (Exception e) - { - throw new Exception("Exception in ScaleImage(): e: " + e.ToString()); - } - - if (width * height > lod * lod) - { - width >>= 1; - height >>= 1; - } - - int numBytes = (width + 1) * (height + 1); - redBytes = new byte[numBytes]; - greenBytes = new byte[numBytes]; - blueBytes = new byte[numBytes]; - - int byteNdx = 0; - - try - { - for (int y = 0; y <= height; y++) - { - for (int x = 0; x <= width; x++) - { - int bmY = y < height ? y * 2 : y * 2 - 1; - int bmX = x < width ? x * 2 : x * 2 - 1; - Color c = bm.GetPixel(bmX, bmY); - - redBytes[byteNdx] = c.R; - greenBytes[byteNdx] = c.G; - blueBytes[byteNdx] = c.B; - - ++byteNdx; - } - } - } - catch (Exception e) - { - throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString()); - } - - width++; - height++; - } - - public List> ToRows(bool mirror) - { - int numRows = height; - int numCols = width; - - List> rows = new List>(numRows); - - float pixScale = 1.0f / 255; - - int rowNdx, colNdx; - int smNdx = 0; - - for (rowNdx = 0; rowNdx < numRows; rowNdx++) - { - List row = new List(numCols); - for (colNdx = 0; colNdx < numCols; colNdx++) - { - if (mirror) - row.Add(new Coord(-(redBytes[smNdx] * pixScale - 0.5f), (greenBytes[smNdx] * pixScale - 0.5f), blueBytes[smNdx] * pixScale - 0.5f)); - else - row.Add(new Coord(redBytes[smNdx] * pixScale - 0.5f, greenBytes[smNdx] * pixScale - 0.5f, blueBytes[smNdx] * pixScale - 0.5f)); - - ++smNdx; - } - rows.Add(row); - } - return rows; - } - - private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight, - System.Drawing.Drawing2D.InterpolationMode interpMode) - { - Bitmap scaledImage = new Bitmap(srcImage, destWidth, destHeight); - scaledImage.SetResolution(96.0f, 96.0f); - - Graphics grPhoto = Graphics.FromImage(scaledImage); - grPhoto.InterpolationMode = interpMode; - - grPhoto.DrawImage(srcImage, - new Rectangle(0, 0, destWidth, destHeight), - new Rectangle(0, 0, srcImage.Width, srcImage.Height), - GraphicsUnit.Pixel); - - grPhoto.Dispose(); - return scaledImage; - } - } -} -#endif +/* + * Copyright (c) Contributors + * 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 OpenSimulator 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. + */ + +// to build without references to System.Drawing, comment this out +#define SYSTEM_DRAWING + +using System; +using System.Collections.Generic; +using System.Text; + +#if SYSTEM_DRAWING +using System.Drawing; +using System.Drawing.Imaging; + +namespace PrimMesher +{ + public class SculptMap + { + public int width; + public int height; + public byte[] redBytes; + public byte[] greenBytes; + public byte[] blueBytes; + + public SculptMap() + { + } + + public SculptMap(Bitmap bm, int lod) + { + int bmW = bm.Width; + int bmH = bm.Height; + + if (bmW == 0 || bmH == 0) + throw new Exception("SculptMap: bitmap has no data"); + + int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image + + bool needsScaling = false; + + width = bmW; + height = bmH; + while (width * height > numLodPixels) + { + width >>= 1; + height >>= 1; + needsScaling = true; + } + + + + try + { + if (needsScaling) + bm = ScaleImage(bm, width, height, + System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor); + } + + catch (Exception e) + { + throw new Exception("Exception in ScaleImage(): e: " + e.ToString()); + } + + if (width * height > lod * lod) + { + width >>= 1; + height >>= 1; + } + + int numBytes = (width + 1) * (height + 1); + redBytes = new byte[numBytes]; + greenBytes = new byte[numBytes]; + blueBytes = new byte[numBytes]; + + int byteNdx = 0; + + try + { + for (int y = 0; y <= height; y++) + { + for (int x = 0; x <= width; x++) + { + int bmY = y < height ? y * 2 : y * 2 - 1; + int bmX = x < width ? x * 2 : x * 2 - 1; + Color c = bm.GetPixel(bmX, bmY); + + redBytes[byteNdx] = c.R; + greenBytes[byteNdx] = c.G; + blueBytes[byteNdx] = c.B; + + ++byteNdx; + } + } + } + catch (Exception e) + { + throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString()); + } + + width++; + height++; + } + + public List> ToRows(bool mirror) + { + int numRows = height; + int numCols = width; + + List> rows = new List>(numRows); + + float pixScale = 1.0f / 255; + + int rowNdx, colNdx; + int smNdx = 0; + + for (rowNdx = 0; rowNdx < numRows; rowNdx++) + { + List row = new List(numCols); + for (colNdx = 0; colNdx < numCols; colNdx++) + { + if (mirror) + row.Add(new Coord(-(redBytes[smNdx] * pixScale - 0.5f), (greenBytes[smNdx] * pixScale - 0.5f), blueBytes[smNdx] * pixScale - 0.5f)); + else + row.Add(new Coord(redBytes[smNdx] * pixScale - 0.5f, greenBytes[smNdx] * pixScale - 0.5f, blueBytes[smNdx] * pixScale - 0.5f)); + + ++smNdx; + } + rows.Add(row); + } + return rows; + } + + private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight, + System.Drawing.Drawing2D.InterpolationMode interpMode) + { + Bitmap scaledImage = new Bitmap(srcImage, destWidth, destHeight); + scaledImage.SetResolution(96.0f, 96.0f); + + Graphics grPhoto = Graphics.FromImage(scaledImage); + grPhoto.InterpolationMode = interpMode; + + grPhoto.DrawImage(srcImage, + new Rectangle(0, 0, destWidth, destHeight), + new Rectangle(0, 0, srcImage.Width, srcImage.Height), + GraphicsUnit.Pixel); + + grPhoto.Dispose(); + return scaledImage; + } + } +} +#endif diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs index 06606c39db..6aa8fe49a3 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs @@ -1,647 +1,647 @@ -/* - * Copyright (c) Contributors - * 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 OpenSimulator 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. - */ - -// to build without references to System.Drawing, comment this out -#define SYSTEM_DRAWING - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -#if SYSTEM_DRAWING -using System.Drawing; -using System.Drawing.Imaging; -#endif - -namespace PrimMesher -{ - - public class SculptMesh - { - public List coords; - public List faces; - - public List viewerFaces; - public List normals; - public List uvs; - - public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; - -#if SYSTEM_DRAWING - - public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) - { - Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); - SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); - bitmap.Dispose(); - return sculptMesh; - } - - - public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) - { - Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); - _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); - bitmap.Dispose(); - } -#endif - - /// - /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications - /// Construct a sculpt mesh from a 2D array of floats - /// - /// - /// - /// - /// - /// - /// - public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) - { - float xStep, yStep; - float uStep, vStep; - - int numYElements = zMap.GetLength(0); - int numXElements = zMap.GetLength(1); - - try - { - xStep = (xEnd - xBegin) / (float)(numXElements - 1); - yStep = (yEnd - yBegin) / (float)(numYElements - 1); - - uStep = 1.0f / (numXElements - 1); - vStep = 1.0f / (numYElements - 1); - } - catch (DivideByZeroException) - { - return; - } - - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - viewerFaces = new List(); - - int p1, p2, p3, p4; - - int x, y; - int xStart = 0, yStart = 0; - - for (y = yStart; y < numYElements; y++) - { - int rowOffset = y * numXElements; - - for (x = xStart; x < numXElements; x++) - { - /* - * p1-----p2 - * | \ f2 | - * | \ | - * | f1 \| - * p3-----p4 - */ - - p4 = rowOffset + x; - p3 = p4 - 1; - - p2 = p4 - numXElements; - p1 = p3 - numXElements; - - Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); - this.coords.Add(c); - if (viewerMode) - { - this.normals.Add(new Coord()); - this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); - } - - if (y > 0 && x > 0) - { - Face f1, f2; - - if (viewerMode) - { - f1 = new Face(p1, p4, p3, p1, p4, p3); - f1.uv1 = p1; - f1.uv2 = p4; - f1.uv3 = p3; - - f2 = new Face(p1, p2, p4, p1, p2, p4); - f2.uv1 = p1; - f2.uv2 = p2; - f2.uv3 = p4; - } - else - { - f1 = new Face(p1, p4, p3); - f2 = new Face(p1, p2, p4); - } - - this.faces.Add(f1); - this.faces.Add(f2); - } - } - } - - if (viewerMode) - calcVertexNormals(SculptType.plane, numXElements, numYElements); - } - -#if SYSTEM_DRAWING - public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) - { - _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); - } - - public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); - } -#endif - - public SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(rows, sculptType, viewerMode, mirror, invert); - } - -#if SYSTEM_DRAWING - /// - /// converts a bitmap to a list of lists of coords, while scaling the image. - /// the scaling is done in floating point so as to allow for reduced vertex position - /// quantization as the position will be averaged between pixel values. this routine will - /// likely fail if the bitmap width and height are not powers of 2. - /// - /// - /// - /// - /// - private List> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) - { - int numRows = bitmap.Height / scale; - int numCols = bitmap.Width / scale; - List> rows = new List>(numRows); - - float pixScale = 1.0f / (scale * scale); - pixScale /= 255; - - int imageX, imageY = 0; - - int rowNdx, colNdx; - - for (rowNdx = 0; rowNdx < numRows; rowNdx++) - { - List row = new List(numCols); - for (colNdx = 0; colNdx < numCols; colNdx++) - { - imageX = colNdx * scale; - int imageYStart = rowNdx * scale; - int imageYEnd = imageYStart + scale; - int imageXEnd = imageX + scale; - float rSum = 0.0f; - float gSum = 0.0f; - float bSum = 0.0f; - for (; imageX < imageXEnd; imageX++) - { - for (imageY = imageYStart; imageY < imageYEnd; imageY++) - { - Color c = bitmap.GetPixel(imageX, imageY); - if (c.A != 255) - { - bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); - c = bitmap.GetPixel(imageX, imageY); - } - rSum += c.R; - gSum += c.G; - bSum += c.B; - } - } - if (mirror) - row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); - else - row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); - - } - rows.Add(row); - } - return rows; - } - - private List> bitmap2CoordsSampled(Bitmap bitmap, int scale, bool mirror) - { - int numRows = bitmap.Height / scale; - int numCols = bitmap.Width / scale; - List> rows = new List>(numRows); - - float pixScale = 1.0f / 256.0f; - - int imageX, imageY = 0; - - int rowNdx, colNdx; - - for (rowNdx = 0; rowNdx <= numRows; rowNdx++) - { - List row = new List(numCols); - imageY = rowNdx * scale; - if (rowNdx == numRows) imageY--; - for (colNdx = 0; colNdx <= numCols; colNdx++) - { - imageX = colNdx * scale; - if (colNdx == numCols) imageX--; - - Color c = bitmap.GetPixel(imageX, imageY); - if (c.A != 255) - { - bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); - c = bitmap.GetPixel(imageX, imageY); - } - - if (mirror) - row.Add(new Coord(-(c.R * pixScale - 0.5f), c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); - else - row.Add(new Coord(c.R * pixScale - 0.5f, c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); - - } - rows.Add(row); - } - return rows; - } - - - void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert); - } -#endif - - void _SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) - { - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - sculptType = (SculptType)(((int)sculptType) & 0x07); - - if (mirror) - if (sculptType == SculptType.plane) - invert = !invert; - - viewerFaces = new List(); - - int width = rows[0].Count; - - int p1, p2, p3, p4; - - int imageX, imageY; - - if (sculptType != SculptType.plane) - { - if (rows.Count % 2 == 0) - { - for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) - rows[rowNdx].Add(rows[rowNdx][0]); - } - else - { - int lastIndex = rows[0].Count - 1; - - for (int i = 0; i < rows.Count; i++) - rows[i][0] = rows[i][lastIndex]; - } - } - - Coord topPole = rows[0][width / 2]; - Coord bottomPole = rows[rows.Count - 1][width / 2]; - - if (sculptType == SculptType.sphere) - { - if (rows.Count % 2 == 0) - { - int count = rows[0].Count; - List topPoleRow = new List(count); - List bottomPoleRow = new List(count); - - for (int i = 0; i < count; i++) - { - topPoleRow.Add(topPole); - bottomPoleRow.Add(bottomPole); - } - rows.Insert(0, topPoleRow); - rows.Add(bottomPoleRow); - } - else - { - int count = rows[0].Count; - - List topPoleRow = rows[0]; - List bottomPoleRow = rows[rows.Count - 1]; - - for (int i = 0; i < count; i++) - { - topPoleRow[i] = topPole; - bottomPoleRow[i] = bottomPole; - } - } - } - - if (sculptType == SculptType.torus) - rows.Add(rows[0]); - - int coordsDown = rows.Count; - int coordsAcross = rows[0].Count; - int lastColumn = coordsAcross - 1; - - float widthUnit = 1.0f / (coordsAcross - 1); - float heightUnit = 1.0f / (coordsDown - 1); - - for (imageY = 0; imageY < coordsDown; imageY++) - { - int rowOffset = imageY * coordsAcross; - - for (imageX = 0; imageX < coordsAcross; imageX++) - { +/* + * Copyright (c) Contributors + * 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 OpenSimulator 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. + */ + +// to build without references to System.Drawing, comment this out +#define SYSTEM_DRAWING + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +#if SYSTEM_DRAWING +using System.Drawing; +using System.Drawing.Imaging; +#endif + +namespace PrimMesher +{ + + public class SculptMesh + { + public List coords; + public List faces; + + public List viewerFaces; + public List normals; + public List uvs; + + public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; + +#if SYSTEM_DRAWING + + public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) + { + Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); + SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); + bitmap.Dispose(); + return sculptMesh; + } + + + public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) + { + Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); + _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); + bitmap.Dispose(); + } +#endif + + /// + /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications + /// Construct a sculpt mesh from a 2D array of floats + /// + /// + /// + /// + /// + /// + /// + public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) + { + float xStep, yStep; + float uStep, vStep; + + int numYElements = zMap.GetLength(0); + int numXElements = zMap.GetLength(1); + + try + { + xStep = (xEnd - xBegin) / (float)(numXElements - 1); + yStep = (yEnd - yBegin) / (float)(numYElements - 1); + + uStep = 1.0f / (numXElements - 1); + vStep = 1.0f / (numYElements - 1); + } + catch (DivideByZeroException) + { + return; + } + + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + viewerFaces = new List(); + + int p1, p2, p3, p4; + + int x, y; + int xStart = 0, yStart = 0; + + for (y = yStart; y < numYElements; y++) + { + int rowOffset = y * numXElements; + + for (x = xStart; x < numXElements; x++) + { /* * p1-----p2 * | \ f2 | * | \ | * | f1 \| * p3-----p4 - */ - - p4 = rowOffset + imageX; - p3 = p4 - 1; - - p2 = p4 - coordsAcross; - p1 = p3 - coordsAcross; - - this.coords.Add(rows[imageY][imageX]); - if (viewerMode) - { - this.normals.Add(new Coord()); - this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); - } - - if (imageY > 0 && imageX > 0) - { - Face f1, f2; - - if (viewerMode) - { - if (invert) - { - f1 = new Face(p1, p4, p3, p1, p4, p3); - f1.uv1 = p1; - f1.uv2 = p4; - f1.uv3 = p3; - - f2 = new Face(p1, p2, p4, p1, p2, p4); - f2.uv1 = p1; - f2.uv2 = p2; - f2.uv3 = p4; - } - else - { - f1 = new Face(p1, p3, p4, p1, p3, p4); - f1.uv1 = p1; - f1.uv2 = p3; - f1.uv3 = p4; - - f2 = new Face(p1, p4, p2, p1, p4, p2); - f2.uv1 = p1; - f2.uv2 = p4; - f2.uv3 = p2; - } - } - else - { - if (invert) - { - f1 = new Face(p1, p4, p3); - f2 = new Face(p1, p2, p4); - } - else - { - f1 = new Face(p1, p3, p4); - f2 = new Face(p1, p4, p2); - } - } - - this.faces.Add(f1); - this.faces.Add(f2); - } - } - } - - if (viewerMode) - calcVertexNormals(sculptType, coordsAcross, coordsDown); - } - - /// - /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. - /// - /// - public SculptMesh Copy() - { - return new SculptMesh(this); - } - - public SculptMesh(SculptMesh sm) - { - coords = new List(sm.coords); - faces = new List(sm.faces); - viewerFaces = new List(sm.viewerFaces); - normals = new List(sm.normals); - uvs = new List(sm.uvs); - } - - private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) - { // compute vertex normals by summing all the surface normals of all the triangles sharing - // each vertex and then normalizing - int numFaces = this.faces.Count; - for (int i = 0; i < numFaces; i++) - { - Face face = this.faces[i]; - Coord surfaceNormal = face.SurfaceNormal(this.coords); - this.normals[face.n1] += surfaceNormal; - this.normals[face.n2] += surfaceNormal; - this.normals[face.n3] += surfaceNormal; - } - - int numNormals = this.normals.Count; - for (int i = 0; i < numNormals; i++) - this.normals[i] = this.normals[i].Normalize(); - - if (sculptType != SculptType.plane) - { // blend the vertex normals at the cylinder seam - for (int y = 0; y < ySize; y++) - { - int rowOffset = y * xSize; - - this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); - } - } - - foreach (Face face in this.faces) - { - ViewerFace vf = new ViewerFace(0); - vf.v1 = this.coords[face.v1]; - vf.v2 = this.coords[face.v2]; - vf.v3 = this.coords[face.v3]; - - vf.coordIndex1 = face.v1; - vf.coordIndex2 = face.v2; - vf.coordIndex3 = face.v3; - - vf.n1 = this.normals[face.n1]; - vf.n2 = this.normals[face.n2]; - vf.n3 = this.normals[face.n3]; - - vf.uv1 = this.uvs[face.uv1]; - vf.uv2 = this.uvs[face.uv2]; - vf.uv3 = this.uvs[face.uv3]; - - this.viewerFaces.Add(vf); - } - } - - /// - /// Adds a value to each XYZ vertex coordinate in the mesh - /// - /// - /// - /// - public void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.AddPos(x, y, z); - this.viewerFaces[i] = v; - } - } - } - - /// - /// Rotates the mesh - /// - /// - public void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - int numNormals = this.normals.Count; - for (i = 0; i < numNormals; i++) - this.normals[i] *= q; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= q; - v.v2 *= q; - v.v3 *= q; - - v.n1 *= q; - v.n2 *= q; - v.n3 *= q; - - this.viewerFaces[i] = v; - } - } - } - - public void Scale(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - - Coord m = new Coord(x, y, z); - for (i = 0; i < numVerts; i++) - this.coords[i] *= m; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= m; - v.v2 *= m; - v.v3 *= m; - this.viewerFaces[i] = v; - } - } - } - - public void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } -} + */ + + p4 = rowOffset + x; + p3 = p4 - 1; + + p2 = p4 - numXElements; + p1 = p3 - numXElements; + + Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); + this.coords.Add(c); + if (viewerMode) + { + this.normals.Add(new Coord()); + this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); + } + + if (y > 0 && x > 0) + { + Face f1, f2; + + if (viewerMode) + { + f1 = new Face(p1, p4, p3, p1, p4, p3); + f1.uv1 = p1; + f1.uv2 = p4; + f1.uv3 = p3; + + f2 = new Face(p1, p2, p4, p1, p2, p4); + f2.uv1 = p1; + f2.uv2 = p2; + f2.uv3 = p4; + } + else + { + f1 = new Face(p1, p4, p3); + f2 = new Face(p1, p2, p4); + } + + this.faces.Add(f1); + this.faces.Add(f2); + } + } + } + + if (viewerMode) + calcVertexNormals(SculptType.plane, numXElements, numYElements); + } + +#if SYSTEM_DRAWING + public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) + { + _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); + } + + public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); + } +#endif + + public SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(rows, sculptType, viewerMode, mirror, invert); + } + +#if SYSTEM_DRAWING + /// + /// converts a bitmap to a list of lists of coords, while scaling the image. + /// the scaling is done in floating point so as to allow for reduced vertex position + /// quantization as the position will be averaged between pixel values. this routine will + /// likely fail if the bitmap width and height are not powers of 2. + /// + /// + /// + /// + /// + private List> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) + { + int numRows = bitmap.Height / scale; + int numCols = bitmap.Width / scale; + List> rows = new List>(numRows); + + float pixScale = 1.0f / (scale * scale); + pixScale /= 255; + + int imageX, imageY = 0; + + int rowNdx, colNdx; + + for (rowNdx = 0; rowNdx < numRows; rowNdx++) + { + List row = new List(numCols); + for (colNdx = 0; colNdx < numCols; colNdx++) + { + imageX = colNdx * scale; + int imageYStart = rowNdx * scale; + int imageYEnd = imageYStart + scale; + int imageXEnd = imageX + scale; + float rSum = 0.0f; + float gSum = 0.0f; + float bSum = 0.0f; + for (; imageX < imageXEnd; imageX++) + { + for (imageY = imageYStart; imageY < imageYEnd; imageY++) + { + Color c = bitmap.GetPixel(imageX, imageY); + if (c.A != 255) + { + bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); + c = bitmap.GetPixel(imageX, imageY); + } + rSum += c.R; + gSum += c.G; + bSum += c.B; + } + } + if (mirror) + row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); + else + row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); + + } + rows.Add(row); + } + return rows; + } + + private List> bitmap2CoordsSampled(Bitmap bitmap, int scale, bool mirror) + { + int numRows = bitmap.Height / scale; + int numCols = bitmap.Width / scale; + List> rows = new List>(numRows); + + float pixScale = 1.0f / 256.0f; + + int imageX, imageY = 0; + + int rowNdx, colNdx; + + for (rowNdx = 0; rowNdx <= numRows; rowNdx++) + { + List row = new List(numCols); + imageY = rowNdx * scale; + if (rowNdx == numRows) imageY--; + for (colNdx = 0; colNdx <= numCols; colNdx++) + { + imageX = colNdx * scale; + if (colNdx == numCols) imageX--; + + Color c = bitmap.GetPixel(imageX, imageY); + if (c.A != 255) + { + bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); + c = bitmap.GetPixel(imageX, imageY); + } + + if (mirror) + row.Add(new Coord(-(c.R * pixScale - 0.5f), c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); + else + row.Add(new Coord(c.R * pixScale - 0.5f, c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); + + } + rows.Add(row); + } + return rows; + } + + + void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert); + } +#endif + + void _SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) + { + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + sculptType = (SculptType)(((int)sculptType) & 0x07); + + if (mirror) + if (sculptType == SculptType.plane) + invert = !invert; + + viewerFaces = new List(); + + int width = rows[0].Count; + + int p1, p2, p3, p4; + + int imageX, imageY; + + if (sculptType != SculptType.plane) + { + if (rows.Count % 2 == 0) + { + for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) + rows[rowNdx].Add(rows[rowNdx][0]); + } + else + { + int lastIndex = rows[0].Count - 1; + + for (int i = 0; i < rows.Count; i++) + rows[i][0] = rows[i][lastIndex]; + } + } + + Coord topPole = rows[0][width / 2]; + Coord bottomPole = rows[rows.Count - 1][width / 2]; + + if (sculptType == SculptType.sphere) + { + if (rows.Count % 2 == 0) + { + int count = rows[0].Count; + List topPoleRow = new List(count); + List bottomPoleRow = new List(count); + + for (int i = 0; i < count; i++) + { + topPoleRow.Add(topPole); + bottomPoleRow.Add(bottomPole); + } + rows.Insert(0, topPoleRow); + rows.Add(bottomPoleRow); + } + else + { + int count = rows[0].Count; + + List topPoleRow = rows[0]; + List bottomPoleRow = rows[rows.Count - 1]; + + for (int i = 0; i < count; i++) + { + topPoleRow[i] = topPole; + bottomPoleRow[i] = bottomPole; + } + } + } + + if (sculptType == SculptType.torus) + rows.Add(rows[0]); + + int coordsDown = rows.Count; + int coordsAcross = rows[0].Count; + int lastColumn = coordsAcross - 1; + + float widthUnit = 1.0f / (coordsAcross - 1); + float heightUnit = 1.0f / (coordsDown - 1); + + for (imageY = 0; imageY < coordsDown; imageY++) + { + int rowOffset = imageY * coordsAcross; + + for (imageX = 0; imageX < coordsAcross; imageX++) + { + /* + * p1-----p2 + * | \ f2 | + * | \ | + * | f1 \| + * p3-----p4 + */ + + p4 = rowOffset + imageX; + p3 = p4 - 1; + + p2 = p4 - coordsAcross; + p1 = p3 - coordsAcross; + + this.coords.Add(rows[imageY][imageX]); + if (viewerMode) + { + this.normals.Add(new Coord()); + this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); + } + + if (imageY > 0 && imageX > 0) + { + Face f1, f2; + + if (viewerMode) + { + if (invert) + { + f1 = new Face(p1, p4, p3, p1, p4, p3); + f1.uv1 = p1; + f1.uv2 = p4; + f1.uv3 = p3; + + f2 = new Face(p1, p2, p4, p1, p2, p4); + f2.uv1 = p1; + f2.uv2 = p2; + f2.uv3 = p4; + } + else + { + f1 = new Face(p1, p3, p4, p1, p3, p4); + f1.uv1 = p1; + f1.uv2 = p3; + f1.uv3 = p4; + + f2 = new Face(p1, p4, p2, p1, p4, p2); + f2.uv1 = p1; + f2.uv2 = p4; + f2.uv3 = p2; + } + } + else + { + if (invert) + { + f1 = new Face(p1, p4, p3); + f2 = new Face(p1, p2, p4); + } + else + { + f1 = new Face(p1, p3, p4); + f2 = new Face(p1, p4, p2); + } + } + + this.faces.Add(f1); + this.faces.Add(f2); + } + } + } + + if (viewerMode) + calcVertexNormals(sculptType, coordsAcross, coordsDown); + } + + /// + /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. + /// + /// + public SculptMesh Copy() + { + return new SculptMesh(this); + } + + public SculptMesh(SculptMesh sm) + { + coords = new List(sm.coords); + faces = new List(sm.faces); + viewerFaces = new List(sm.viewerFaces); + normals = new List(sm.normals); + uvs = new List(sm.uvs); + } + + private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) + { // compute vertex normals by summing all the surface normals of all the triangles sharing + // each vertex and then normalizing + int numFaces = this.faces.Count; + for (int i = 0; i < numFaces; i++) + { + Face face = this.faces[i]; + Coord surfaceNormal = face.SurfaceNormal(this.coords); + this.normals[face.n1] += surfaceNormal; + this.normals[face.n2] += surfaceNormal; + this.normals[face.n3] += surfaceNormal; + } + + int numNormals = this.normals.Count; + for (int i = 0; i < numNormals; i++) + this.normals[i] = this.normals[i].Normalize(); + + if (sculptType != SculptType.plane) + { // blend the vertex normals at the cylinder seam + for (int y = 0; y < ySize; y++) + { + int rowOffset = y * xSize; + + this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); + } + } + + foreach (Face face in this.faces) + { + ViewerFace vf = new ViewerFace(0); + vf.v1 = this.coords[face.v1]; + vf.v2 = this.coords[face.v2]; + vf.v3 = this.coords[face.v3]; + + vf.coordIndex1 = face.v1; + vf.coordIndex2 = face.v2; + vf.coordIndex3 = face.v3; + + vf.n1 = this.normals[face.n1]; + vf.n2 = this.normals[face.n2]; + vf.n3 = this.normals[face.n3]; + + vf.uv1 = this.uvs[face.uv1]; + vf.uv2 = this.uvs[face.uv2]; + vf.uv3 = this.uvs[face.uv3]; + + this.viewerFaces.Add(vf); + } + } + + /// + /// Adds a value to each XYZ vertex coordinate in the mesh + /// + /// + /// + /// + public void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.AddPos(x, y, z); + this.viewerFaces[i] = v; + } + } + } + + /// + /// Rotates the mesh + /// + /// + public void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + int numNormals = this.normals.Count; + for (i = 0; i < numNormals; i++) + this.normals[i] *= q; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= q; + v.v2 *= q; + v.v3 *= q; + + v.n1 *= q; + v.n2 *= q; + v.n3 *= q; + + this.viewerFaces[i] = v; + } + } + } + + public void Scale(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + + Coord m = new Coord(x, y, z); + for (i = 0; i < numVerts; i++) + this.coords[i] *= m; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= m; + v.v2 *= m; + v.v3 *= m; + this.viewerFaces[i] = v; + } + } + } + + public void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } +} diff --git a/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs b/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs index 4ebf93329c..3104917f83 100644 --- a/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs +++ b/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs @@ -90,8 +90,6 @@ namespace OpenSim.Server.Handlers.Presence return GetAgent(request); case "getagents": return GetAgents(request); - case "sethome": - return SetHome(request); } m_log.DebugFormat("[PRESENCE HANDLER]: unknown method request: {0}", method); } @@ -140,12 +138,7 @@ namespace OpenSim.Server.Handlers.Presence if (!UUID.TryParse(request["SessionID"].ToString(), out session)) return FailureResult(); - if (request.ContainsKey("Position") && request["Position"] != null) - Vector3.TryParse(request["Position"].ToString(), out position); - if (request.ContainsKey("LookAt") && request["Position"] != null) - Vector3.TryParse(request["LookAt"].ToString(), out lookat); - - if (m_PresenceService.LogoutAgent(session, position, lookat)) + if (m_PresenceService.LogoutAgent(session)) return SuccessResult(); return FailureResult(); @@ -171,8 +164,6 @@ namespace OpenSim.Server.Handlers.Presence { UUID session = UUID.Zero; UUID region = UUID.Zero; - Vector3 position = new Vector3(128, 128, 70); - Vector3 look = Vector3.Zero; if (!request.ContainsKey("SessionID") || !request.ContainsKey("RegionID")) return FailureResult(); @@ -183,13 +174,7 @@ namespace OpenSim.Server.Handlers.Presence if (!UUID.TryParse(request["RegionID"].ToString(), out region)) return FailureResult(); - if (request.ContainsKey("position")) - Vector3.TryParse(request["position"].ToString(), out position); - - if (request.ContainsKey("lookAt")) - Vector3.TryParse(request["lookAt"].ToString(), out look); - - if (m_PresenceService.ReportAgent(session, region, position, look)) + if (m_PresenceService.ReportAgent(session, region)) { return SuccessResult(); } @@ -318,31 +303,5 @@ namespace OpenSim.Server.Handlers.Presence return ms.ToArray(); } - byte[] SetHome(Dictionary request) - { - UUID region = UUID.Zero; - Vector3 position = new Vector3(128, 128, 70); - Vector3 look = Vector3.Zero; - - if (!request.ContainsKey("UserID") || !request.ContainsKey("RegionID")) - return FailureResult(); - - string user = request["UserID"].ToString(); - - if (!UUID.TryParse(request["RegionID"].ToString(), out region)) - return FailureResult(); - - if (request.ContainsKey("position")) - Vector3.TryParse(request["position"].ToString(), out position); - - if (request.ContainsKey("lookAt")) - Vector3.TryParse(request["lookAt"].ToString(), out look); - - if (m_PresenceService.SetHomeLocation(user, region, position, look)) - return SuccessResult(); - - return FailureResult(); - } - } } diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs index 0e8506706c..b4500a5f55 100644 --- a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs +++ b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs @@ -25,14 +25,206 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using log4net; using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenMetaverse; namespace OpenSim.Services.Connectors { - public class GridUserServiceConnector + public class GridUserServicesConnector : IGridUserService { - public GridUserServiceConnector() + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public GridUserServicesConnector() { } + + public GridUserServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public GridUserServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["GridUserService"]; + if (gridConfig == null) + { + m_log.Error("[GRID USER CONNECTOR]: GridUserService missing from OpenSim.ini"); + throw new Exception("GridUser connector init error"); + } + + string serviceURI = gridConfig.GetString("GridUserServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[GRID USER CONNECTOR]: No Server URI named in section GridUserService"); + throw new Exception("GridUser connector init error"); + } + m_ServerURI = serviceURI; + } + + + #region IPresenceService + + + public GridUserInfo LoggedIn(string userID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "loggedin"; + + sendData["UserID"] = userID; + + return Get(sendData); + + } + + public bool LoggedOut(string userID, UUID region, Vector3 position, Vector3 lookat) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "loggedout"; + + return Set(sendData, userID, region, position, lookat); + } + + public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "sethome"; + + return Set(sendData, userID, regionID, position, lookAt); + } + + public bool SetLastPosition(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "setposition"; + + return Set(sendData, userID, regionID, position, lookAt); + } + + public GridUserInfo GetGridUserInfo(string userID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getgriduserinfo"; + + sendData["UserID"] = userID; + + return Get(sendData); + } + + #endregion + + protected bool Set(Dictionary sendData, string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + sendData["UserID"] = userID; + sendData["RegionID"] = regionID.ToString(); + sendData["Position"] = position.ToString(); + sendData["LookAt"] = lookAt.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + // m_log.DebugFormat("[GRID USER CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/griduser", + reqString); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: SetPosition reply data does not contain result field"); + + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: SetPosition received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER CONNECTOR]: Exception when contacting grid user server: {0}", e.Message); + } + + return false; + } + + protected GridUserInfo Get(Dictionary sendData) + { + string reqString = ServerUtils.BuildQueryString(sendData); + // m_log.DebugFormat("[GRID USER CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/griduser", + reqString); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + GridUserInfo guinfo = null; + + if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + { + guinfo = new GridUserInfo((Dictionary)replyData["result"]); + } + } + + return guinfo; + + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: Loggedin received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER CONNECTOR]: Exception when contacting grid user server: {0}", e.Message); + } + + return null; + + } + } } diff --git a/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs b/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs index 23621b7387..41ebeaf05e 100644 --- a/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/Presence/PresenceServiceConnector.cs @@ -132,7 +132,7 @@ namespace OpenSim.Services.Connectors } - public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookat) + public bool LogoutAgent(UUID sessionID) { Dictionary sendData = new Dictionary(); //sendData["SCOPEID"] = scopeID.ToString(); @@ -141,8 +141,6 @@ namespace OpenSim.Services.Connectors sendData["METHOD"] = "logout"; sendData["SessionID"] = sessionID.ToString(); - sendData["Position"] = position.ToString(); - sendData["LookAt"] = lookat.ToString(); string reqString = ServerUtils.BuildQueryString(sendData); // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); @@ -220,7 +218,7 @@ namespace OpenSim.Services.Connectors return false; } - public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { Dictionary sendData = new Dictionary(); //sendData["SCOPEID"] = scopeID.ToString(); @@ -230,8 +228,6 @@ namespace OpenSim.Services.Connectors sendData["SessionID"] = sessionID.ToString(); sendData["RegionID"] = regionID.ToString(); - sendData["position"] = position.ToString(); - sendData["lookAt"] = lookAt.ToString(); string reqString = ServerUtils.BuildQueryString(sendData); // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); @@ -371,52 +367,6 @@ namespace OpenSim.Services.Connectors } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - Dictionary sendData = new Dictionary(); - //sendData["SCOPEID"] = scopeID.ToString(); - sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); - sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); - sendData["METHOD"] = "sethome"; - - sendData["UserID"] = userID; - sendData["RegionID"] = regionID.ToString(); - sendData["position"] = position.ToString(); - sendData["lookAt"] = lookAt.ToString(); - - string reqString = ServerUtils.BuildQueryString(sendData); - // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); - try - { - string reply = SynchronousRestFormsRequester.MakeRequest("POST", - m_ServerURI + "/presence", - reqString); - if (reply != string.Empty) - { - Dictionary replyData = ServerUtils.ParseXmlResponse(reply); - - if (replyData.ContainsKey("result")) - { - if (replyData["result"].ToString().ToLower() == "success") - return true; - else - return false; - } - else - m_log.DebugFormat("[PRESENCE CONNECTOR]: SetHomeLocation reply data does not contain result field"); - - } - else - m_log.DebugFormat("[PRESENCE CONNECTOR]: SetHomeLocation received empty reply"); - } - catch (Exception e) - { - m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server: {0}", e.Message); - } - - return false; - } - #endregion } diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs index c324272093..e48b7de3bf 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs @@ -51,7 +51,7 @@ namespace OpenSim.Services.Connectors.SimianGrid /// message routing) to the SimianGrid backend ///
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] - public class SimianPresenceServiceConnector : IPresenceService, ISharedRegionModule + public class SimianPresenceServiceConnector : IPresenceService, IGridUserService, ISharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger( @@ -73,6 +73,7 @@ namespace OpenSim.Services.Connectors.SimianGrid if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); + scene.RegisterModuleInterface(this); scene.EventManager.OnMakeRootAgent += MakeRootAgentHandler; scene.EventManager.OnNewClient += NewClientHandler; @@ -86,6 +87,7 @@ namespace OpenSim.Services.Connectors.SimianGrid if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); + scene.UnregisterModuleInterface(this); scene.EventManager.OnMakeRootAgent -= MakeRootAgentHandler; scene.EventManager.OnNewClient -= NewClientHandler; @@ -151,7 +153,7 @@ namespace OpenSim.Services.Connectors.SimianGrid return success; } - public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookAt) + public bool LogoutAgent(UUID sessionID) { m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for agent with sessionID " + sessionID); @@ -189,7 +191,12 @@ namespace OpenSim.Services.Connectors.SimianGrid return success; } - public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) + { + return ReportAgent(sessionID, regionID, Vector3.Zero, Vector3.Zero); + } + + protected bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) { //m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Updating session data for agent with sessionID " + sessionID); @@ -261,7 +268,23 @@ namespace OpenSim.Services.Connectors.SimianGrid return presences.ToArray(); } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + #endregion IPresenceService + + #region IGridUserService + + public GridUserInfo LoggedIn(string userID) + { + // never implemented at the sim + return null; + } + + public bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + // Not needed for simian grid, event handler is doing it + return true; + } + + public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Setting home location for user " + userID); @@ -281,7 +304,35 @@ namespace OpenSim.Services.Connectors.SimianGrid return success; } - #endregion IPresenceService + public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + // Not needed for simian grid, presence detection is doing it + return true; + } + + public GridUserInfo GetGridUserInfo(string user) + { + m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent " + user); + + UUID userID = new UUID(user); + m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs); + if (userResponse["Success"].AsBoolean()) + return ResponseToGridUserInfo(userResponse); + else + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString()); + + return null; + } + + #endregion #region Presence Detection @@ -325,7 +376,7 @@ namespace OpenSim.Services.Connectors.SimianGrid SetLastLocation(client.SessionId); } - LogoutAgent(client.SessionId, Vector3.Zero, Vector3.UnitX); + LogoutAgent(client.SessionId); } } @@ -478,6 +529,31 @@ namespace OpenSim.Services.Connectors.SimianGrid return info; } + private GridUserInfo ResponseToGridUserInfo(OSDMap userResponse) + { + if (userResponse != null && userResponse["User"] is OSDMap) + { + + GridUserInfo info = new GridUserInfo(); + + info.Online = true; + info.UserID = userResponse["UserID"].AsUUID().ToString(); + info.LastRegionID = userResponse["SceneID"].AsUUID(); + info.LastPosition = userResponse["ScenePosition"].AsVector3(); + info.LastLookAt = userResponse["SceneLookAt"].AsVector3(); + + OSDMap user = (OSDMap)userResponse["User"]; + + info.Login = user["LastLoginDate"].AsDate(); + info.Logout = user["LastLogoutDate"].AsDate(); + DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt); + + return info; + } + + return null; + } + private string SerializeLocation(UUID regionID, Vector3 position, Vector3 lookAt) { return "{" + String.Format("\"SceneID\":\"{0}\",\"Position\":\"{1}\",\"LookAt\":\"{2}\"", regionID, position, lookAt) + "}"; diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 26f211b5d9..3af7ef9d3f 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -59,7 +59,7 @@ namespace OpenSim.Services.HypergridService static bool m_Initialized = false; - protected static IPresenceService m_PresenceService; + protected static IGridUserService m_GridUserService; protected static IGridService m_GridService; protected static GatekeeperServiceConnector m_GatekeeperConnector; @@ -74,14 +74,14 @@ namespace OpenSim.Services.HypergridService throw new Exception(String.Format("No section UserAgentService in config file")); string gridService = serverConfig.GetString("GridService", String.Empty); - string presenceService = serverConfig.GetString("PresenceService", String.Empty); + string gridUserService = serverConfig.GetString("GridUserService", String.Empty); - if (gridService == string.Empty || presenceService == string.Empty) + if (gridService == string.Empty || gridUserService == string.Empty) throw new Exception(String.Format("Incomplete specifications, UserAgent Service cannot function.")); Object[] args = new Object[] { config }; m_GridService = ServerUtils.LoadPlugin(gridService, args); - m_PresenceService = ServerUtils.LoadPlugin(presenceService, args); + m_GridUserService = ServerUtils.LoadPlugin(gridUserService, args); m_GatekeeperConnector = new GatekeeperServiceConnector(); m_Initialized = true; @@ -95,15 +95,14 @@ namespace OpenSim.Services.HypergridService m_log.DebugFormat("[USER AGENT SERVICE]: Request to get home region of user {0}", userID); GridRegion home = null; - PresenceInfo[] presences = m_PresenceService.GetAgents(new string[] { userID.ToString() }); - if (presences != null && presences.Length > 0) + GridUserInfo uinfo = m_GridUserService.GetGridUserInfo(userID.ToString()); + if (uinfo != null) { - UUID homeID = presences[0].HomeRegionID; - if (homeID != UUID.Zero) + if (uinfo.HomeRegionID != UUID.Zero) { - home = m_GridService.GetRegionByUUID(UUID.Zero, homeID); - position = presences[0].HomePosition; - lookAt = presences[0].HomeLookAt; + home = m_GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); + position = uinfo.HomePosition; + lookAt = uinfo.HomeLookAt; } if (home == null) { diff --git a/OpenSim/Services/Interfaces/IGridUserService.cs b/OpenSim/Services/Interfaces/IGridUserService.cs index a7c2c6f014..e629dffda4 100644 --- a/OpenSim/Services/Interfaces/IGridUserService.cs +++ b/OpenSim/Services/Interfaces/IGridUserService.cs @@ -37,39 +37,79 @@ namespace OpenSim.Services.Interfaces public class GridUserInfo { public string UserID; + public UUID HomeRegionID; public Vector3 HomePosition; public Vector3 HomeLookAt; + public UUID LastRegionID; + public Vector3 LastPosition; + public Vector3 LastLookAt; + + public bool Online; + public DateTime Login; + public DateTime Logout; + public GridUserInfo() {} public GridUserInfo(Dictionary kvp) { if (kvp.ContainsKey("UserID")) UserID = kvp["UserID"].ToString(); + if (kvp.ContainsKey("HomeRegionID")) UUID.TryParse(kvp["HomeRegionID"].ToString(), out HomeRegionID); if (kvp.ContainsKey("HomePosition")) Vector3.TryParse(kvp["HomePosition"].ToString(), out HomePosition); if (kvp.ContainsKey("HomeLookAt")) Vector3.TryParse(kvp["HomeLookAt"].ToString(), out HomeLookAt); + + if (kvp.ContainsKey("LastRegionID")) + UUID.TryParse(kvp["LastRegionID"].ToString(), out HomeRegionID); + if (kvp.ContainsKey("LastPosition")) + Vector3.TryParse(kvp["LastPosition"].ToString(), out LastPosition); + if (kvp.ContainsKey("LastLookAt")) + Vector3.TryParse(kvp["LastLookAt"].ToString(), out LastLookAt); + + if (kvp.ContainsKey("Login")) + DateTime.TryParse(kvp["Login"].ToString(), out Login); + if (kvp.ContainsKey("Logout")) + DateTime.TryParse(kvp["Logout"].ToString(), out Logout); + if (kvp.ContainsKey("Online")) + Boolean.TryParse(kvp["Online"].ToString(), out Online); + } public Dictionary ToKeyValuePairs() { Dictionary result = new Dictionary(); result["UserID"] = UserID; + result["HomeRegionID"] = HomeRegionID.ToString(); result["HomePosition"] = HomePosition.ToString(); result["HomeLookAt"] = HomeLookAt.ToString(); + result["LastRegionID"] = LastRegionID.ToString(); + result["LastPosition"] = LastPosition.ToString(); + result["LastLookAt"] = LastLookAt.ToString(); + + result["Online"] = Online.ToString(); + result["Login"] = Login.ToString(); + result["Logout"] = Logout.ToString(); + + return result; } } public interface IGridUserService { + GridUserInfo LoggedIn(string userID); + bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); + + bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt); + bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); + GridUserInfo GetGridUserInfo(string userID); - bool StoreGridUserInfo(GridUserInfo info); } } \ No newline at end of file diff --git a/OpenSim/Services/Interfaces/IPresenceService.cs b/OpenSim/Services/Interfaces/IPresenceService.cs index b4c18592cd..9687d22bea 100644 --- a/OpenSim/Services/Interfaces/IPresenceService.cs +++ b/OpenSim/Services/Interfaces/IPresenceService.cs @@ -55,23 +55,10 @@ namespace OpenSim.Services.Interfaces UserID = kvp["UserID"].ToString(); if (kvp.ContainsKey("RegionID")) UUID.TryParse(kvp["RegionID"].ToString(), out RegionID); - if (kvp.ContainsKey("login")) - DateTime.TryParse(kvp["login"].ToString(), out Login); - if (kvp.ContainsKey("logout")) - DateTime.TryParse(kvp["logout"].ToString(), out Logout); if (kvp.ContainsKey("lookAt")) Vector3.TryParse(kvp["lookAt"].ToString(), out LookAt); - if (kvp.ContainsKey("online")) - Boolean.TryParse(kvp["online"].ToString(), out Online); if (kvp.ContainsKey("position")) Vector3.TryParse(kvp["position"].ToString(), out Position); - if (kvp.ContainsKey("HomeRegionID")) - UUID.TryParse(kvp["HomeRegionID"].ToString(), out HomeRegionID); - if (kvp.ContainsKey("HomePosition")) - Vector3.TryParse(kvp["HomePosition"].ToString(), out HomePosition); - if (kvp.ContainsKey("HomeLookAt")) - Vector3.TryParse(kvp["HomeLookAt"].ToString(), out HomeLookAt); - } public Dictionary ToKeyValuePairs() @@ -79,14 +66,8 @@ namespace OpenSim.Services.Interfaces Dictionary result = new Dictionary(); result["UserID"] = UserID; result["RegionID"] = RegionID.ToString(); - result["online"] = Online.ToString(); - result["login"] = Login.ToString(); - result["logout"] = Logout.ToString(); result["position"] = Position.ToString(); result["lookAt"] = LookAt.ToString(); - result["HomeRegionID"] = HomeRegionID.ToString(); - result["HomePosition"] = HomePosition.ToString(); - result["HomeLookAt"] = HomeLookAt.ToString(); return result; } @@ -115,11 +96,10 @@ namespace OpenSim.Services.Interfaces public interface IPresenceService { bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID); - bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookAt); + bool LogoutAgent(UUID sessionID); bool LogoutRegionAgents(UUID regionID); - bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt); - bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt); + bool ReportAgent(UUID sessionID, UUID regionID); PresenceInfo GetAgent(UUID sessionID); PresenceInfo[] GetAgents(string[] userIDs); diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs index ee30fa38ef..d1dcfe7579 100644 --- a/OpenSim/Services/LLLoginService/LLLoginResponse.cs +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -215,7 +215,7 @@ namespace OpenSim.Services.LLLoginService SetDefaultValues(); } - public LLLoginResponse(UserAccount account, AgentCircuitData aCircuit, PresenceInfo pinfo, + public LLLoginResponse(UserAccount account, AgentCircuitData aCircuit, GridUserInfo pinfo, GridRegion destination, List invSkel, FriendInfo[] friendsList, ILibraryService libService, string where, string startlocation, Vector3 position, Vector3 lookAt, string message, GridRegion home, IPEndPoint clientIP) @@ -283,7 +283,7 @@ namespace OpenSim.Services.LLLoginService } } - private void FillOutHomeData(PresenceInfo pinfo, GridRegion home) + private void FillOutHomeData(GridUserInfo pinfo, GridRegion home) { int x = 1000 * (int)Constants.RegionSize, y = 1000 * (int)Constants.RegionSize; if (home != null) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index be90d38d2e..f97222ef63 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -53,6 +53,7 @@ namespace OpenSim.Services.LLLoginService private static bool Initialized = false; protected IUserAccountService m_UserAccountService; + protected IGridUserService m_GridUserService; protected IAuthenticationService m_AuthenticationService; protected IInventoryService m_InventoryService; protected IGridService m_GridService; @@ -82,6 +83,7 @@ namespace OpenSim.Services.LLLoginService throw new Exception(String.Format("No section LoginService in config file")); string accountService = m_LoginServerConfig.GetString("UserAccountService", String.Empty); + string gridUserService = m_LoginServerConfig.GetString("GridUserService", String.Empty); string agentService = m_LoginServerConfig.GetString("UserAgentService", String.Empty); string authService = m_LoginServerConfig.GetString("AuthenticationService", String.Empty); string invService = m_LoginServerConfig.GetString("InventoryService", String.Empty); @@ -105,8 +107,10 @@ namespace OpenSim.Services.LLLoginService Object[] args = new Object[] { config }; m_UserAccountService = ServerUtils.LoadPlugin(accountService, args); + m_GridUserService = ServerUtils.LoadPlugin(gridUserService, args); m_AuthenticationService = ServerUtils.LoadPlugin(authService, args); m_InventoryService = ServerUtils.LoadPlugin(invService, args); + if (gridService != string.Empty) m_GridService = ServerUtils.LoadPlugin(gridService, args); if (presenceService != string.Empty) @@ -271,8 +275,6 @@ namespace OpenSim.Services.LLLoginService // // Login the presence // - PresenceInfo presence = null; - GridRegion home = null; if (m_PresenceService != null) { success = m_PresenceService.LoginAgent(account.PrincipalID.ToString(), session, secureSession); @@ -281,17 +283,24 @@ namespace OpenSim.Services.LLLoginService m_log.InfoFormat("[LLOGIN SERVICE]: Login failed, reason: could not login presence"); return LLFailedLoginResponse.GridProblem; } - - // Get the updated presence info - presence = m_PresenceService.GetAgent(session); - - // Get the home region - if ((presence.HomeRegionID != UUID.Zero) && m_GridService != null) - { - home = m_GridService.GetRegionByUUID(scopeID, presence.HomeRegionID); - } } + // + // Change Online status and get the home region + // + GridRegion home = null; + GridUserInfo guinfo = m_GridUserService.LoggedIn(account.PrincipalID.ToString()); + if (guinfo != null && (guinfo.HomeRegionID != UUID.Zero) && m_GridService != null) + { + home = m_GridService.GetRegionByUUID(scopeID, guinfo.HomeRegionID); + } + if (guinfo == null) + { + // something went wrong, make something up, so that we don't have to test this anywhere else + guinfo = new GridUserInfo(); + guinfo.LastPosition = guinfo.HomePosition = new Vector3(128, 128, 30); + } + // // Find the destination region/grid // @@ -299,10 +308,10 @@ namespace OpenSim.Services.LLLoginService Vector3 position = Vector3.Zero; Vector3 lookAt = Vector3.Zero; GridRegion gatekeeper = null; - GridRegion destination = FindDestination(account, scopeID, presence, session, startLocation, out gatekeeper, out where, out position, out lookAt); + GridRegion destination = FindDestination(account, scopeID, guinfo, session, startLocation, home, out gatekeeper, out where, out position, out lookAt); if (destination == null) { - m_PresenceService.LogoutAgent(session, presence.Position, presence.LookAt); + m_PresenceService.LogoutAgent(session); m_log.InfoFormat("[LLOGIN SERVICE]: Login failed, reason: destination not found"); return LLFailedLoginResponse.GridProblem; } @@ -324,7 +333,7 @@ namespace OpenSim.Services.LLLoginService if (aCircuit == null) { - m_PresenceService.LogoutAgent(session, presence.Position, presence.LookAt); + m_PresenceService.LogoutAgent(session); m_log.InfoFormat("[LLOGIN SERVICE]: Login failed, reason: {0}", reason); return LLFailedLoginResponse.AuthorizationProblem; @@ -340,7 +349,7 @@ namespace OpenSim.Services.LLLoginService // // Finally, fill out the response and return it // - LLLoginResponse response = new LLLoginResponse(account, aCircuit, presence, destination, inventorySkel, friendsList, m_LibraryService, + LLLoginResponse response = new LLLoginResponse(account, aCircuit, guinfo, destination, inventorySkel, friendsList, m_LibraryService, where, startLocation, position, lookAt, m_WelcomeMessage, home, clientIP); m_log.DebugFormat("[LLOGIN SERVICE]: All clear. Sending login response to client."); @@ -350,12 +359,12 @@ namespace OpenSim.Services.LLLoginService { m_log.WarnFormat("[LLOGIN SERVICE]: Exception processing login for {0} {1}: {2} {3}", firstName, lastName, e.ToString(), e.StackTrace); if (m_PresenceService != null) - m_PresenceService.LogoutAgent(session, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + m_PresenceService.LogoutAgent(session); return LLFailedLoginResponse.InternalError; } } - protected GridRegion FindDestination(UserAccount account, UUID scopeID, PresenceInfo pinfo, UUID sessionID, string startLocation, out GridRegion gatekeeper, out string where, out Vector3 position, out Vector3 lookAt) + protected GridRegion FindDestination(UserAccount account, UUID scopeID, GridUserInfo pinfo, UUID sessionID, string startLocation, GridRegion home, out GridRegion gatekeeper, out string where, out Vector3 position, out Vector3 lookAt) { m_log.DebugFormat("[LLOGIN SERVICE]: FindDestination for start location {0}", startLocation); @@ -377,7 +386,7 @@ namespace OpenSim.Services.LLLoginService bool tryDefaults = false; - if (pinfo.HomeRegionID.Equals(UUID.Zero)) + if (home == null) { m_log.WarnFormat( "[LLOGIN SERVICE]: User {0} {1} tried to login to a 'home' start location but they have none set", @@ -387,16 +396,10 @@ namespace OpenSim.Services.LLLoginService } else { - region = m_GridService.GetRegionByUUID(scopeID, pinfo.HomeRegionID); + region = home; - if (null == region) - { - m_log.WarnFormat( - "[LLOGIN SERVICE]: User {0} {1} has a recorded home region of {2} but this cannot be found by the grid service", - account.FirstName, account.LastName, pinfo.HomeRegionID); - - tryDefaults = true; - } + position = pinfo.HomePosition; + lookAt = pinfo.HomeLookAt; } if (tryDefaults) @@ -432,7 +435,7 @@ namespace OpenSim.Services.LLLoginService GridRegion region = null; - if (pinfo.RegionID.Equals(UUID.Zero) || (region = m_GridService.GetRegionByUUID(scopeID, pinfo.RegionID)) == null) + if (pinfo.LastRegionID.Equals(UUID.Zero) || (region = m_GridService.GetRegionByUUID(scopeID, pinfo.LastRegionID)) == null) { List defaults = m_GridService.GetDefaultRegions(scopeID); if (defaults != null && defaults.Count > 0) @@ -454,8 +457,8 @@ namespace OpenSim.Services.LLLoginService } else { - position = pinfo.Position; - lookAt = pinfo.LookAt; + position = pinfo.LastPosition; + lookAt = pinfo.LastLookAt; } return region; diff --git a/OpenSim/Services/PresenceService/PresenceService.cs b/OpenSim/Services/PresenceService/PresenceService.cs index ea8d673c1f..7e7e98e417 100644 --- a/OpenSim/Services/PresenceService/PresenceService.cs +++ b/OpenSim/Services/PresenceService/PresenceService.cs @@ -54,8 +54,6 @@ namespace OpenSim.Services.PresenceService public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) { - m_Database.Prune(userID); - PresenceData[] d = m_Database.Get("UserID", userID); PresenceData data = new PresenceData(); @@ -65,24 +63,6 @@ namespace OpenSim.Services.PresenceService data.SessionID = sessionID; data.Data = new Dictionary(); data.Data["SecureSessionID"] = secureSessionID.ToString(); - data.Data["Online"] = "true"; - data.Data["Login"] = Util.UnixTimeSinceEpoch().ToString(); - if (d != null && d.Length > 0) - { - data.Data["HomeRegionID"] = d[0].Data["HomeRegionID"]; - data.Data["HomePosition"] = d[0].Data["HomePosition"]; - data.Data["HomeLookAt"] = d[0].Data["HomeLookAt"]; - data.Data["Position"] = d[0].Data["Position"]; - data.Data["LookAt"] = d[0].Data["LookAt"]; - - data.RegionID = d[0].RegionID; - } - else - { - data.Data["HomeRegionID"] = UUID.Zero.ToString(); - data.Data["HomePosition"] = new Vector3(128, 128, 0).ToString(); - data.Data["HomeLookAt"] = new Vector3(0, 1, 0).ToString(); - } m_Database.Store(data); @@ -91,28 +71,10 @@ namespace OpenSim.Services.PresenceService return true; } - public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookat) + public bool LogoutAgent(UUID sessionID) { - PresenceData data = m_Database.Get(sessionID); - if (data == null) - return false; - - PresenceData[] d = m_Database.Get("UserID", data.UserID); - - m_log.DebugFormat("[PRESENCE SERVICE]: LogoutAgent {0} with {1} sessions currently present", data.UserID, d.Length); - if (d.Length > 1) - { - m_Database.Delete("UserID", data.UserID); - } - - data.Data["Online"] = "false"; - data.Data["Logout"] = Util.UnixTimeSinceEpoch().ToString(); - data.Data["Position"] = position.ToString(); - data.Data["LookAt"] = lookat.ToString(); - - m_Database.Store(data); - - return true; + m_log.DebugFormat("[PRESENCE SERVICE]: Session {0} logout", sessionID); + return m_Database.Delete("SessionID", sessionID.ToString()); } public bool LogoutRegionAgents(UUID regionID) @@ -123,7 +85,7 @@ namespace OpenSim.Services.PresenceService } - public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool ReportAgent(UUID sessionID, UUID regionID) { m_log.DebugFormat("[PRESENCE SERVICE]: ReportAgent with session {0} in region {1}", sessionID, regionID); try @@ -134,14 +96,7 @@ namespace OpenSim.Services.PresenceService if (pdata.Data == null) return false; - if (!pdata.Data.ContainsKey("Online") || (pdata.Data.ContainsKey("Online") && pdata.Data["Online"] == "false")) - { - m_log.WarnFormat("[PRESENCE SERVICE]: Someone tried to report presence of an agent who's not online"); - return false; - } - - return m_Database.ReportAgent(sessionID, regionID, - position.ToString(), lookAt.ToString()); + return m_Database.ReportAgent(sessionID, regionID); } catch (Exception e) { @@ -160,22 +115,10 @@ namespace OpenSim.Services.PresenceService ret.UserID = data.UserID; ret.RegionID = data.RegionID; - if (data.Data.ContainsKey("Online")) - ret.Online = bool.Parse(data.Data["Online"]); - if (data.Data.ContainsKey("Login")) - ret.Login = Util.ToDateTime(Convert.ToInt32(data.Data["Login"])); - if (data.Data.ContainsKey("Logout")) - ret.Logout = Util.ToDateTime(Convert.ToInt32(data.Data["Logout"])); if (data.Data.ContainsKey("Position")) ret.Position = Vector3.Parse(data.Data["Position"]); if (data.Data.ContainsKey("LookAt")) ret.LookAt = Vector3.Parse(data.Data["LookAt"]); - if (data.Data.ContainsKey("HomeRegionID")) - ret.HomeRegionID = new UUID(data.Data["HomeRegionID"]); - if (data.Data.ContainsKey("HomePosition")) - ret.HomePosition = Vector3.Parse(data.Data["HomePosition"]); - if (data.Data.ContainsKey("HomeLookAt")) - ret.HomeLookAt = Vector3.Parse(data.Data["HomeLookAt"]); return ret; } @@ -195,16 +138,8 @@ namespace OpenSim.Services.PresenceService ret.UserID = d.UserID; ret.RegionID = d.RegionID; - ret.Online = bool.Parse(d.Data["Online"]); - ret.Login = Util.ToDateTime(Convert.ToInt32( - d.Data["Login"])); - ret.Logout = Util.ToDateTime(Convert.ToInt32( - d.Data["Logout"])); ret.Position = Vector3.Parse(d.Data["Position"]); ret.LookAt = Vector3.Parse(d.Data["LookAt"]); - ret.HomeRegionID = new UUID(d.Data["HomeRegionID"]); - ret.HomePosition = Vector3.Parse(d.Data["HomePosition"]); - ret.HomeLookAt = Vector3.Parse(d.Data["HomeLookAt"]); info.Add(ret); } @@ -214,9 +149,5 @@ namespace OpenSim.Services.PresenceService return info.ToArray(); } - public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { - return m_Database.SetHomeLocation(userID, regionID, position, lookAt); - } } } diff --git a/OpenSim/Services/UserAccountService/GridUserService.cs b/OpenSim/Services/UserAccountService/GridUserService.cs index c6e33bbbfe..697ba639d2 100644 --- a/OpenSim/Services/UserAccountService/GridUserService.cs +++ b/OpenSim/Services/UserAccountService/GridUserService.cs @@ -31,6 +31,7 @@ using System.Reflection; using Nini.Config; using OpenSim.Data; using OpenSim.Services.Interfaces; +using OpenSim.Framework; using OpenSim.Framework.Console; using GridRegion = OpenSim.Services.Interfaces.GridRegion; @@ -50,27 +51,109 @@ namespace OpenSim.Services.UserAccountService public GridUserInfo GetGridUserInfo(string userID) { - GridUserData d = m_Database.GetGridUserData(userID); - + GridUserData d = m_Database.Get(userID); + + if (d == null) + return null; + GridUserInfo info = new GridUserInfo(); info.UserID = d.UserID; info.HomeRegionID = new UUID(d.Data["HomeRegionID"]); info.HomePosition = Vector3.Parse(d.Data["HomePosition"]); info.HomeLookAt = Vector3.Parse(d.Data["HomeLookAt"]); + info.LastRegionID = new UUID(d.Data["LastRegionID"]); + info.LastPosition = Vector3.Parse(d.Data["LastPosition"]); + info.LastLookAt = Vector3.Parse(d.Data["LastLookAt"]); + + info.Online = bool.Parse(d.Data["Online"]); + info.Login = Util.ToDateTime(Convert.ToInt32(d.Data["Login"])); + info.Logout = Util.ToDateTime(Convert.ToInt32(d.Data["Logout"])); + return info; } - - public bool StoreGridUserInfo(GridUserInfo info) + + public GridUserInfo LoggedIn(string userID) + { + m_log.DebugFormat("[GRID USER SERVICE]: User {0} is online", userID); + GridUserData d = m_Database.Get(userID); + + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["Online"] = true.ToString(); + d.Data["Login"] = Util.UnixTimeSinceEpoch().ToString(); + + m_Database.Store(d); + + return GetGridUserInfo(userID); + } + + public bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + m_log.DebugFormat("[GRID USER SERVICE]: User {0} is offline", userID); + GridUserData d = m_Database.Get(userID); + + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["Online"] = false.ToString(); + d.Data["Logout"] = Util.UnixTimeSinceEpoch().ToString(); + d.Data["LastRegionID"] = regionID.ToString(); + d.Data["LastPosition"] = lastPosition.ToString(); + d.Data["LastLookAt"] = lastLookAt.ToString(); + + return m_Database.Store(d); + } + + protected bool StoreGridUserInfo(GridUserInfo info) { GridUserData d = new GridUserData(); - d.Data["UserID"] = info.UserID; d.Data["HomeRegionID"] = info.HomeRegionID.ToString(); d.Data["HomePosition"] = info.HomePosition.ToString(); d.Data["HomeLookAt"] = info.HomeLookAt.ToString(); - return m_Database.StoreGridUserData(d); + return m_Database.Store(d); + } + + public bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt) + { + GridUserData d = m_Database.Get(userID); + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["HomeRegionID"] = homeID.ToString(); + d.Data["HomePosition"] = homePosition.ToString(); + d.Data["HomeLookAt"] = homeLookAt.ToString(); + + return m_Database.Store(d); + } + + public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + //m_log.DebugFormat("[Grid User Service]: SetLastPosition for {0}", userID); + GridUserData d = m_Database.Get(userID); + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["LastRegionID"] = regionID.ToString(); + d.Data["LastPosition"] = lastPosition.ToString(); + d.Data["LastLookAt"] = lastLookAt.ToString(); + + return m_Database.Store(d); } } } \ No newline at end of file diff --git a/OpenSim/Services/UserAccountService/UserAccountService.cs b/OpenSim/Services/UserAccountService/UserAccountService.cs index 35e2826a71..6923293154 100644 --- a/OpenSim/Services/UserAccountService/UserAccountService.cs +++ b/OpenSim/Services/UserAccountService/UserAccountService.cs @@ -46,7 +46,7 @@ namespace OpenSim.Services.UserAccountService protected IGridService m_GridService; protected IAuthenticationService m_AuthenticationService; - protected IPresenceService m_PresenceService; + protected IGridUserService m_GridUserService; protected IInventoryService m_InventoryService; public UserAccountService(IConfigSource config) @@ -69,9 +69,9 @@ namespace OpenSim.Services.UserAccountService if (authServiceDll != string.Empty) m_AuthenticationService = LoadPlugin(authServiceDll, new Object[] { config }); - string presenceServiceDll = userConfig.GetString("PresenceService", string.Empty); + string presenceServiceDll = userConfig.GetString("GridUserService", string.Empty); if (presenceServiceDll != string.Empty) - m_PresenceService = LoadPlugin(presenceServiceDll, new Object[] { config }); + m_GridUserService = LoadPlugin(presenceServiceDll, new Object[] { config }); string invServiceDll = userConfig.GetString("InventoryService", string.Empty); if (invServiceDll != string.Empty) @@ -333,8 +333,8 @@ namespace OpenSim.Services.UserAccountService if (defaultRegions != null && defaultRegions.Count >= 1) home = defaultRegions[0]; - if (m_PresenceService != null && home != null) - m_PresenceService.SetHomeLocation(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + if (m_GridUserService != null && home != null) + m_GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); else m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to set home for account {0} {1}.", firstName, lastName); diff --git a/OpenSim/Tests/Clients/Presence/PresenceClient.cs b/OpenSim/Tests/Clients/Presence/PresenceClient.cs index 4f959f6547..0f6b80e209 100644 --- a/OpenSim/Tests/Clients/Presence/PresenceClient.cs +++ b/OpenSim/Tests/Clients/Presence/PresenceClient.cs @@ -77,7 +77,7 @@ namespace OpenSim.Tests.Clients.PresenceClient pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); System.Console.WriteLine("\n"); - success = m_Connector.ReportAgent(session1, region1, new Vector3(128, 128, 128), new Vector3(4, 5, 6)); + success = m_Connector.ReportAgent(session1, region1); if (success) m_log.InfoFormat("[PRESENCE CLIENT]: Successfully reported session {0} in region {1}", user1, region1); else @@ -90,20 +90,7 @@ namespace OpenSim.Tests.Clients.PresenceClient pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); System.Console.WriteLine("\n"); - success = m_Connector.SetHomeLocation(user1.ToString(), region1, new Vector3(128, 128, 128), new Vector3(4, 5, 6)); - if (success) - m_log.InfoFormat("[PRESENCE CLIENT]: Successfully set home for user {0} in region {1}", user1, region1); - else - m_log.InfoFormat("[PRESENCE CLIENT]: failed to set home for user {0}", user1); - pinfo = m_Connector.GetAgent(session1); - if (pinfo == null) - m_log.InfoFormat("[PRESENCE CLIENT]: Unable to retrieve presence for {0} for third time", user1); - else - m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; Online={1}; regionID={2}; homeRegion={3}", - pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); - - System.Console.WriteLine("\n"); - success = m_Connector.LogoutAgent(session1, Vector3.Zero, Vector3.UnitY); + success = m_Connector.LogoutAgent(session1); if (success) m_log.InfoFormat("[PRESENCE CLIENT]: Successfully logged out user {0}", user1); else @@ -116,7 +103,7 @@ namespace OpenSim.Tests.Clients.PresenceClient pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); System.Console.WriteLine("\n"); - success = m_Connector.ReportAgent(session1, UUID.Random(), Vector3.Zero, Vector3.Zero); + success = m_Connector.ReportAgent(session1, UUID.Random()); if (success) m_log.InfoFormat("[PRESENCE CLIENT]: Report agent succeeded, but this is wrong"); else diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index d8145af2db..9af1e4cdf3 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -11,7 +11,7 @@ ;; [Startup] -ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryServiceInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8003/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector,8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector,HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector,8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" +ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryServiceInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8003/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector,8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector,HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector,8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none if specified above @@ -93,6 +93,10 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 GridService = "OpenSim.Services.GridService.dll:GridService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" +[GridUserService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + [PresenceService] ; for the server connector LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" @@ -114,6 +118,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" ; for the service UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" @@ -196,7 +201,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 [UserAgentService] LocalServiceModule = "OpenSim.Services.HypergridService.dll:UserAgentService" ;; for the service - PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" GridService = "OpenSim.Services.GridService.dll:GridService" ;; The interface that local users get when they are in other grids. diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index ae3a53c635..502a8e656a 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -11,7 +11,7 @@ ; * [Startup] -ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" +ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryInConnector,8002/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none if specified above @@ -95,6 +95,10 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 GridService = "OpenSim.Services.GridService.dll:GridService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" +[GridUserService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + [PresenceService] ; for the server connector LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" @@ -116,6 +120,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" ; for the service UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" diff --git a/bin/config-include/Grid.ini b/bin/config-include/Grid.ini index 4767cbc847..9a75f19845 100644 --- a/bin/config-include/Grid.ini +++ b/bin/config-include/Grid.ini @@ -8,21 +8,24 @@ Include-Common = "config-include/GridCommon.ini" [Modules] - AssetServices = "RemoteAssetServicesConnector" - InventoryServices = "RemoteXInventoryServicesConnector" - GridServices = "RemoteGridServicesConnector" - AvatarServices = "RemoteAvatarServicesConnector" - NeighbourServices = "RemoteNeighbourServicesConnector" - AuthenticationServices = "RemoteAuthenticationServicesConnector" - PresenceServices = "RemotePresenceServicesConnector" - UserAccountServices = "RemoteUserAccountServicesConnector" - SimulationServices = "RemoteSimulationConnectorModule" - EntityTransferModule = "BasicEntityTransferModule" - InventoryAccessModule = "BasicInventoryAccessModule" - LandServiceInConnector = true - NeighbourServiceInConnector = true - SimulationServiceInConnector = true - LibraryModule = true + AssetServices = "RemoteAssetServicesConnector" + InventoryServices = "RemoteXInventoryServicesConnector" + GridServices = "RemoteGridServicesConnector" + AvatarServices = "RemoteAvatarServicesConnector" + NeighbourServices = "RemoteNeighbourServicesConnector" + AuthenticationServices = "RemoteAuthenticationServicesConnector" + AuthorizationServices = "RemoteAuthorizationServicesConnector" + PresenceServices = "RemotePresenceServicesConnector" + UserAccountServices = "RemoteUserAccountServicesConnector" + GridUserServices = "RemoteGridUserServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "BasicEntityTransferModule" + InventoryAccessModule = "BasicInventoryAccessModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = true [GridService] diff --git a/bin/config-include/GridCommon.ini.example b/bin/config-include/GridCommon.ini.example index 6e27694cd6..88ac5e2dbf 100644 --- a/bin/config-include/GridCommon.ini.example +++ b/bin/config-include/GridCommon.ini.example @@ -40,6 +40,12 @@ ; UserAccountServerURI = "http://mygridserver.com:8003" +[GridUserService] + ; + ; change this to your grid-wide user accounts server + ; + GridUserServerURI = "http://mygridserver.com:8003" + [AuthenticationService] ; ; change this to your grid-wide authentication server diff --git a/bin/config-include/GridHypergrid.ini b/bin/config-include/GridHypergrid.ini index 1e24f88749..1adc2d8068 100644 --- a/bin/config-include/GridHypergrid.ini +++ b/bin/config-include/GridHypergrid.ini @@ -8,22 +8,24 @@ Include-Common = "config-include/GridCommon.ini" [Modules] - AssetServices = "HGAssetBroker" - InventoryServices = "HGInventoryBroker" - GridServices = "RemoteGridServicesConnector" - AvatarServices = "RemoteAvatarServicesConnector" - NeighbourServices = "RemoteNeighbourServicesConnector" - AuthenticationServices = "RemoteAuthenticationServicesConnector" - AuthorizationServices = "LocalAuthorizationServicesConnector" - PresenceServices = "RemotePresenceServicesConnector" - UserAccountServices = "RemoteUserAccountServicesConnector" - SimulationServices = "RemoteSimulationConnectorModule" - EntityTransferModule = "HGEntityTransferModule" - InventoryAccessModule = "HGInventoryAccessModule" - LandServiceInConnector = true - NeighbourServiceInConnector = true - SimulationServiceInConnector = true - LibraryModule = true + AssetServices = "HGAssetBroker" + InventoryServices = "HGInventoryBroker" + GridServices = "RemoteGridServicesConnector" + AvatarServices = "RemoteAvatarServicesConnector" + NeighbourServices = "RemoteNeighbourServicesConnector" + AuthenticationServices = "RemoteAuthenticationServicesConnector" + AuthorizationServices = "RemoteAuthorizationServicesConnector" + PresenceServices = "RemotePresenceServicesConnector" + UserAccountServices = "RemoteUserAccountServicesConnector" + GridUserServices = "RemoteGridUserServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "HGEntityTransferModule" + InventoryAccessModule = "HGInventoryAccessModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = true [AssetService] LocalGridAssetService = "OpenSim.Services.Connectors.dll:AssetServicesConnector" diff --git a/bin/config-include/Standalone.ini b/bin/config-include/Standalone.ini index 027b4ea66e..6b7dc50b7f 100644 --- a/bin/config-include/Standalone.ini +++ b/bin/config-include/Standalone.ini @@ -17,8 +17,9 @@ AvatarServices = "LocalAvatarServicesConnector" EntityTransferModule = "BasicEntityTransferModule" InventoryAccessModule = "BasicInventoryAccessModule" - LibraryModule = true - LLLoginServiceInConnector = true + + LibraryModule = true + LLLoginServiceInConnector = true [AssetService] LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" @@ -54,7 +55,7 @@ ;; These are for creating new accounts AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" GridService = "OpenSim.Services.GridService.dll:GridService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" @@ -70,6 +71,7 @@ [LoginService] LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index 35e6f207eb..130e210a77 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -5,25 +5,27 @@ ;; [Modules] - AssetServices = "HGAssetBroker" - InventoryServices = "HGInventoryBroker" - NeighbourServices = "LocalNeighbourServicesConnector" - AuthenticationServices = "LocalAuthenticationServicesConnector" - GridServices = "LocalGridServicesConnector" - PresenceServices = "LocalPresenceServicesConnector" - UserAccountServices = "LocalUserAccountServicesConnector" - SimulationServices = "RemoteSimulationConnectorModule" - AvatarServices = "LocalAvatarServicesConnector" - EntityTransferModule = "HGEntityTransferModule" - InventoryAccessModule = "HGInventoryAccessModule" - InventoryServiceInConnector = true - AssetServiceInConnector = true - HypergridServiceInConnector = true - NeighbourServiceInConnector = true - LibraryModule = true - LLLoginServiceInConnector = true - AuthenticationServiceInConnector = true - SimulationServiceInConnector = true + AssetServices = "HGAssetBroker" + InventoryServices = "HGInventoryBroker" + NeighbourServices = "LocalNeighbourServicesConnector" + AuthenticationServices = "LocalAuthenticationServicesConnector" + GridServices = "LocalGridServicesConnector" + PresenceServices = "LocalPresenceServicesConnector" + UserAccountServices = "LocalUserAccountServicesConnector" + GridUserServices = "LocalGridUserServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + AvatarServices = "LocalAvatarServicesConnector" + EntityTransferModule = "HGEntityTransferModule" + InventoryAccessModule = "HGInventoryAccessModule" + + InventoryServiceInConnector = true + AssetServiceInConnector = true + HypergridServiceInConnector = true + NeighbourServiceInConnector = true + LibraryModule = true + LLLoginServiceInConnector = true + AuthenticationServiceInConnector = true + SimulationServiceInConnector = true [AssetService] LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" @@ -68,10 +70,10 @@ [UserAccountService] LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" - ConnectionString = "URI=file:userprofiles.db,version=3" + ;; These are for creating new accounts by the service AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" GridService = "OpenSim.Services.GridService.dll:GridService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" @@ -83,15 +85,16 @@ Connector = "OpenSim.Services.FriendsService.dll" [LoginService] - LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" - UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" - UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" - AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" - PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" - GridService = "OpenSim.Services.GridService.dll:GridService" - AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" - FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" [GatekeeperService] LocalServiceModule = "OpenSim.Services.HypergridService.dll:GatekeeperService" @@ -106,7 +109,7 @@ [UserAgentService] LocalServiceModule = "OpenSim.Services.HypergridService.dll:UserAgentService" ;; for the service - PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" GridService = "OpenSim.Services.GridService.dll:GridService" ;; The interface that local users get when they are in other grids diff --git a/bin/config-include/storage/SQLiteStandalone.ini b/bin/config-include/storage/SQLiteStandalone.ini index ed88a8a182..fe814d7022 100644 --- a/bin/config-include/storage/SQLiteStandalone.ini +++ b/bin/config-include/storage/SQLiteStandalone.ini @@ -17,6 +17,9 @@ [UserAccountService] ConnectionString = "URI=file:userprofiles.db,version=3" +[GridUserService] + ConnectionString = "URI=file:griduser.db,version=3" + [FriendsService] ConnectionString = "URI=file:friends.db,version=3" From 15562017f220e2474b548f860ce1174541d7e22f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 7 May 2010 21:32:02 -0700 Subject: [PATCH 099/260] These files are part of the GridUserService write-up. --- .../MySQL/Resources/001_GridUserStore.sql | 17 ++ .../SQLite/Resources/001_GridUserStore.sql | 16 + OpenSim/Data/SQLite/SQLiteGridUserData.cs | 61 ++++ .../GridUser/ActivityDetector.cs | 116 ++++++++ .../RemoteGridUserServiceConnector.cs | 153 ++++++++++ .../GridUser/GridUserServerConnector.cs | 61 ++++ .../GridUser/GridUserServerPostHandler.cs | 278 ++++++++++++++++++ 7 files changed, 702 insertions(+) create mode 100644 OpenSim/Data/MySQL/Resources/001_GridUserStore.sql create mode 100644 OpenSim/Data/SQLite/Resources/001_GridUserStore.sql create mode 100644 OpenSim/Data/SQLite/SQLiteGridUserData.cs create mode 100644 OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs create mode 100644 OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs create mode 100644 OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs create mode 100644 OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs diff --git a/OpenSim/Data/MySQL/Resources/001_GridUserStore.sql b/OpenSim/Data/MySQL/Resources/001_GridUserStore.sql new file mode 100644 index 0000000000..ce4ab96e97 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/001_GridUserStore.sql @@ -0,0 +1,17 @@ +BEGIN; + +CREATE TABLE `GridUser` ( + `UserID` VARCHAR(255) NOT NULL, + `HomeRegionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `HomePosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `HomeLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `LastRegionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `LastPosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `LastLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `Online` CHAR(5) NOT NULL DEFAULT 'false', + `Login` CHAR(16) NOT NULL DEFAULT '0', + `Logout` CHAR(16) NOT NULL DEFAULT '0', + PRIMARY KEY (`UserID`) +) ENGINE=InnoDB; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_GridUserStore.sql b/OpenSim/Data/SQLite/Resources/001_GridUserStore.sql new file mode 100644 index 0000000000..1a24613275 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/001_GridUserStore.sql @@ -0,0 +1,16 @@ +BEGIN TRANSACTION; + +CREATE TABLE GridUser ( + UserID VARCHAR(255) primary key, + HomeRegionID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + HomePosition CHAR(64) NOT NULL DEFAULT '<0,0,0>', + HomeLookAt CHAR(64) NOT NULL DEFAULT '<0,0,0>', + LastRegionID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + LastPosition CHAR(64) NOT NULL DEFAULT '<0,0,0>', + LastLookAt CHAR(64) NOT NULL DEFAULT '<0,0,0>', + Online CHAR(5) NOT NULL DEFAULT 'false', + Login CHAR(16) NOT NULL DEFAULT '0', + Logout CHAR(16) NOT NULL DEFAULT '0' +) ; + +COMMIT; diff --git a/OpenSim/Data/SQLite/SQLiteGridUserData.cs b/OpenSim/Data/SQLite/SQLiteGridUserData.cs new file mode 100644 index 0000000000..1bb5ed87d6 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteGridUserData.cs @@ -0,0 +1,61 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQL Interface for user grid data + /// + public class SQLiteGridUserData : SQLiteGenericTableHandler, IGridUserData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public SQLiteGridUserData(string connectionString, string realm) + : base(connectionString, realm, "GridUserStore") {} + + public new GridUserData Get(string userID) + { + GridUserData[] ret = Get("UserID", userID); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs new file mode 100644 index 0000000000..6c01927c5e --- /dev/null +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.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 OpenSimulator 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 OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser +{ + public class ActivityDetector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGridUserService m_GridUserService; + private Scene m_aScene; + + public ActivityDetector(IGridUserService guservice) + { + m_GridUserService = guservice; + m_log.DebugFormat("[ACTIVITY DETECTOR]: starting "); + } + + public void AddRegion(Scene scene) + { + // For now the only events we listen to are these + // But we could trigger the position update more often + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnAvatarEnteringNewParcel += OnEnteringNewParcel; + + if (m_aScene == null) + m_aScene = scene; + } + + public void RemoveRegion(Scene scene) + { + scene.EventManager.OnMakeRootAgent -= OnMakeRootAgent; + scene.EventManager.OnNewClient -= OnNewClient; + } + + public void OnMakeRootAgent(ScenePresence sp) + { + m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); + + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + + public void OnNewClient(IClientAPI client) + { + client.OnConnectionClosed += OnConnectionClose; + } + + public void OnConnectionClose(IClientAPI client) + { + if (client.IsLoggingOut) + { + object sp = null; + Vector3 position = new Vector3(128, 128, 0); + Vector3 lookat = new Vector3(0, 1, 0); + + if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) + { + if (sp is ScenePresence) + { + if (((ScenePresence)sp).IsChildAgent) + return; + + position = ((ScenePresence)sp).AbsolutePosition; + lookat = ((ScenePresence)sp).Lookat; + } + } + m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); + m_GridUserService.LoggedOut(client.AgentId.ToString(), client.Scene.RegionInfo.RegionID, position, lookat); + } + + } + + void OnEnteringNewParcel(ScenePresence sp, int localLandID, UUID regionID) + { + // TODO: grab the parcel ID from ILandModule + // and send that along + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + + } +} diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs new file mode 100644 index 0000000000..e3e2e619c1 --- /dev/null +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs @@ -0,0 +1,153 @@ +/* + * 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 OpenSimulator 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 OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors; + +using OpenMetaverse; +using log4net; +using Nini.Config; + +namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser +{ + public class RemoteGridUserServicesConnector : ISharedRegionModule, IGridUserService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region ISharedRegionModule + + private bool m_Enabled = false; + + private ActivityDetector m_ActivityDetector; + private IGridUserService m_RemoteConnector; + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "RemoteGridUserServicesConnector"; } + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("GridUserServices", ""); + if (name == Name) + { + m_RemoteConnector = new GridUserServicesConnector(source); + + m_Enabled = true; + + m_ActivityDetector = new ActivityDetector(this); + + m_log.Info("[REMOTE GRID USER CONNECTOR]: Remote grid user enabled"); + } + } + + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.RegisterModuleInterface(this); + m_ActivityDetector.AddRegion(scene); + + m_log.InfoFormat("[REMOTE GRID USER CONNECTOR]: Enabled remote grid user for region {0}", scene.RegionInfo.RegionName); + + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_ActivityDetector.RemoveRegion(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + } + + #endregion + + #region IGridUserService + + public GridUserInfo LoggedIn(string userID) + { + m_log.Warn("[REMOTE GRID USER CONNECTOR]: LoggedIn not implemented at the simulators"); + return null; + } + + public bool LoggedOut(string userID, UUID region, Vector3 position, Vector3 lookat) + { + return m_RemoteConnector.LoggedOut(userID, region, position, lookat); + } + + + public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + return m_RemoteConnector.SetHome(userID, regionID, position, lookAt); + } + + public bool SetLastPosition(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + return m_RemoteConnector.SetLastPosition(userID, regionID, position, lookAt); + } + + public GridUserInfo GetGridUserInfo(string userID) + { + return m_RemoteConnector.GetGridUserInfo(userID); + } + + #endregion + + } +} diff --git a/OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs b/OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs new file mode 100644 index 0000000000..66f35e3bdc --- /dev/null +++ b/OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs @@ -0,0 +1,61 @@ +/* + * 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 OpenSimulator 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.GridUser +{ + public class GridUserServiceConnector : ServiceConnector + { + private IGridUserService m_GridUserService; + private string m_ConfigName = "GridUserService"; + + public GridUserServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string service = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (service == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_GridUserService = ServerUtils.LoadPlugin(service, args); + + server.AddStreamHandler(new GridUserServerPostHandler(m_GridUserService)); + } + } +} diff --git a/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs new file mode 100644 index 0000000000..f8fa42967f --- /dev/null +++ b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs @@ -0,0 +1,278 @@ +/* + * 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 OpenSimulator 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.GridUser +{ + public class GridUserServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGridUserService m_GridUserService; + + public GridUserServerPostHandler(IGridUserService service) : + base("POST", "/griduser") + { + m_GridUserService = service; + } + + public override byte[] Handle(string path, Stream requestData, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + string method = string.Empty; + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + method = request["METHOD"].ToString(); + + switch (method) + { + case "loggedin": + return LoggedIn(request); + case "loggedout": + return LoggedOut(request); + case "sethome": + return SetHome(request); + case "setposition": + return SetPosition(request); + case "getgriduserinfo": + return GetGridUserInfo(request); + } + m_log.DebugFormat("[GRID USER HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER HANDLER]: Exception in method {0}: {1}", method, e); + } + + return FailureResult(); + + } + + byte[] LoggedIn(Dictionary request) + { + string user = String.Empty; + + if (!request.ContainsKey("UserID")) + return FailureResult(); + + user = request["UserID"].ToString(); + + GridUserInfo guinfo = m_GridUserService.LoggedIn(user); + + Dictionary result = new Dictionary(); + result["result"] = guinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + //m_log.DebugFormat("[GRID USER HANDLER]: resp string: {0}", xmlString); + UTF8Encoding encoding = new UTF8Encoding(); + return encoding.GetBytes(xmlString); + + } + + byte[] LoggedOut(Dictionary request) + { + string userID = string.Empty; + UUID regionID = UUID.Zero; + Vector3 position = Vector3.Zero; + Vector3 lookat = Vector3.Zero; + + if (!UnpackArgs(request, out userID, out regionID, out position, out lookat)) + return FailureResult(); + + if (m_GridUserService.LoggedOut(userID, regionID, position, lookat)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] SetHome(Dictionary request) + { + string user = string.Empty; + UUID region = UUID.Zero; + Vector3 position = new Vector3(128, 128, 70); + Vector3 look = Vector3.Zero; + + if (!UnpackArgs(request, out user, out region, out position, out look)) + return FailureResult(); + + if (m_GridUserService.SetHome(user, region, position, look)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] SetPosition(Dictionary request) + { + string user = string.Empty; + UUID region = UUID.Zero; + Vector3 position = new Vector3(128, 128, 70); + Vector3 look = Vector3.Zero; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("RegionID")) + return FailureResult(); + + if (!UnpackArgs(request, out user, out region, out position, out look)) + return FailureResult(); + + if (m_GridUserService.SetLastPosition(user, region, position, look)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] GetGridUserInfo(Dictionary request) + { + string user = String.Empty; + + if (!request.ContainsKey("UserID")) + return FailureResult(); + + user = request["UserID"].ToString(); + + GridUserInfo guinfo = m_GridUserService.GetGridUserInfo(user); + + Dictionary result = new Dictionary(); + result["result"] = guinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + //m_log.DebugFormat("[GRID USER HANDLER]: resp string: {0}", xmlString); + UTF8Encoding encoding = new UTF8Encoding(); + return encoding.GetBytes(xmlString); + + } + + private bool UnpackArgs(Dictionary request, out string user, out UUID region, out Vector3 position, out Vector3 lookAt) + { + user = string.Empty; + region = UUID.Zero; + position = new Vector3(128, 128, 70); + lookAt = Vector3.Zero; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("RegionID")) + return false; + + user = request["UserID"].ToString(); + + if (!UUID.TryParse(request["RegionID"].ToString(), out region)) + return false; + + if (request.ContainsKey("Position")) + Vector3.TryParse(request["Position"].ToString(), out position); + + if (request.ContainsKey("LookAt")) + Vector3.TryParse(request["LookAt"].ToString(), out lookAt); + + return true; + } + + + private byte[] SuccessResult() + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "ServerResponse", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "result", ""); + result.AppendChild(doc.CreateTextNode("Success")); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] FailureResult() + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "ServerResponse", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "result", ""); + result.AppendChild(doc.CreateTextNode("Failure")); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] DocToBytes(XmlDocument doc) + { + MemoryStream ms = new MemoryStream(); + XmlTextWriter xw = new XmlTextWriter(ms, null); + xw.Formatting = Formatting.Indented; + doc.WriteTo(xw); + xw.Flush(); + + return ms.ToArray(); + } + + + } +} From bfb7a4999010eb9449cd8855236daac67a469f11 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 7 May 2010 21:56:14 -0700 Subject: [PATCH 100/260] Fixed Presence unit test. Removed unnecessary packing/unpacking of obsolete fields. --- .../Presence/Tests/PresenceConnectorsTests.cs | 4 +--- OpenSim/Services/Interfaces/IPresenceService.cs | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs index e5ded5b5c0..ef910f4c26 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs @@ -90,7 +90,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests PresenceInfo result = m_LocalConnector.GetAgent(session1); Assert.IsNotNull(result, "Retrieved GetAgent is null"); Assert.That(result.UserID, Is.EqualTo(user1), "Retrieved userID does not match"); - Assert.IsTrue(result.Online, "Agent just logged in but is offline"); UUID region1 = UUID.Random(); bool r = m_LocalConnector.ReportAgent(session1, region1); @@ -107,8 +106,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests r = m_LocalConnector.LogoutAgent(session1); Assert.IsTrue(r, "LogoutAgent returned false"); result = m_LocalConnector.GetAgent(session1); - Assert.IsNotNull(result, "Agent session disappeared from storage after logout"); - Assert.IsFalse(result.Online, "Agent is reported to be Online after logout"); + Assert.IsNull(result, "Agent session is still stored after logout"); r = m_LocalConnector.ReportAgent(session1, region1); Assert.IsFalse(r, "ReportAgent of non-logged in user returned true"); diff --git a/OpenSim/Services/Interfaces/IPresenceService.cs b/OpenSim/Services/Interfaces/IPresenceService.cs index 9687d22bea..abbae2c559 100644 --- a/OpenSim/Services/Interfaces/IPresenceService.cs +++ b/OpenSim/Services/Interfaces/IPresenceService.cs @@ -55,10 +55,6 @@ namespace OpenSim.Services.Interfaces UserID = kvp["UserID"].ToString(); if (kvp.ContainsKey("RegionID")) UUID.TryParse(kvp["RegionID"].ToString(), out RegionID); - if (kvp.ContainsKey("lookAt")) - Vector3.TryParse(kvp["lookAt"].ToString(), out LookAt); - if (kvp.ContainsKey("position")) - Vector3.TryParse(kvp["position"].ToString(), out Position); } public Dictionary ToKeyValuePairs() @@ -66,8 +62,6 @@ namespace OpenSim.Services.Interfaces Dictionary result = new Dictionary(); result["UserID"] = UserID; result["RegionID"] = RegionID.ToString(); - result["position"] = Position.ToString(); - result["lookAt"] = LookAt.ToString(); return result; } From a6e7bee75752d7b3a09b100bff39bb49642fa7b3 Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sat, 8 May 2010 01:44:03 -0400 Subject: [PATCH 101/260] More Git/panda integration Testing.. --- README.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.txt b/README.txt index f1a71beadf..a4adf35667 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ Welcome to OpenSim! -== OVERVIEW == +=== OVERVIEW === OpenSim is a BSD Licensed Open Source project to develop a functioning virtual worlds server platform capable of supporting multiple clients @@ -10,12 +10,12 @@ C#, and can run under Mono or the Microsoft .NET runtimes. This is considered an alpha release. Some stuff works, a lot doesn't. If it breaks, you get to keep *both* pieces. -== Compiling OpenSim == +=== Compiling OpenSim === Please see BUILDING.txt if you downloaded a source distribution and need to build OpenSim before running it. -== Running OpenSim on Windows == +=== Running OpenSim on Windows === We recommend that you run OpenSim from a command prompt on Windows in order to capture any errors, though you can also run it by double-clicking @@ -28,7 +28,7 @@ To run OpenSim from a command prompt Now see the "Configuring OpenSim" section -== Running OpenSim on Linux == +=== Running OpenSim on Linux === You will need Mono >= 2.4.2 to run OpenSim. On some Linux distributions you may need to install additional packages. See http://opensimulator.org/wiki/Dependencies @@ -41,7 +41,7 @@ To run OpenSim, from the unpacked distribution type: Now see the "Configuring OpenSim" section -== Configuring OpenSim == +=== Configuring OpenSim === When OpenSim starts for the first time, you will be prompted with a series of questions that look something like: @@ -69,14 +69,14 @@ Helpful resources: * http://opensimulator.org/wiki/Configuring_Regions * http://opensimulator.org/wiki/Mysql-config -== Connecting to your OpenSim == +=== Connecting to your OpenSim === By default your sim will be running on http://127.0.0.1:9000. To use your OpenSim add -loginuri http://127.0.0.1:9000 to your second life client (running on the same machine as your OpenSim). To login, use the same avatar details that you gave to the "create user" console command. -== Bug reports == +=== Bug reports === In the likely event of bugs biting you (err, your OpenSim) we encourage you to see whether the problem has already been reported on @@ -97,7 +97,7 @@ mantis"). Useful information to include: mono --debug OpenSim.exe -== More Information on OpenSim == +=== More Information on OpenSim === More extensive information on building, running, and configuring OpenSim, as well as how to report bugs, and participate in the OpenSim From 6817f4849cba54caca87fef1a0251afcb39609ee Mon Sep 17 00:00:00 2001 From: dahlia Date: Fri, 7 May 2010 22:52:39 -0700 Subject: [PATCH 102/260] test commit for panda --- README.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/README.txt b/README.txt index a4adf35667..e19e58ecad 100644 --- a/README.txt +++ b/README.txt @@ -104,3 +104,4 @@ OpenSim, as well as how to report bugs, and participate in the OpenSim project can always be found at http://opensimulator.org. Thanks for trying OpenSim, we hope it is a pleasant experience. + \ No newline at end of file From d72769930aebb14ae4bfee9803adb13fbb44eb8b Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sat, 8 May 2010 07:44:07 -0700 Subject: [PATCH 103/260] More cleaning on presence. Friends online/offline works again. --- .../RemoteController/RemoteAdminPlugin.cs | 6 +- .../Avatar/Friends/FriendsModule.cs | 87 +++++++++++-------- .../InstantMessage/MessageTransferModule.cs | 78 +++++++---------- .../Avatar/InstantMessage/PresenceModule.cs | 22 ++--- .../Shared/Api/Implementation/LSL_Api.cs | 4 +- .../SimianPresenceServiceConnector.cs | 12 --- .../Services/Interfaces/IPresenceService.cs | 28 ------ .../PresenceService/PresenceService.cs | 6 -- .../Tests/Clients/Presence/PresenceClient.cs | 12 +-- 9 files changed, 101 insertions(+), 154 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index 13f9c9f04f..c5346d4ac0 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -1119,9 +1119,9 @@ namespace OpenSim.ApplicationPlugins.RemoteController } else { - PresenceInfo[] pinfos = m_app.SceneManager.CurrentOrFirstScene.PresenceService.GetAgents(new string[] { account.PrincipalID.ToString() }); - if (pinfos != null && pinfos.Length >= 1) - responseData["lastlogin"] = pinfos[0].Login; + GridUserInfo guinfo = m_app.SceneManager.CurrentOrFirstScene.GridUserService.GetGridUserInfo(account.PrincipalID.ToString()); + if (guinfo != null) + responseData["lastlogin"] = guinfo.Login; else responseData["lastlogin"] = 0; diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index febd4ca993..0c81f446f1 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -345,8 +345,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(client.Scene.RegionInfo.ScopeID, new UUID(im.fromAgentID)); im.fromAgentName = account.FirstName + " " + account.LastName; + PresenceInfo presence = null; PresenceInfo[] presences = PresenceService.GetAgents(new string[] { fid }); - PresenceInfo presence = PresenceInfo.GetOnlinePresence(presences); + if (presences != null && presences.Length > 0) + presence = presences[0]; if (presence != null) im.offline = 0; @@ -380,13 +382,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray()); foreach (PresenceInfo pi in presence) - { - if (pi.Online) - { - online.Add(new UUID(pi.UserID)); - //m_log.DebugFormat("[XXX] {0} friend online {1}", userID, pi.UserID); - } - } + online.Add(new UUID(pi.UserID)); + //m_log.DebugFormat("[XXX] {0} friend online {1}", userID, pi.UserID); return online; } @@ -462,11 +459,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // The friend is not here [as root]. Let's forward. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); - PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions); - if (friendSession != null) + if (friendSessions != null && friendSessions.Length > 0) { - GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); - m_FriendsSimConnector.StatusNotify(region, userID, friendID, online); + PresenceInfo friendSession = friendSessions[0]; + if (friendSession != null) + { + GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + m_FriendsSimConnector.StatusNotify(region, userID, friendID, online); + } } // Friend is not online. Ignore. @@ -504,13 +504,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // The prospective friend is not here [as root]. Let's forward. PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); - PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions); - if (friendSession != null) + if (friendSessions != null && friendSessions.Length > 0) { - GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); - m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message); + PresenceInfo friendSession = friendSessions[0]; + if (friendSession != null) + { + GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + m_FriendsSimConnector.FriendshipOffered(region, agentID, friendID, im.message); + } } - // If the prospective friend is not online, he'll get the message upon login. } @@ -536,14 +538,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // The friend is not here PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); - PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions); - if (friendSession != null) + if (friendSessions != null && friendSessions.Length > 0) { - GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); - m_FriendsSimConnector.FriendshipApproved(region, agentID, client.Name, friendID); - client.SendAgentOnline(new UUID[] { friendID }); + PresenceInfo friendSession = friendSessions[0]; + if (friendSession != null) + { + GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + m_FriendsSimConnector.FriendshipApproved(region, agentID, client.Name, friendID); + client.SendAgentOnline(new UUID[] { friendID }); + } } - } private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List callingCardFolders) @@ -562,11 +566,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return; PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { friendID.ToString() }); - PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions); - if (friendSession != null) + if (friendSessions != null && friendSessions.Length > 0) { - GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); - m_FriendsSimConnector.FriendshipDenied(region, agentID, client.Name, friendID); + PresenceInfo friendSession = friendSessions[0]; + if (friendSession != null) + { + GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + m_FriendsSimConnector.FriendshipDenied(region, agentID, client.Name, friendID); + } } } @@ -589,11 +596,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return; PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() }); - PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions); - if (friendSession != null) + if (friendSessions != null && friendSessions.Length > 0) { - GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); - m_FriendsSimConnector.FriendshipTerminated(region, agentID, exfriendID); + PresenceInfo friendSession = friendSessions[0]; + if (friendSession != null) + { + GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + m_FriendsSimConnector.FriendshipTerminated(region, agentID, exfriendID); + } } } @@ -631,13 +641,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return; PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { target.ToString() }); - PresenceInfo friendSession = PresenceInfo.GetOnlinePresence(friendSessions); - if (friendSession != null) + if (friendSessions != null && friendSessions.Length > 0) { - GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); - // TODO: You might want to send the delta to save the lookup - // on the other end!! - m_FriendsSimConnector.GrantRights(region, requester, target, myFlags, rights); + PresenceInfo friendSession = friendSessions[0]; + if (friendSession != null) + { + GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); + // TODO: You might want to send the delta to save the lookup + // on the other end!! + m_FriendsSimConnector.GrantRights(region, requester, target, myFlags, rights); + } } } } diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs index ad050a1410..5d20e63646 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs @@ -471,7 +471,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage if (m_UserRegionMap.ContainsKey(toAgentID)) { upd = new PresenceInfo(); - upd.Online = true; upd.RegionID = m_UserRegionMap[toAgentID]; // We need to compare the current regionhandle with the previous region handle @@ -493,15 +492,8 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage { // Non-cached user agent lookup. PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); - if (presences != null) - { - foreach (PresenceInfo p in presences) - if (p.Online) - { - upd = presences[0]; - break; - } - } + if (presences != null && presences.Length > 0) + upd = presences[0]; if (upd != null) { @@ -525,61 +517,53 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage if (upd != null) { - if (upd.Online) + GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, + upd.RegionID); + if (reginfo != null) { - GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, - upd.RegionID); - if (reginfo != null) + Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); + // Not actually used anymore, left in for compatibility + // Remove at next interface change + // + msgdata["region_handle"] = 0; + bool imresult = doIMSending(reginfo, msgdata); + if (imresult) { - Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); - // Not actually used anymore, left in for compatibility - // Remove at next interface change - // - msgdata["region_handle"] = 0; - bool imresult = doIMSending(reginfo, msgdata); - if (imresult) + // IM delivery successful, so store the Agent's location in our local cache. + lock (m_UserRegionMap) { - // IM delivery successful, so store the Agent's location in our local cache. - lock (m_UserRegionMap) + if (m_UserRegionMap.ContainsKey(toAgentID)) { - if (m_UserRegionMap.ContainsKey(toAgentID)) - { - m_UserRegionMap[toAgentID] = upd.RegionID; - } - else - { - m_UserRegionMap.Add(toAgentID, upd.RegionID); - } + m_UserRegionMap[toAgentID] = upd.RegionID; + } + else + { + m_UserRegionMap.Add(toAgentID, upd.RegionID); } - result(true); - } - else - { - // try again, but lookup user this time. - // Warning, this must call the Async version - // of this method or we'll be making thousands of threads - // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync - // The version that spawns the thread is SendGridInstantMessageViaXMLRPC - - // This is recursive!!!!! - SendGridInstantMessageViaXMLRPCAsync(im, result, - upd.RegionID); } + result(true); } else { - m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.RegionID); - HandleUndeliveredMessage(im, result); + // try again, but lookup user this time. + // Warning, this must call the Async version + // of this method or we'll be making thousands of threads + // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync + // The version that spawns the thread is SendGridInstantMessageViaXMLRPC + + // This is recursive!!!!! + SendGridInstantMessageViaXMLRPCAsync(im, result, + upd.RegionID); } } else { + m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.RegionID); HandleUndeliveredMessage(im, result); } } else { - m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find user {0}", toAgentID); HandleUndeliveredMessage(im, result); } } diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs index bafad827a6..dd17f3c26b 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs @@ -133,20 +133,14 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage foreach (PresenceInfo pi in status) { UUID uuid = new UUID(pi.UserID); - if (pi.Online) - { - if (!online.Contains(uuid)) - { - online.Add(uuid); - if (offline.Contains(uuid)) - offline.Remove(uuid); - } - } - else - { - if (!online.Contains(uuid) && !offline.Contains(uuid)) - offline.Add(uuid); - } + if (!online.Contains(uuid)) + online.Add(uuid); + } + foreach (string s in args) + { + UUID uuid = new UUID(s); + if (!online.Contains(uuid) && !offline.Contains(uuid)) + offline.Add(uuid); } if (online.Count > 0) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index b2eb585b12..79b6be398a 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -3884,8 +3884,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UserAccount account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, uuid); + PresenceInfo pinfo = null; PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() }); - PresenceInfo pinfo = PresenceInfo.GetOnlinePresence(pinfos); + if (pinfos != null && pinfos.Length > 0) + pinfo = pinfos[0]; if (pinfo == null) return UUID.Zero.ToString(); diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs index e48b7de3bf..b86c45c033 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs @@ -511,20 +511,8 @@ namespace OpenSim.Services.Connectors.SimianGrid PresenceInfo info = new PresenceInfo(); - info.Online = true; info.UserID = sessionResponse["UserID"].AsUUID().ToString(); info.RegionID = sessionResponse["SceneID"].AsUUID(); - info.Position = sessionResponse["ScenePosition"].AsVector3(); - info.LookAt = sessionResponse["SceneLookAt"].AsVector3(); - - if (userResponse != null && userResponse["User"] is OSDMap) - { - OSDMap user = (OSDMap)userResponse["User"]; - - info.Login = user["LastLoginDate"].AsDate(); - info.Logout = user["LastLogoutDate"].AsDate(); - DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt); - } return info; } diff --git a/OpenSim/Services/Interfaces/IPresenceService.cs b/OpenSim/Services/Interfaces/IPresenceService.cs index abbae2c559..8d583fff73 100644 --- a/OpenSim/Services/Interfaces/IPresenceService.cs +++ b/OpenSim/Services/Interfaces/IPresenceService.cs @@ -36,14 +36,6 @@ namespace OpenSim.Services.Interfaces { public string UserID; public UUID RegionID; - public bool Online; - public DateTime Login; - public DateTime Logout; - public Vector3 Position; - public Vector3 LookAt; - public UUID HomeRegionID; - public Vector3 HomePosition; - public Vector3 HomeLookAt; public PresenceInfo() { @@ -65,26 +57,6 @@ namespace OpenSim.Services.Interfaces return result; } - - public static PresenceInfo[] GetOnlinePresences(PresenceInfo[] pinfos) - { - if (pinfos == null) - return null; - - List lst = new List(pinfos); - lst = lst.FindAll(delegate(PresenceInfo each) { return each.Online; }); - - return lst.ToArray(); - } - - public static PresenceInfo GetOnlinePresence(PresenceInfo[] pinfos) - { - pinfos = GetOnlinePresences(pinfos); - if (pinfos != null && pinfos.Length >= 1) - return pinfos[0]; - - return null; - } } public interface IPresenceService diff --git a/OpenSim/Services/PresenceService/PresenceService.cs b/OpenSim/Services/PresenceService/PresenceService.cs index 7e7e98e417..19f636a1c1 100644 --- a/OpenSim/Services/PresenceService/PresenceService.cs +++ b/OpenSim/Services/PresenceService/PresenceService.cs @@ -115,10 +115,6 @@ namespace OpenSim.Services.PresenceService ret.UserID = data.UserID; ret.RegionID = data.RegionID; - if (data.Data.ContainsKey("Position")) - ret.Position = Vector3.Parse(data.Data["Position"]); - if (data.Data.ContainsKey("LookAt")) - ret.LookAt = Vector3.Parse(data.Data["LookAt"]); return ret; } @@ -138,8 +134,6 @@ namespace OpenSim.Services.PresenceService ret.UserID = d.UserID; ret.RegionID = d.RegionID; - ret.Position = Vector3.Parse(d.Data["Position"]); - ret.LookAt = Vector3.Parse(d.Data["LookAt"]); info.Add(ret); } diff --git a/OpenSim/Tests/Clients/Presence/PresenceClient.cs b/OpenSim/Tests/Clients/Presence/PresenceClient.cs index 0f6b80e209..fd3905adcb 100644 --- a/OpenSim/Tests/Clients/Presence/PresenceClient.cs +++ b/OpenSim/Tests/Clients/Presence/PresenceClient.cs @@ -73,8 +73,8 @@ namespace OpenSim.Tests.Clients.PresenceClient if (pinfo == null) m_log.InfoFormat("[PRESENCE CLIENT]: Unable to retrieve presence for {0}", user1); else - m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; Online={1}; regionID={2}; homeRegion={3}", - pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); + m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; regionID={1}", + pinfo.UserID, pinfo.RegionID); System.Console.WriteLine("\n"); success = m_Connector.ReportAgent(session1, region1); @@ -86,8 +86,8 @@ namespace OpenSim.Tests.Clients.PresenceClient if (pinfo == null) m_log.InfoFormat("[PRESENCE CLIENT]: Unable to retrieve presence for {0} for second time", user1); else - m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; Online={1}; regionID={2}; homeRegion={3}", - pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); + m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; regionID={2}", + pinfo.UserID, pinfo.RegionID); System.Console.WriteLine("\n"); success = m_Connector.LogoutAgent(session1); @@ -99,8 +99,8 @@ namespace OpenSim.Tests.Clients.PresenceClient if (pinfo == null) m_log.InfoFormat("[PRESENCE CLIENT]: Unable to retrieve presence for {0} for fourth time", user1); else - m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; Online={1}; regionID={2}; homeRegion={3}", - pinfo.UserID, pinfo.Online, pinfo.RegionID, pinfo.HomeRegionID); + m_log.InfoFormat("[PRESENCE CLIENT]: Presence retrieved correctly: userID={0}; regionID={1}", + pinfo.UserID, pinfo.RegionID); System.Console.WriteLine("\n"); success = m_Connector.ReportAgent(session1, UUID.Random()); From c1fe07b02248b66d45fbbbd6f8e3db3fe3bdc157 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sat, 8 May 2010 12:21:17 -0700 Subject: [PATCH 104/260] * Added missing loggout notification to home grid upon agents logging out in foreign grids. * Added missing config in StandaloneHypergrid.ini --- .../Framework/EntityTransfer/HGEntityTransferModule.cs | 8 ++++++++ OpenSim/Services/HypergridService/UserAgentService.cs | 4 ++++ bin/config-include/StandaloneHypergrid.ini | 3 +++ 3 files changed, 15 insertions(+) diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index 137dfec13b..7d26e3ff5f 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs @@ -245,6 +245,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return; } + // Let's find out if this is a foreign user or a local user + UserAccount account = m_aScene.UserAccountService.GetUserAccount(m_aScene.RegionInfo.ScopeID, obj.AgentId); + if (account != null) + { + // local grid user + return; + } + AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 3af7ef9d3f..64f7e8aaca 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -185,6 +185,10 @@ namespace OpenSim.Services.HypergridService foreach (UUID session in travels) m_TravelingAgents.Remove(session); } + + GridUserInfo guinfo = m_GridUserService.GetGridUserInfo(userID.ToString()); + if (guinfo != null) + m_GridUserService.LoggedOut(userID.ToString(), guinfo.LastRegionID, guinfo.LastPosition, guinfo.LastLookAt); } // We need to prevent foreign users with the same UUID as a local user diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index 130e210a77..32b240b8b5 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -77,6 +77,9 @@ GridService = "OpenSim.Services.GridService.dll:GridService" InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" +[GridUserService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + [FriendsService] LocalServiceModule = "OpenSim.Services.FriendsService.dll" ConnectionString = "URI=file:friends.db,version=3" From 9b22393cf308507dc751704c8b0d3e65ac1d4323 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 9 May 2010 17:02:22 +0100 Subject: [PATCH 105/260] Add a field asset_flags and a corresponding enum to the asset database. This CHANGES THE ASSET SERVER PROTOCOL and means you CAN NOT MIX PRIOR VERSIONS WITH LATER ONES. It may also eat your babies, yada, yada, yada. The usual cautions for migrations to the assets table apply. Coding: Can not guarantee nut free. --- OpenSim/Data/MySQL/MySQLAssetData.cs | 8 ++++--- .../Data/MySQL/Resources/007_AssetStore.sql | 5 +++++ OpenSim/Framework/AssetBase.cs | 22 +++++++++++++++++++ OpenSim/Services/AssetService/AssetService.cs | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 OpenSim/Data/MySQL/Resources/007_AssetStore.sql diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index d55369a946..5a2af4f9ac 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -161,8 +161,8 @@ namespace OpenSim.Data.MySQL MySqlCommand cmd = new MySqlCommand( - "replace INTO assets(id, name, description, assetType, local, temporary, create_time, access_time, data)" + - "VALUES(?id, ?name, ?description, ?assetType, ?local, ?temporary, ?create_time, ?access_time, ?data)", + "replace INTO assets(id, name, description, assetType, local, temporary, create_time, access_time, asset_flags, data)" + + "VALUES(?id, ?name, ?description, ?assetType, ?local, ?temporary, ?create_time, ?access_time, ?asset_flags, ?data)", dbcon); string assetName = asset.Name; @@ -194,6 +194,7 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("?temporary", asset.Temporary); cmd.Parameters.AddWithValue("?create_time", now); cmd.Parameters.AddWithValue("?access_time", now); + cmd.Parameters.AddWithValue("?asset_flags", (int)asset.Flags); cmd.Parameters.AddWithValue("?data", asset.Data); cmd.ExecuteNonQuery(); cmd.Dispose(); @@ -302,7 +303,7 @@ namespace OpenSim.Data.MySQL using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { dbcon.Open(); - MySqlCommand cmd = new MySqlCommand("SELECT name,description,assetType,temporary,id FROM assets LIMIT ?start, ?count", dbcon); + MySqlCommand cmd = new MySqlCommand("SELECT name,description,assetType,temporary,id,asset_flags FROM assets LIMIT ?start, ?count", dbcon); cmd.Parameters.AddWithValue("?start", start); cmd.Parameters.AddWithValue("?count", count); @@ -317,6 +318,7 @@ namespace OpenSim.Data.MySQL metadata.Description = (string)dbReader["description"]; metadata.Type = (sbyte)dbReader["assetType"]; metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); metadata.FullID = new UUID((string)dbReader["id"]); // Current SHA1s are not stored/computed. diff --git a/OpenSim/Data/MySQL/Resources/007_AssetStore.sql b/OpenSim/Data/MySQL/Resources/007_AssetStore.sql new file mode 100644 index 0000000000..f06121abc1 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/007_AssetStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE assets ADD COLUMN asset_flags INTEGER NOT NULL DEFAULT 0; + +COMMIT; diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs index 19ca232208..7ecf198bd8 100644 --- a/OpenSim/Framework/AssetBase.cs +++ b/OpenSim/Framework/AssetBase.cs @@ -33,6 +33,15 @@ using OpenMetaverse; namespace OpenSim.Framework { + [Flags] + public enum AssetFlags : int + { + Normal = 0, + Maptile = 1, + Rewritable = 2, + Collectable = 4 + } + /// /// Asset class. All Assets are reference by this class or a class derived from this class /// @@ -206,6 +215,12 @@ namespace OpenSim.Framework set { m_metadata.Temporary = value; } } + public AssetFlags Flags + { + get { return m_metadata.Flags; } + set { m_metadata.Flags = value; } + } + [XmlIgnore] public AssetMetadata Metadata { @@ -233,6 +248,7 @@ namespace OpenSim.Framework private bool m_local; private bool m_temporary; private string m_creatorid; + private AssetFlags m_flags; public UUID FullID { @@ -330,5 +346,11 @@ namespace OpenSim.Framework get { return m_creatorid; } set { m_creatorid = value; } } + + public AssetFlags Flags + { + get { return m_flags; } + set { m_flags = value; } + } } } diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index 4e512e729c..2114933ec7 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -181,6 +181,7 @@ namespace OpenSim.Services.AssetService MainConsole.Instance.Output(String.Format("Description: {0}", asset.Description)); MainConsole.Instance.Output(String.Format("Type: {0}", asset.Type)); MainConsole.Instance.Output(String.Format("Content-type: {0}", asset.Metadata.ContentType)); + MainConsole.Instance.Output(String.Format("Flags: {0}", asset.Metadata.Flags.ToString())); for (i = 0 ; i < 5 ; i++) { From 60357d3778c95a47481f790803b7af39c70cde9c Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 9 May 2010 17:56:52 +0100 Subject: [PATCH 106/260] Implement the "delete" path for assets. Adds a new option to allow remote asset deletion in robust handler. --- OpenSim/Data/AssetDataBase.cs | 1 + OpenSim/Data/IAssetData.cs | 1 + OpenSim/Data/MSSQL/MSSQLAssetData.cs | 4 ++++ OpenSim/Data/MySQL/MySQLAssetData.cs | 18 ++++++++++++++++++ OpenSim/Data/SQLite/SQLiteAssetData.cs | 5 +++++ OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs | 6 +++++- .../Handlers/Asset/AssetServerConnector.cs | 4 +++- .../Handlers/Asset/AssetServerDeleteHandler.cs | 8 +++++--- OpenSim/Services/AssetService/AssetService.cs | 11 +++++++++++ .../Tests/Common/Mock/MockAssetDataPlugin.cs | 7 ++++++- bin/Robust.ini.example | 1 + 11 files changed, 60 insertions(+), 6 deletions(-) diff --git a/OpenSim/Data/AssetDataBase.cs b/OpenSim/Data/AssetDataBase.cs index 5deb44e61e..e1a810c9af 100644 --- a/OpenSim/Data/AssetDataBase.cs +++ b/OpenSim/Data/AssetDataBase.cs @@ -48,5 +48,6 @@ namespace OpenSim.Data public abstract void Initialise(string connect); public abstract void Initialise(); public abstract void Dispose(); + public abstract bool Delete(string id); } } diff --git a/OpenSim/Data/IAssetData.cs b/OpenSim/Data/IAssetData.cs index 2149bcac0a..90d5eeb489 100644 --- a/OpenSim/Data/IAssetData.cs +++ b/OpenSim/Data/IAssetData.cs @@ -38,6 +38,7 @@ namespace OpenSim.Data bool ExistsAsset(UUID uuid); List FetchAssetMetadataSet(int start, int count); void Initialise(string connect); + bool Delete(string id); } public class AssetDataInitialiser : PluginInitialiserBase diff --git a/OpenSim/Data/MSSQL/MSSQLAssetData.cs b/OpenSim/Data/MSSQL/MSSQLAssetData.cs index d6ea26254a..8475b22022 100644 --- a/OpenSim/Data/MSSQL/MSSQLAssetData.cs +++ b/OpenSim/Data/MSSQL/MSSQLAssetData.cs @@ -322,6 +322,10 @@ namespace OpenSim.Data.MSSQL return retList; } + public override bool Delete(string id) + { + return false; + } #endregion } } diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index 5a2af4f9ac..35eed56813 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -338,6 +338,24 @@ namespace OpenSim.Data.MySQL return retList; } + public override bool Delete(string id) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + MySqlCommand cmd = new MySqlCommand("delete from assets where id=?id"); + cmd.Parameters.AddWithValue("?id", id); + cmd.ExecuteNonQuery(); + + cmd.Dispose(); + } + } + + return true; + } + #endregion } } diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 636bf8645b..9b938fab73 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -338,6 +338,11 @@ namespace OpenSim.Data.SQLite get { return "SQLite Asset storage engine"; } } + public override bool Delete(string id) + { + return false; + } + #endregion } } diff --git a/OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs b/OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs index 0d63deacd8..df509023eb 100644 --- a/OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLiteLegacy/SQLiteAssetData.cs @@ -338,6 +338,10 @@ namespace OpenSim.Data.SQLiteLegacy get { return "SQLite Asset storage engine"; } } + public override bool Delete(string id) + { + return false; + } #endregion } -} \ No newline at end of file +} diff --git a/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs b/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs index f7eb292091..b6425f4c49 100644 --- a/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs +++ b/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs @@ -59,9 +59,11 @@ namespace OpenSim.Server.Handlers.Asset m_AssetService = ServerUtils.LoadPlugin(assetService, args); + bool allowDelete = serverConfig.GetBoolean("AllowRemoteDelete", false); + server.AddStreamHandler(new AssetServerGetHandler(m_AssetService)); server.AddStreamHandler(new AssetServerPostHandler(m_AssetService)); - server.AddStreamHandler(new AssetServerDeleteHandler(m_AssetService)); + server.AddStreamHandler(new AssetServerDeleteHandler(m_AssetService, allowDelete)); } } } diff --git a/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs b/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs index f33bb90fde..8014fb5354 100644 --- a/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs +++ b/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs @@ -47,11 +47,13 @@ namespace OpenSim.Server.Handlers.Asset // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IAssetService m_AssetService; + protected bool m_allowDelete; - public AssetServerDeleteHandler(IAssetService service) : + public AssetServerDeleteHandler(IAssetService service, bool allowDelete) : base("DELETE", "/assets") { m_AssetService = service; + m_allowDelete = allowDelete; } public override byte[] Handle(string path, Stream request, @@ -61,9 +63,9 @@ namespace OpenSim.Server.Handlers.Asset string[] p = SplitParams(path); - if (p.Length > 0) + if (p.Length > 0 && m_allowDelete) { - // result = m_AssetService.Delete(p[0]); + result = m_AssetService.Delete(p[0]); } XmlSerializer xs = new XmlSerializer(typeof(bool)); diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index 2114933ec7..4fc38f3173 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -156,6 +156,17 @@ namespace OpenSim.Services.AssetService public bool Delete(string id) { + UUID assetID; + if (!UUID.TryParse(id, out assetID)) + return false; + + AssetBase asset = m_Database.GetAsset(assetID); + if (asset == null) + return false; + + if ((int)(asset.Flags & AssetFlags.Maptile) != 0) + return m_Database.Delete(id); + return false; } diff --git a/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs b/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs index cc1dfbf02a..4a15cf2d64 100644 --- a/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs +++ b/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs @@ -60,5 +60,10 @@ namespace OpenSim.Tests.Common.Mock } public List FetchAssetMetadataSet(int start, int count) { return new List(count); } + + public bool Delete(string id) + { + return false; + } } -} \ No newline at end of file +} diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 502a8e656a..f1b91269a7 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -40,6 +40,7 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003 LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" AssetLoaderArgs = "assets/AssetSets.xml" + AllowRemoteDelete = "false" ; * This configuration loads the inventory server modules. It duplicates ; * the function of the legacy inventory server From bc6995f92123ff29fdfd6f811d3d252d99284527 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 9 May 2010 18:02:36 +0100 Subject: [PATCH 107/260] Add Delete handler to SQLite (NG) --- OpenSim/Data/SQLite/SQLiteAssetData.cs | 31 +++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 9b938fab73..2783ba1556 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -207,20 +207,6 @@ namespace OpenSim.Data.SQLite } } - /// - /// Delete an asset from database - /// - /// - public void DeleteAsset(UUID uuid) - { - using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn)) - { - cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); - - cmd.ExecuteNonQuery(); - } - } - /// /// /// @@ -340,7 +326,22 @@ namespace OpenSim.Data.SQLite public override bool Delete(string id) { - return false; + UUID assetID; + + if (!UUID.TryParse(id, out assetID)) + return false; + + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", assetID.ToString())); + + cmd.ExecuteNonQuery(); + } + } + + return true; } #endregion From b233a4b2cab3a39f9edc17130cd7c2f2f807d6bb Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 9 May 2010 13:39:56 -0700 Subject: [PATCH 108/260] * Fixed spamming the assets table with map tiles. The tile image ID is now stored in regionsettings. Upon generation of a new tile image, the old one is deleted. Tested for SQLite and MySql standalone. * Fixed small bug with map search where the local sim regions weren't found. --- OpenSim/Data/MySQL/MySQLAssetData.cs | 5 +-- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 6 +++- .../Data/MySQL/Resources/033_RegionStore.sql | 3 ++ OpenSim/Data/Null/NullRegionData.cs | 8 +++-- .../Data/SQLite/Resources/005_AssetStore.sql | 5 +++ .../Data/SQLite/Resources/019_RegionStore.sql | 5 +++ OpenSim/Data/SQLite/SQLiteAssetData.cs | 10 ++++-- OpenSim/Data/SQLite/SQLiteRegionData.cs | 3 ++ OpenSim/Framework/AssetBase.cs | 8 ++--- OpenSim/Region/Application/OpenSimBase.cs | 2 +- .../Avatar/Assets/GetTextureModule.cs | 2 ++ .../Grid/RemoteGridServiceConnector.cs | 4 ++- .../World/WorldMap/WorldMapModule.cs | 31 +++++++++---------- .../Framework/Interfaces/IWorldMapModule.cs | 2 +- OpenSim/Region/Framework/Scenes/Scene.cs | 6 ++-- OpenSim/Services/AssetService/AssetService.cs | 5 +++ OpenSim/Services/GridService/GridService.cs | 3 +- 17 files changed, 72 insertions(+), 36 deletions(-) create mode 100644 OpenSim/Data/MySQL/Resources/033_RegionStore.sql create mode 100644 OpenSim/Data/SQLite/Resources/005_AssetStore.sql create mode 100644 OpenSim/Data/SQLite/Resources/019_RegionStore.sql diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index 35eed56813..13f5fa2ac6 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -111,7 +111,7 @@ namespace OpenSim.Data.MySQL dbcon.Open(); using (MySqlCommand cmd = new MySqlCommand( - "SELECT name, description, assetType, local, temporary, data FROM assets WHERE id=?id", + "SELECT name, description, assetType, local, temporary, asset_flags, data FROM assets WHERE id=?id", dbcon)) { cmd.Parameters.AddWithValue("?id", assetID.ToString()); @@ -133,6 +133,7 @@ namespace OpenSim.Data.MySQL asset.Local = false; asset.Temporary = Convert.ToBoolean(dbReader["temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); } } } @@ -345,7 +346,7 @@ namespace OpenSim.Data.MySQL using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { dbcon.Open(); - MySqlCommand cmd = new MySqlCommand("delete from assets where id=?id"); + MySqlCommand cmd = new MySqlCommand("delete from assets where id=?id", dbcon); cmd.Parameters.AddWithValue("?id", id); cmd.ExecuteNonQuery(); diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index a395ddc3e7..8c83ef13f7 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -989,7 +989,8 @@ namespace OpenSim.Data.MySQL "?TerrainLowerLimit, ?UseEstateSun, ?FixedSun, " + "?SunPosition, ?Covenant, ?Sandbox, " + "?SunVectorX, ?SunVectorY, ?SunVectorZ, " + - "?LoadedCreationDateTime, ?LoadedCreationID)"; + "?LoadedCreationDateTime, ?LoadedCreationID)" + + "?map_tile_ID, ?TerrainImageID"; FillRegionSettingsCommand(cmd, rs); @@ -1276,6 +1277,8 @@ namespace OpenSim.Data.MySQL else newSettings.LoadedCreationID = (String) row["loaded_creation_id"]; + newSettings.TerrainImageID = new UUID((String)row["map_tile_ID"]); + return newSettings; } @@ -1596,6 +1599,7 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("Covenant", settings.Covenant.ToString()); cmd.Parameters.AddWithValue("LoadedCreationDateTime", settings.LoadedCreationDateTime); cmd.Parameters.AddWithValue("LoadedCreationID", settings.LoadedCreationID); + cmd.Parameters.AddWithValue("TerrainImageID", settings.TerrainImageID); } diff --git a/OpenSim/Data/MySQL/Resources/033_RegionStore.sql b/OpenSim/Data/MySQL/Resources/033_RegionStore.sql new file mode 100644 index 0000000000..2832b413ff --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/033_RegionStore.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE regionsettings ADD map_tile_ID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +COMMIT; diff --git a/OpenSim/Data/Null/NullRegionData.cs b/OpenSim/Data/Null/NullRegionData.cs index 30ad747d25..d596698127 100644 --- a/OpenSim/Data/Null/NullRegionData.cs +++ b/OpenSim/Data/Null/NullRegionData.cs @@ -40,7 +40,7 @@ namespace OpenSim.Data.Null { private static NullRegionData Instance = null; -// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); Dictionary m_regionData = new Dictionary(); @@ -62,12 +62,14 @@ namespace OpenSim.Data.Null { if (regionName.Contains("%")) { - if (r.RegionName.Contains(regionName.Replace("%", ""))) + string cleanname = regionName.Replace("%", ""); + m_log.DebugFormat("[NULL REGION DATA]: comparing {0} to {1}", cleanname.ToLower(), r.RegionName.ToLower()); + if (r.RegionName.ToLower().Contains(cleanname.ToLower())) ret.Add(r); } else { - if (r.RegionName == regionName) + if (r.RegionName.ToLower() == regionName.ToLower()) ret.Add(r); } } diff --git a/OpenSim/Data/SQLite/Resources/005_AssetStore.sql b/OpenSim/Data/SQLite/Resources/005_AssetStore.sql new file mode 100644 index 0000000000..f06121abc1 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/005_AssetStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE assets ADD COLUMN asset_flags INTEGER NOT NULL DEFAULT 0; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/019_RegionStore.sql b/OpenSim/Data/SQLite/Resources/019_RegionStore.sql new file mode 100644 index 0000000000..d62f8484b8 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/019_RegionStore.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE regionsettings ADD COLUMN map_tile_ID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 2783ba1556..7d6df8d627 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -46,8 +46,8 @@ namespace OpenSim.Data.SQLite private const string SelectAssetSQL = "select * from assets where UUID=:UUID"; private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, UUID from assets limit :start, :count"; private const string DeleteAssetSQL = "delete from assets where UUID=:UUID"; - private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Data)"; - private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, Data=:Data where UUID=:UUID"; + private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, asset_flags, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Flags, :Data)"; + private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, asset_flags=:Flags, Data=:Data where UUID=:UUID"; private const string assetSelect = "select * from assets"; private SqliteConnection m_conn; @@ -136,6 +136,7 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type)); cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); + cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); cmd.ExecuteNonQuery(); @@ -154,6 +155,7 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type)); cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); + cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); cmd.ExecuteNonQuery(); @@ -227,7 +229,8 @@ namespace OpenSim.Data.SQLite asset.Description = (String) row["Description"]; asset.Local = Convert.ToBoolean(row["Local"]); asset.Temporary = Convert.ToBoolean(row["Temporary"]); - asset.Data = (byte[]) row["Data"]; + asset.Flags = (AssetFlags)Convert.ToInt32(row["asset_flags"]); + asset.Data = (byte[])row["Data"]; return asset; } @@ -240,6 +243,7 @@ namespace OpenSim.Data.SQLite metadata.Description = (string) row["Description"]; metadata.Type = Convert.ToSByte(row["Type"]); metadata.Temporary = Convert.ToBoolean(row["Temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(row["asset_flags"]); // Current SHA1s are not stored/computed. metadata.SHA1 = new byte[] {}; diff --git a/OpenSim/Data/SQLite/SQLiteRegionData.cs b/OpenSim/Data/SQLite/SQLiteRegionData.cs index 997664a45c..85703dc2ca 100644 --- a/OpenSim/Data/SQLite/SQLiteRegionData.cs +++ b/OpenSim/Data/SQLite/SQLiteRegionData.cs @@ -1156,6 +1156,7 @@ namespace OpenSim.Data.SQLite createCol(regionsettings, "fixed_sun", typeof (Int32)); createCol(regionsettings, "sun_position", typeof (Double)); createCol(regionsettings, "covenant", typeof(String)); + createCol(regionsettings, "map_tile_ID", typeof(String)); regionsettings.PrimaryKey = new DataColumn[] { regionsettings.Columns["regionUUID"] }; return regionsettings; } @@ -1474,6 +1475,7 @@ namespace OpenSim.Data.SQLite newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); newSettings.Covenant = new UUID((String) row["covenant"]); + newSettings.TerrainImageID = new UUID((String)row["map_tile_ID"]); return newSettings; } @@ -1792,6 +1794,7 @@ namespace OpenSim.Data.SQLite row["fixed_sun"] = settings.FixedSun; row["sun_position"] = settings.SunPosition; row["covenant"] = settings.Covenant.ToString(); + row["map_tile_ID"] = settings.TerrainImageID.ToString(); } /// diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs index 7ecf198bd8..53d28be088 100644 --- a/OpenSim/Framework/AssetBase.cs +++ b/OpenSim/Framework/AssetBase.cs @@ -36,10 +36,10 @@ namespace OpenSim.Framework [Flags] public enum AssetFlags : int { - Normal = 0, - Maptile = 1, - Rewritable = 2, - Collectable = 4 + Normal = 0, // Immutable asset + Maptile = 1, // What it says + Rewritable = 2, // Content can be rewritten + Collectable = 4 // Can be GC'ed after some time } /// diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index 06ffa917f5..83be61ee63 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -349,7 +349,7 @@ namespace OpenSim // moved these here as the terrain texture has to be created after the modules are initialized // and has to happen before the region is registered with the grid. - scene.CreateTerrainTexture(false); + scene.CreateTerrainTexture(); // TODO : Try setting resource for region xstats here on scene MainServer.Instance.AddStreamHandler(new Region.Framework.Scenes.RegionStatsHandler(regionInfo)); diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs index 53d2cefeac..f8e3d595c9 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs @@ -121,6 +121,7 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps UUID textureID; if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out textureID)) { + //m_log.DebugFormat("[GETTEXTURE]: {0}", textureID); AssetBase texture; if (!String.IsNullOrEmpty(REDIRECT_URL)) @@ -167,6 +168,7 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps private void SendTexture(OSHttpRequest request, OSHttpResponse response, AssetBase texture) { string range = request.Headers.GetOne("Range"); + //m_log.DebugFormat("[GETTEXTURE]: Range {0}", range); if (!String.IsNullOrEmpty(range)) { // Range request diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs index d44ddf4b1a..46741a58ec 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Grid/RemoteGridServiceConnector.cs @@ -197,7 +197,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid if (grinfo != null) { //m_log.DebugFormat("[REMOTE GRID CONNECTOR]: Remote GetRegionsByName {0} found {1} regions", name, grinfo.Count); - rinfo.AddRange(grinfo); + foreach (GridRegion r in grinfo) + if (rinfo.Find(delegate(GridRegion gr) { return gr.RegionID == r.RegionID; }) == null) + rinfo.Add(r); } return rinfo; diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 2b0e83f52f..ac6a633413 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -1000,7 +1000,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap return responsemap; } - public void LazySaveGeneratedMaptile(byte[] data, bool temporary) + public void RegenerateMaptile(byte[] data) { // Overwrites the local Asset cache with new maptile data // Assets are single write, this causes the asset server to ignore this update, @@ -1010,7 +1010,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap // map tile while protecting the (grid) asset database from bloat caused by a new asset each // time a mapimage is generated! - UUID lastMapRegionUUID = m_scene.RegionInfo.lastMapUUID; + UUID lastMapRegionUUID = m_scene.RegionInfo.RegionSettings.TerrainImageID; int lastMapRefresh = 0; int twoDays = 172800; @@ -1030,21 +1030,9 @@ namespace OpenSim.Region.CoreModules.World.WorldMap { } - UUID TerrainImageUUID = UUID.Random(); + m_log.Debug("[MAPTILE]: STORING MAPTILE IMAGE"); - if (lastMapRegionUUID == UUID.Zero || (lastMapRefresh + RefreshSeconds) < Util.UnixTimeSinceEpoch()) - { - m_scene.RegionInfo.SaveLastMapUUID(TerrainImageUUID); - - m_log.Debug("[MAPTILE]: STORING MAPTILE IMAGE"); - } - else - { - TerrainImageUUID = lastMapRegionUUID; - m_log.Debug("[MAPTILE]: REUSING OLD MAPTILE IMAGE ID"); - } - - m_scene.RegionInfo.RegionSettings.TerrainImageID = TerrainImageUUID; + m_scene.RegionInfo.RegionSettings.TerrainImageID = UUID.Random(); AssetBase asset = new AssetBase( m_scene.RegionInfo.RegionSettings.TerrainImageID, @@ -1053,8 +1041,17 @@ namespace OpenSim.Region.CoreModules.World.WorldMap m_scene.RegionInfo.RegionID.ToString()); asset.Data = data; asset.Description = m_scene.RegionInfo.RegionName; - asset.Temporary = temporary; + asset.Temporary = false; + asset.Flags = AssetFlags.Maptile; + + // Store the new one + m_log.DebugFormat("[WORLDMAP]: Storing map tile {0}", asset.ID); m_scene.AssetService.Store(asset); + m_scene.RegionInfo.RegionSettings.Save(); + + // Delete the old one + m_log.DebugFormat("[WORLDMAP]: Deleting old map tile {0}", lastMapRegionUUID); + m_scene.AssetService.Delete(lastMapRegionUUID.ToString()); } private void MakeRootAgent(ScenePresence avatar) diff --git a/OpenSim/Region/Framework/Interfaces/IWorldMapModule.cs b/OpenSim/Region/Framework/Interfaces/IWorldMapModule.cs index de1bcd4382..ac6afed9ce 100644 --- a/OpenSim/Region/Framework/Interfaces/IWorldMapModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IWorldMapModule.cs @@ -29,6 +29,6 @@ namespace OpenSim.Region.Framework.Interfaces { public interface IWorldMapModule { - void LazySaveGeneratedMaptile(byte[] data, bool temporary); + void RegenerateMaptile(byte[] data); } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index c0fa7b4289..edbef4c884 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1823,7 +1823,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// Create a terrain texture for this scene /// - public void CreateTerrainTexture(bool temporary) + public void CreateTerrainTexture() { //create a texture asset of the terrain IMapImageGenerator terrain = RequestModuleInterface(); @@ -1841,7 +1841,9 @@ namespace OpenSim.Region.Framework.Scenes IWorldMapModule mapModule = RequestModuleInterface(); if (mapModule != null) - mapModule.LazySaveGeneratedMaptile(data, temporary); + mapModule.RegenerateMaptile(data); + else + m_log.DebugFormat("[SCENE]: MapModule is null, can't save maptile"); } } diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs index 4fc38f3173..470a4ddcc5 100644 --- a/OpenSim/Services/AssetService/AssetService.cs +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -156,6 +156,7 @@ namespace OpenSim.Services.AssetService public bool Delete(string id) { + m_log.DebugFormat("[ASSET SERVICE]: Deleting asset {0}", id); UUID assetID; if (!UUID.TryParse(id, out assetID)) return false; @@ -165,7 +166,11 @@ namespace OpenSim.Services.AssetService return false; if ((int)(asset.Flags & AssetFlags.Maptile) != 0) + { return m_Database.Delete(id); + } + else + m_log.DebugFormat("[ASSET SERVICE]: Request to delete asset {0}, but flags are not Maptile", id); return false; } diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index 4089fcee18..7c9864270c 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -324,6 +324,7 @@ namespace OpenSim.Services.GridService if (rdatas != null) { + m_log.DebugFormat("[GRID SERVICE]: Found {0} regions", rdatas.Count); foreach (RegionData rdata in rdatas) { if (count++ < maxNumber) @@ -331,7 +332,7 @@ namespace OpenSim.Services.GridService } } - if (m_AllowHypergridMapSearch && (rdatas == null || (rdatas != null && rdatas.Count == 0) && name.Contains("."))) + if (m_AllowHypergridMapSearch && (rdatas == null || (rdatas != null && rdatas.Count == 0)) && name.Contains(".")) { GridRegion r = m_HypergridLinker.LinkRegion(scopeID, name); if (r != null) From 9cf6b81256b6c92cb5d5fb7a6db697f7784191e4 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 9 May 2010 14:02:02 -0700 Subject: [PATCH 109/260] Yey for unit tests. The previous commit had a couple of bugs on SQL statements. Fixed here. --- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 4 ++-- OpenSim/Data/SQLite/SQLiteAssetData.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index 8c83ef13f7..d2892e9354 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -989,8 +989,8 @@ namespace OpenSim.Data.MySQL "?TerrainLowerLimit, ?UseEstateSun, ?FixedSun, " + "?SunPosition, ?Covenant, ?Sandbox, " + "?SunVectorX, ?SunVectorY, ?SunVectorZ, " + - "?LoadedCreationDateTime, ?LoadedCreationID)" + - "?map_tile_ID, ?TerrainImageID"; + "?LoadedCreationDateTime, ?LoadedCreationID, " + + "?map_tile_ID)"; FillRegionSettingsCommand(cmd, rs); diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 7d6df8d627..7081f99054 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -44,7 +44,7 @@ namespace OpenSim.Data.SQLite // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private const string SelectAssetSQL = "select * from assets where UUID=:UUID"; - private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, UUID from assets limit :start, :count"; + private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, asset_flags, UUID from assets limit :start, :count"; private const string DeleteAssetSQL = "delete from assets where UUID=:UUID"; private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, asset_flags, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Flags, :Data)"; private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, asset_flags=:Flags, Data=:Data where UUID=:UUID"; From 6f2f0fa0cad4c2668826f2684f5b8c2d9b30f243 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 9 May 2010 14:12:59 -0700 Subject: [PATCH 110/260] OK, this really fixes it, I promise. --- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index d2892e9354..07371e7473 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -975,7 +975,7 @@ namespace OpenSim.Data.MySQL "use_estate_sun, fixed_sun, sun_position, " + "covenant, Sandbox, sunvectorx, sunvectory, " + "sunvectorz, loaded_creation_datetime, " + - "loaded_creation_id) values (?RegionUUID, ?BlockTerraform, " + + "loaded_creation_id, map_tile_ID) values (?RegionUUID, ?BlockTerraform, " + "?BlockFly, ?AllowDamage, ?RestrictPushing, " + "?AllowLandResell, ?AllowLandJoinDivide, " + "?BlockShowInSearch, ?AgentLimit, ?ObjectBonus, " + @@ -990,7 +990,7 @@ namespace OpenSim.Data.MySQL "?SunPosition, ?Covenant, ?Sandbox, " + "?SunVectorX, ?SunVectorY, ?SunVectorZ, " + "?LoadedCreationDateTime, ?LoadedCreationID, " + - "?map_tile_ID)"; + "?TerrainImageID)"; FillRegionSettingsCommand(cmd, rs); From 89c762209c9d8fb4cf74c0e89f33caf0f4962f44 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 10 May 2010 03:57:17 +0100 Subject: [PATCH 111/260] Fix a null ref on region crossing --- .../Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 79b6be398a..7a9a92d175 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -5596,7 +5596,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_String llGetLandOwnerAt(LSL_Vector pos) { m_host.AddScriptLPS(1); - return World.LandChannel.GetLandObject((float)pos.x, (float)pos.y).LandData.OwnerID.ToString(); + ILandObject land = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y); + if (land == null) + return UUID.Zero.ToString(); + return land.LandData.OwnerID.ToString(); } /// From 31dc77d8a1452d7635a7ae167fb0066fcf99a115 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 10 May 2010 04:02:56 +0100 Subject: [PATCH 112/260] Return agents when angle is PI Fixes Mantis #4703 --- .../Shared/Api/Implementation/Plugins/SensorRepeat.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index 4d7ead6553..5c2abd5e9d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -472,6 +472,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins sensedEntities.Add(new SensedEntity(dis, presence.UUID)); } } + else + { + sensedEntities.Add(new SensedEntity(dis, presence.UUID)); + } } }); From 739b5e2c08a7b996813506b694c725c2c8d50524 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 10 May 2010 08:01:00 -0700 Subject: [PATCH 113/260] Fixed a typo in the Friends packaging (Pricipal -> Principal) that was making offline friendship offers fail in grids. --- OpenSim/Services/Interfaces/IFriendsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/Interfaces/IFriendsService.cs b/OpenSim/Services/Interfaces/IFriendsService.cs index 2692c48f39..0ddd5e53a9 100644 --- a/OpenSim/Services/Interfaces/IFriendsService.cs +++ b/OpenSim/Services/Interfaces/IFriendsService.cs @@ -62,7 +62,7 @@ namespace OpenSim.Services.Interfaces public Dictionary ToKeyValuePairs() { Dictionary result = new Dictionary(); - result["PricipalID"] = PrincipalID.ToString(); + result["PrincipalID"] = PrincipalID.ToString(); result["Friend"] = Friend; result["MyFlags"] = MyFlags.ToString(); result["TheirFlags"] = TheirFlags.ToString(); From 836849455431b59239db485aae6261e6313779e5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 May 2010 13:52:21 -0700 Subject: [PATCH 114/260] Removed the unreferenced MaxPrimsPerFrame from OpenSim.ini.example --- bin/OpenSim.ini.example | 3 --- 1 file changed, 3 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index f49cd975f1..a5eb78b777 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -87,9 +87,6 @@ ; YOU HAVE BEEN WARNED!!! TrustBinaries = false - ; How many prims to send to each avatar in the scene on each Update() - ; MaxPrimsPerFrame = 200 - ; Combine all contiguous regions into one large region ; Order your regions from South to North, West to East in your regions.ini and then set this to true ; Warning! Don't use this with regions that have existing content!, This will likely break them From bf5c81d77e492cd6df5517ecab32cd64168b01c2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 May 2010 15:59:48 -0700 Subject: [PATCH 115/260] * Initial commit of the slimupdates2 rewrite. This pass maintains the original behavior of avatar update sending and has a simplified set of IClientAPI methods for sending avatar/prim updates --- .../Client/MXP/ClientStack/MXPClientView.cs | 47 +- .../ClientStack/SirikataClientView.cs | 26 +- .../VWoHTTP/ClientStack/VWHClientView.cs | 16 +- OpenSim/Framework/IClientAPI.cs | 258 ++----- OpenSim/Framework/ISceneEntity.cs | 37 + OpenSim/Framework/Lazy.cs | 236 +++++++ .../ClientStack/LindenUDP/LLClientView.cs | 637 +++++++++--------- .../ClientStack/LindenUDP/LLUDPServer.cs | 18 +- .../Examples/SimpleModule/MyNpcCharacter.cs | 14 +- .../Framework/Scenes/SceneObjectPart.cs | 36 +- .../Region/Framework/Scenes/ScenePresence.cs | 29 +- .../Server/IRCClientView.cs | 34 +- .../OptionalModules/World/NPC/NPCAvatar.cs | 14 +- OpenSim/Tests/Common/Mock/TestClient.cs | 14 +- 14 files changed, 712 insertions(+), 704 deletions(-) create mode 100644 OpenSim/Framework/ISceneEntity.cs create mode 100644 OpenSim/Framework/Lazy.cs diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index 1d6d4c1d03..a62d8974a9 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1026,23 +1026,6 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendAvatarData(SendAvatarData data) - { - //ScenePresence presence=((Scene)this.Scene).GetScenePresence(avatarID); - UUID ownerID = data.AvatarID; - MXPSendAvatarData(data.FirstName + " " + data.LastName, ownerID, UUID.Zero, data.AvatarID, data.AvatarLocalID, data.Position, data.Rotation); - } - - public void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - MovementEventMessage me = new MovementEventMessage(); - me.ObjectIndex = data.LocalID; - me.Location = ToOmVector(data.Position); - me.Orientation = ToOmQuaternion(data.Rotation); - - Session.Send(me); - } - public void SendCoarseLocationUpdate(List users, List CoarseLocations) { // Minimap function, not used. @@ -1058,23 +1041,31 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendPrimitiveToClient(SendPrimitiveData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { - MXPSendPrimitive(data.localID, data.ownerID, data.acc, data.rvel, data.primShape, data.pos, data.objectID, data.vel, - data.rotation, (uint)data.flags, data.text, data.color, data.parentID, data.particleSystem, data.clickAction, - data.material, data.textureanim); + //ScenePresence presence=((Scene)this.Scene).GetScenePresence(avatarID); + ScenePresence presence = (ScenePresence)avatar; + UUID ownerID = presence.UUID; + MXPSendAvatarData(presence.Firstname + " " + presence.Lastname, ownerID, UUID.Zero, presence.UUID, presence.LocalId, presence.AbsolutePosition, presence.Rotation); } - public void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { - MovementEventMessage me = new MovementEventMessage(); - me.ObjectIndex = data.LocalID; - me.Location = ToOmVector(data.Position); - me.Orientation = ToOmQuaternion(data.Rotation); - Session.Send(me); + //MovementEventMessage me = new MovementEventMessage(); + //me.ObjectIndex = data.LocalID; + //me.Location = ToOmVector(data.Position); + //me.Orientation = ToOmQuaternion(data.Rotation); + + //MXPSendPrimitive(data.localID, data.ownerID, data.acc, data.rvel, data.primShape, data.pos, data.objectID, data.vel, + // data.rotation, (uint)data.flags, data.text, data.color, data.parentID, data.particleSystem, data.clickAction, + // data.material, data.textureanim); + + //Session.Send(me); + + throw new System.NotImplementedException(); } - public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { } diff --git a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs index 43c64e9584..d1f098892b 100644 --- a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs +++ b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs @@ -584,16 +584,6 @@ namespace OpenSim.Client.Sirikata.ClientStack throw new System.NotImplementedException(); } - public void SendAvatarData(SendAvatarData data) - { - throw new System.NotImplementedException(); - } - - public void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - throw new System.NotImplementedException(); - } - public void SendCoarseLocationUpdate(List users, List CoarseLocations) { throw new System.NotImplementedException(); @@ -609,22 +599,17 @@ namespace OpenSim.Client.Sirikata.ClientStack throw new System.NotImplementedException(); } - public void SendPrimitiveToClient(SendPrimitiveData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { throw new System.NotImplementedException(); } - public void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { throw new System.NotImplementedException(); } - public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) - { - throw new System.NotImplementedException(); - } - - public void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, int version, bool fetchFolders, bool fetchItems) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { throw new System.NotImplementedException(); } @@ -634,6 +619,11 @@ namespace OpenSim.Client.Sirikata.ClientStack throw new System.NotImplementedException(); } + public void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, int version, bool fetchFolders, bool fetchItems) + { + throw new System.NotImplementedException(); + } + public void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item) { throw new System.NotImplementedException(); diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index 864b4f16f3..c0da3263c2 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -590,16 +590,6 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendAvatarData(SendAvatarData data) - { - throw new System.NotImplementedException(); - } - - public void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - throw new System.NotImplementedException(); - } - public void SendCoarseLocationUpdate(List users, List CoarseLocations) { throw new System.NotImplementedException(); @@ -615,17 +605,17 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendPrimitiveToClient(SendPrimitiveData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { throw new System.NotImplementedException(); } - public void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { throw new System.NotImplementedException(); } - public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 01daeb1402..00681cf80d 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -571,207 +571,6 @@ namespace OpenSim.Framework public float dwell; } - public struct SendAvatarData - { - public readonly ulong RegionHandle; - public readonly string FirstName; - public readonly string LastName; - public readonly string GroupTitle; - public readonly UUID AvatarID; - public readonly uint AvatarLocalID; - public readonly Vector3 Position; - public readonly byte[] TextureEntry; - public readonly uint ParentID; - public readonly Quaternion Rotation; - - public SendAvatarData(ulong regionHandle, string firstName, string lastName, string groupTitle, UUID avatarID, - uint avatarLocalID, Vector3 position, byte[] textureEntry, uint parentID, Quaternion rotation) - { - RegionHandle = regionHandle; - FirstName = firstName; - LastName = lastName; - GroupTitle = groupTitle; - AvatarID = avatarID; - AvatarLocalID = avatarLocalID; - Position = position; - TextureEntry = textureEntry; - ParentID = parentID; - Rotation = rotation; - } - } - - public struct SendAvatarTerseData - { - public readonly ulong RegionHandle; - public readonly ushort TimeDilation; - public readonly uint LocalID; - public readonly Vector3 Position; - public readonly Vector3 Velocity; - public readonly Vector3 Acceleration; - public readonly Quaternion Rotation; - public readonly Vector4 CollisionPlane; - public readonly UUID AgentID; - public readonly byte[] TextureEntry; - public readonly double Priority; - - public SendAvatarTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, Vector3 velocity, - Vector3 acceleration, Quaternion rotation, Vector4 collisionPlane, UUID agentid, byte[] textureEntry, double priority) - { - RegionHandle = regionHandle; - TimeDilation = timeDilation; - LocalID = localID; - Position = position; - Velocity = velocity; - Acceleration = acceleration; - Rotation = rotation; - CollisionPlane = collisionPlane; - AgentID = agentid; - TextureEntry = textureEntry; - Priority = priority; - } - } - - public struct SendPrimitiveTerseData - { - public readonly ulong RegionHandle; - public readonly ushort TimeDilation; - public readonly uint LocalID; - public readonly Vector3 Position; - public readonly Quaternion Rotation; - public readonly Vector3 Velocity; - public readonly Vector3 Acceleration; - public readonly Vector3 AngularVelocity; - public readonly UUID AssetID; - public readonly UUID OwnerID; - public readonly int AttachPoint; - public readonly byte[] TextureEntry; - public readonly double Priority; - - public SendPrimitiveTerseData(ulong regionHandle, ushort timeDilation, uint localID, Vector3 position, - Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 rotationalvelocity, - UUID assetID, UUID ownerID, int attachPoint, byte[] textureEntry, double priority) - { - RegionHandle = regionHandle; - TimeDilation = timeDilation; - LocalID = localID; - Position = position; - Rotation = rotation; - Velocity = velocity; - Acceleration = acceleration; - AngularVelocity = rotationalvelocity; - AssetID = assetID; - OwnerID = ownerID; - AttachPoint = attachPoint; - TextureEntry = textureEntry; - Priority = priority; - } - } - - public struct SendPrimitiveData - { - private ulong m_regionHandle; - private ushort m_timeDilation; - private uint m_localID; - private PrimitiveBaseShape m_primShape; - private Vector3 m_pos; - private Vector3 m_vel; - private Vector3 m_acc; - private Quaternion m_rotation; - private Vector3 m_rvel; - private PrimFlags m_flags; - private UUID m_objectID; - private UUID m_ownerID; - private string m_text; - private byte[] m_color; - private uint m_parentID; - private byte[] m_particleSystem; - private byte m_clickAction; - private byte m_material; - private byte[] m_textureanim; - private bool m_attachment; - private uint m_AttachPoint; - private UUID m_AssetId; - private UUID m_SoundId; - private double m_SoundVolume; - private byte m_SoundFlags; - private double m_SoundRadius; - private double m_priority; - - public SendPrimitiveData(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, double priority) : - this(regionHandle, timeDilation, localID, primShape, pos, vel, acc, rotation, rvel, flags, objectID, - ownerID, text, color, parentID, particleSystem, clickAction, material, new byte[0], false, 0, UUID.Zero, - UUID.Zero, 0, 0, 0, priority) { } - - public SendPrimitiveData(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[] textureanim, bool attachment, - uint AttachPoint, UUID AssetId, UUID SoundId, double SoundVolume, byte SoundFlags, - double SoundRadius, double priority) - { - this.m_regionHandle = regionHandle; - this.m_timeDilation = timeDilation; - this.m_localID = localID; - this.m_primShape = primShape; - this.m_pos = pos; - this.m_vel = vel; - this.m_acc = acc; - this.m_rotation = rotation; - this.m_rvel = rvel; - this.m_flags = (PrimFlags)flags; - this.m_objectID = objectID; - this.m_ownerID = ownerID; - this.m_text = text; - this.m_color = color; - this.m_parentID = parentID; - this.m_particleSystem = particleSystem; - this.m_clickAction = clickAction; - this.m_material = material; - this.m_textureanim = textureanim; - this.m_attachment = attachment; - this.m_AttachPoint = AttachPoint; - this.m_AssetId = AssetId; - this.m_SoundId = SoundId; - this.m_SoundVolume = SoundVolume; - this.m_SoundFlags = SoundFlags; - this.m_SoundRadius = SoundRadius; - this.m_priority = priority; - } - - public ulong regionHandle { get { return this.m_regionHandle; } } - public ushort timeDilation { get { return this.m_timeDilation; } } - public uint localID { get { return this.m_localID; } } - public PrimitiveBaseShape primShape { get { return this.m_primShape; } } - public Vector3 pos { get { return this.m_pos; } } - public Vector3 vel { get { return this.m_vel; } } - public Vector3 acc { get { return this.m_acc; } } - public Quaternion rotation { get { return this.m_rotation; } } - public Vector3 rvel { get { return this.m_rvel; } } - public PrimFlags flags { get { return this.m_flags; } } - public UUID objectID { get { return this.m_objectID; } } - public UUID ownerID { get { return this.m_ownerID; } } - public string text { get { return this.m_text; } } - public byte[] color { get { return this.m_color; } } - public uint parentID { get { return this.m_parentID; } } - public byte[] particleSystem { get { return this.m_particleSystem; } } - public byte clickAction { get { return this.m_clickAction; } } - public byte material { get { return this.m_material; } } - public byte[] textureanim { get { return this.m_textureanim; } } - public bool attachment { get { return this.m_attachment; } } - public uint AttachPoint { get { return this.m_AttachPoint; } } - public UUID AssetId { get { return this.m_AssetId; } } - public UUID SoundId { get { return this.m_SoundId; } } - public double SoundVolume { get { return this.m_SoundVolume; } } - public byte SoundFlags { get { return this.m_SoundFlags; } } - public double SoundRadius { get { return this.m_SoundRadius; } } - public double priority { get { return this.m_priority; } } - } - public struct UpdatePriorityData { private double m_priority; private uint m_localID; @@ -785,14 +584,46 @@ namespace OpenSim.Framework public uint localID { get { return this.m_localID; } } } + /// + /// Specifies the fields that have been changed when sending a prim or + /// avatar update + /// [Flags] - public enum StateUpdateTypes + public enum PrimUpdateFlags : uint { None = 0, - AvatarTerse = 1, - PrimitiveTerse = AvatarTerse << 1, - PrimitiveFull = PrimitiveTerse << 1, - All = AvatarTerse | PrimitiveTerse | PrimitiveFull, + AttachmentPoint = 1 << 0, + Material = 1 << 1, + ClickAction = 1 << 2, + Scale = 1 << 3, + ParentID = 1 << 4, + PrimFlags = 1 << 5, + PrimData = 1 << 6, + MediaURL = 1 << 7, + ScratchPad = 1 << 8, + Textures = 1 << 9, + TextureAnim = 1 << 10, + NameValue = 1 << 11, + Position = 1 << 12, + Rotation = 1 << 13, + Velocity = 1 << 14, + Acceleration = 1 << 15, + AngularVelocity = 1 << 16, + CollisionPlane = 1 << 17, + Text = 1 << 18, + Particles = 1 << 19, + ExtraData = 1 << 20, + Sound = 1 << 21, + Joint = 1 << 22, + FullUpdate = UInt32.MaxValue + } + + public static class PrimUpdateFlagsExtensions + { + public static bool HasFlag(this PrimUpdateFlags updateFlags, PrimUpdateFlags flag) + { + return (updateFlags & flag) == flag; + } } public interface IClientAPI @@ -1186,27 +1017,20 @@ namespace OpenSim.Framework void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance); void SendPayPrice(UUID objectID, int[] payPrice); - void SendAvatarData(SendAvatarData data); - - void SendAvatarTerseUpdate(SendAvatarTerseData data); - void SendCoarseLocationUpdate(List users, List CoarseLocations); void AttachObject(uint localID, Quaternion rotation, byte attachPoint, UUID ownerID); void SetChildAgentThrottle(byte[] throttle); - void SendPrimitiveToClient(SendPrimitiveData data); - - void SendPrimTerseUpdate(SendPrimitiveTerseData data); - - void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler); + void SendAvatarDataImmediate(ISceneEntity avatar); + void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags); + void ReprioritizeUpdates(UpdatePriorityHandler handler); + void FlushPrimUpdates(); void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, int version, bool fetchFolders, bool fetchItems); - void FlushPrimUpdates(); - void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item); /// diff --git a/OpenSim/Framework/ISceneEntity.cs b/OpenSim/Framework/ISceneEntity.cs new file mode 100644 index 0000000000..fa3c514667 --- /dev/null +++ b/OpenSim/Framework/ISceneEntity.cs @@ -0,0 +1,37 @@ +/* + * 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 OpenSimulator 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; + +namespace OpenSim.Framework +{ + public interface ISceneEntity + { + UUID UUID { get; } + uint LocalId { get; } + } +} diff --git a/OpenSim/Framework/Lazy.cs b/OpenSim/Framework/Lazy.cs new file mode 100644 index 0000000000..8a417ac64b --- /dev/null +++ b/OpenSim/Framework/Lazy.cs @@ -0,0 +1,236 @@ +// +// Lazy.cs +// +// Authors: +// Zoltan Varga (vargaz@gmail.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright (C) 2009 Novell +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Threading; +using System.Diagnostics; + +namespace OpenSim.Framework +{ + public enum LazyThreadSafetyMode + { + None, + PublicationOnly, + ExecutionAndPublication + } + + [SerializableAttribute] + [ComVisibleAttribute(false)] + [HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)] + public class Lazy + { + T value; + bool inited; + LazyThreadSafetyMode mode; + Func factory; + object monitor; + Exception exception; + + public Lazy() + : this(LazyThreadSafetyMode.ExecutionAndPublication) + { + } + + public Lazy(Func valueFactory) + : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication) + { + } + + public Lazy(bool isThreadSafe) + : this(() => Activator.CreateInstance(), isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) + { + } + + public Lazy(Func valueFactory, bool isThreadSafe) + : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) + { + } + + public Lazy(LazyThreadSafetyMode mode) + : this(() => Activator.CreateInstance(), mode) + { + } + + public Lazy(Func valueFactory, LazyThreadSafetyMode mode) + { + if (valueFactory == null) + throw new ArgumentNullException("valueFactory"); + this.factory = valueFactory; + if (mode != LazyThreadSafetyMode.None) + monitor = new object(); + this.mode = mode; + } + + // Don't trigger expensive initialization + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public T Value + { + get + { + if (inited) + return value; + if (exception != null) + throw exception; + + return InitValue(); + } + } + + T InitValue() + { + switch (mode) + { + case LazyThreadSafetyMode.None: + { + var init_factory = factory; + if (init_factory == null) + throw exception = new InvalidOperationException("The initialization function tries to access Value on this instance"); + try + { + factory = null; + T v = init_factory(); + value = v; + Thread.MemoryBarrier(); + inited = true; + } + catch (Exception ex) + { + exception = ex; + throw; + } + break; + } + case LazyThreadSafetyMode.PublicationOnly: + { + var init_factory = factory; + T v; + + //exceptions are ignored + if (init_factory != null) + v = init_factory(); + else + v = default(T); + + lock (monitor) + { + if (inited) + return value; + value = v; + Thread.MemoryBarrier(); + inited = true; + factory = null; + } + break; + } + case LazyThreadSafetyMode.ExecutionAndPublication: + { + lock (monitor) + { + if (inited) + return value; + + if (factory == null) + throw exception = new InvalidOperationException("The initialization function tries to access Value on this instance"); + + var init_factory = factory; + try + { + factory = null; + T v = init_factory(); + value = v; + Thread.MemoryBarrier(); + inited = true; + } + catch (Exception ex) + { + exception = ex; + throw; + } + } + break; + } + default: + throw new InvalidOperationException("Invalid LazyThreadSafetyMode " + mode); + } + + if (monitor == null) + { + value = factory(); + inited = true; + } + else + { + lock (monitor) + { + if (inited) + return value; + + if (factory == null) + throw new InvalidOperationException("The initialization function tries to access Value on this instance"); + + var init_factory = factory; + try + { + factory = null; + T v = init_factory(); + value = v; + Thread.MemoryBarrier(); + inited = true; + } + catch + { + factory = init_factory; + throw; + } + } + } + + return value; + } + + public bool IsValueCreated + { + get + { + return inited; + } + } + + public override string ToString() + { + if (inited) + return value.ToString(); + else + return "Value is not created"; + } + } +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1f3582cfba..6154da4728 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -50,43 +50,17 @@ using Nini.Config; namespace OpenSim.Region.ClientStack.LindenUDP { - #region Enums - - /// - /// Specifies the fields that have been changed when sending a prim or - /// avatar update - /// - [Flags] - public enum PrimUpdateFlags : uint + public class EntityUpdate { - None = 0, - AttachmentPoint = 1 << 0, - Material = 1 << 1, - ClickAction = 1 << 2, - Scale = 1 << 3, - ParentID = 1 << 4, - PrimFlags = 1 << 5, - PrimData = 1 << 6, - MediaURL = 1 << 7, - ScratchPad = 1 << 8, - Textures = 1 << 9, - TextureAnim = 1 << 10, - NameValue = 1 << 11, - Position = 1 << 12, - Rotation = 1 << 13, - Velocity = 1 << 14, - Acceleration = 1 << 15, - AngularVelocity = 1 << 16, - CollisionPlane = 1 << 17, - Text = 1 << 18, - Particles = 1 << 19, - ExtraData = 1 << 20, - Sound = 1 << 21, - Joint = 1 << 22, - FullUpdate = UInt32.MaxValue - } + public ISceneEntity Entity; + public PrimUpdateFlags Flags; - #endregion Enums + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags) + { + Entity = entity; + Flags = flags; + } + } public delegate bool PacketMethod(IClientAPI simClient, Packet packet); @@ -350,9 +324,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private readonly IGroupsModule m_GroupsModule; private int m_cachedTextureSerial; - protected PriorityQueue m_avatarTerseUpdates; - private PriorityQueue m_primTerseUpdates; - private PriorityQueue m_primFullUpdates; + private PriorityQueue m_entityUpdates; /// /// List used in construction of data blocks for an object update packet. This is to stop us having to @@ -463,9 +435,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; - m_avatarTerseUpdates = new PriorityQueue(); - m_primTerseUpdates = new PriorityQueue(); - m_primFullUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); @@ -1519,7 +1489,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP kill.Header.Reliable = true; kill.Header.Zerocoded = true; - lock (m_primFullUpdates.SyncRoot) + lock (m_entityUpdates.SyncRoot) { m_killRecord.Add(localID); OutPacket(kill, ThrottleOutPacketType.State); @@ -3419,71 +3389,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Send an ObjectUpdate packet with information about an avatar /// - public void SendAvatarData(SendAvatarData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { + ScenePresence presence = avatar as ScenePresence; + if (presence == null) + return; + ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); objupdate.Header.Zerocoded = true; - objupdate.RegionData.RegionHandle = data.RegionHandle; + objupdate.RegionData.RegionHandle = presence.RegionHandle; objupdate.RegionData.TimeDilation = ushort.MaxValue; objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - objupdate.ObjectData[0] = CreateAvatarUpdateBlock(data); + objupdate.ObjectData[0] = CreateAvatarUpdateBlock(presence); OutPacket(objupdate, ThrottleOutPacketType.Task); } - /// - /// Send a terse positional/rotation/velocity update about an avatar - /// to the client. This avatar can be that of the client itself. - /// - public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - if (data.Priority == double.NaN) - { - m_log.Error("[LLClientView] SendAvatarTerseUpdate received a NaN priority, dropping update"); - return; - } - - Quaternion rotation = data.Rotation; - if (rotation.W == 0.0f && rotation.X == 0.0f && rotation.Y == 0.0f && rotation.Z == 0.0f) - rotation = Quaternion.Identity; - - ImprovedTerseObjectUpdatePacket.ObjectDataBlock terseBlock = CreateImprovedTerseBlock(data); - - lock (m_avatarTerseUpdates.SyncRoot) - m_avatarTerseUpdates.Enqueue(data.Priority, terseBlock, data.LocalID); - - // If we received an update about our own avatar, process the avatar update priority queue immediately - if (data.AgentID == m_agentId) - ProcessAvatarTerseUpdates(); - } - - protected void ProcessAvatarTerseUpdates() - { - ImprovedTerseObjectUpdatePacket terse = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - terse.Header.Reliable = false; - terse.Header.Zerocoded = true; - - //terse.RegionData = new ImprovedTerseObjectUpdatePacket.RegionDataBlock(); - terse.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - terse.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - - lock (m_avatarTerseUpdates.SyncRoot) - { - int count = Math.Min(m_avatarTerseUpdates.Count, m_udpServer.AvatarTerseUpdatesPerPacket); - if (count == 0) - return; - - terse.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; - for (int i = 0; i < count; i++) - terse.ObjectData[i] = m_avatarTerseUpdates.Dequeue(); - } - - // HACK: Using the task category until the tiered reprioritization code is in - OutPacket(terse, ThrottleOutPacketType.Task); - } - public void SendCoarseLocationUpdate(List users, List CoarseLocations) { if (!IsActive) return; // We don't need to update inactive clients. @@ -3528,172 +3451,188 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Primitive Packet/Data Sending Methods - public void SendPrimitiveToClient(SendPrimitiveData data) + /// + /// Generate one of the object update packets based on PrimUpdateFlags + /// and broadcast the packet to clients + /// + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { -// string text = data.text; -// if (text.IndexOf("\n") >= 0) -// text = text.Remove(text.IndexOf("\n")); -// m_log.DebugFormat( -// "[CLIENT]: Placing request to send full info about prim {0} text {1} to client {2}", -// data.localID, text, Name); - - if (data.priority == double.NaN) - { - m_log.Error("[LLClientView] SendPrimitiveToClient received a NaN priority, dropping update"); - return; - } + double priority; - Quaternion rotation = data.rotation; - if (rotation.W == 0.0f && rotation.X == 0.0f && rotation.Y == 0.0f && rotation.Z == 0.0f) - rotation = Quaternion.Identity; + if (entity is SceneObjectPart) + priority = ((SceneObjectPart)entity).ParentGroup.GetUpdatePriority(this); + else if (entity is ScenePresence) + priority = ((ScenePresence)entity).GetUpdatePriority(this); + else + priority = 0.0d; - if (data.AttachPoint > 30 && data.ownerID != AgentId) // Someone else's HUD - return; - if (data.primShape.State != 0 && data.parentID == 0 && data.primShape.PCode == 9) - return; - - ObjectUpdatePacket.ObjectDataBlock objectData = CreatePrimUpdateBlock(data); - - lock (m_primFullUpdates.SyncRoot) - m_primFullUpdates.Enqueue(data.priority, objectData, data.localID); + lock (m_entityUpdates.SyncRoot) + m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags), entity.LocalId); } - void ProcessPrimFullUpdates() + private void ProcessEntityUpdates(int maxUpdates) { - ObjectUpdatePacket outPacket = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); - outPacket.Header.Zerocoded = true; + Lazy> objectUpdateBlocks = new Lazy>(); + Lazy> compressedUpdateBlocks = new Lazy>(); + Lazy> terseUpdateBlocks = new Lazy>(); - outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); + int updatesThisCall = 0; - lock (m_primFullUpdates.SyncRoot) + lock (m_entityUpdates.SyncRoot) { - int count = Math.Min(m_primFullUpdates.Count, m_udpServer.PrimFullUpdatesPerPacket); - if (count == 0) - return; - - m_fullUpdateDataBlocksBuilder.Clear(); - - for (int i = 0; i < count; i++) + EntityUpdate update; + while (m_entityUpdates.TryDequeue(out update)) { - ObjectUpdatePacket.ObjectDataBlock block = m_primFullUpdates.Dequeue(); + #region UpdateFlags to packet type conversion - if (!m_killRecord.Contains(block.ID)) + PrimUpdateFlags updateFlags = update.Flags; + + bool canUseCompressed = true; + bool canUseImproved = true; + + // Compressed object updates only make sense for LL primitives + if (!(update.Entity is SceneObjectPart)) + canUseCompressed = false; + + if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate)) { - m_fullUpdateDataBlocksBuilder.Add(block); - -// string text = Util.FieldToString(outPacket.ObjectData[i].Text); -// if (text.IndexOf("\n") >= 0) -// text = text.Remove(text.IndexOf("\n")); -// m_log.DebugFormat( -// "[CLIENT]: Sending full info about prim {0} text {1} to client {2}", -// outPacket.ObjectData[i].ID, text, Name); + canUseCompressed = false; + canUseImproved = false; } -// else -// { -// m_log.WarnFormat( -// "[CLIENT]: Preventing full update for {0} after kill to {1}", block.ID, Name); -// } + else + { + if (updateFlags.HasFlag(PrimUpdateFlags.Velocity) || + updateFlags.HasFlag(PrimUpdateFlags.Acceleration) || + updateFlags.HasFlag(PrimUpdateFlags.CollisionPlane) || + updateFlags.HasFlag(PrimUpdateFlags.Joint)) + { + canUseCompressed = false; + } + + if (updateFlags.HasFlag(PrimUpdateFlags.PrimFlags) || + updateFlags.HasFlag(PrimUpdateFlags.ParentID) || + updateFlags.HasFlag(PrimUpdateFlags.Scale) || + updateFlags.HasFlag(PrimUpdateFlags.PrimData) || + updateFlags.HasFlag(PrimUpdateFlags.Text) || + updateFlags.HasFlag(PrimUpdateFlags.NameValue) || + updateFlags.HasFlag(PrimUpdateFlags.ExtraData) || + updateFlags.HasFlag(PrimUpdateFlags.TextureAnim) || + updateFlags.HasFlag(PrimUpdateFlags.Sound) || + updateFlags.HasFlag(PrimUpdateFlags.Particles) || + updateFlags.HasFlag(PrimUpdateFlags.Material) || + updateFlags.HasFlag(PrimUpdateFlags.ClickAction) || + updateFlags.HasFlag(PrimUpdateFlags.MediaURL) || + updateFlags.HasFlag(PrimUpdateFlags.Joint)) + { + canUseImproved = false; + } + } + + #endregion UpdateFlags to packet type conversion + + #region Block Construction + + // TODO: Remove this once we can build compressed updates + canUseCompressed = false; + + if (!canUseImproved && !canUseCompressed) + { + if (update.Entity is ScenePresence) + objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); + else + objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); + } + else if (!canUseImproved) + { + compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); + } + else + { + terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); + } + + #endregion Block Construction + + ++updatesThisCall; + if (maxUpdates > 0 && updatesThisCall >= maxUpdates) + break; } - - outPacket.ObjectData = m_fullUpdateDataBlocksBuilder.ToArray(); - - OutPacket(outPacket, ThrottleOutPacketType.State); - } - } - - public void SendPrimTerseUpdate(SendPrimitiveTerseData data) - { - if (data.Priority == double.NaN) - { - m_log.Error("[LLClientView] SendPrimTerseUpdate received a NaN priority, dropping update"); - return; } - Quaternion rotation = data.Rotation; - if (rotation.W == 0.0f && rotation.X == 0.0f && rotation.Y == 0.0f && rotation.Z == 0.0f) - rotation = Quaternion.Identity; + #region Packet Sending - if (data.AttachPoint > 30 && data.OwnerID != AgentId) // Someone else's HUD - return; + const float TIME_DILATION = 1.0f; + ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); - ImprovedTerseObjectUpdatePacket.ObjectDataBlock objectData = CreateImprovedTerseBlock(data); - - lock (m_primTerseUpdates.SyncRoot) - m_primTerseUpdates.Enqueue(data.Priority, objectData, data.LocalID); - } - - void ProcessPrimTerseUpdates() - { - ImprovedTerseObjectUpdatePacket outPacket = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - outPacket.Header.Reliable = false; - outPacket.Header.Zerocoded = true; - - outPacket.RegionData.RegionHandle = Scene.RegionInfo.RegionHandle; - outPacket.RegionData.TimeDilation = (ushort)(Scene.TimeDilation * ushort.MaxValue); - - lock (m_primTerseUpdates.SyncRoot) + if (objectUpdateBlocks.IsValueCreated) { - int count = Math.Min(m_primTerseUpdates.Count, m_udpServer.PrimTerseUpdatesPerPacket); - if (count == 0) - return; + List blocks = objectUpdateBlocks.Value; - outPacket.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[count]; - for (int i = 0; i < count; i++) - outPacket.ObjectData[i] = m_primTerseUpdates.Dequeue(); + ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; + packet.RegionData.TimeDilation = timeDilation; + packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count]; + + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + OutPacket(packet, ThrottleOutPacketType.Task, true); } - OutPacket(outPacket, ThrottleOutPacketType.State); + if (compressedUpdateBlocks.IsValueCreated) + { + List blocks = compressedUpdateBlocks.Value; + + ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; + packet.RegionData.TimeDilation = timeDilation; + packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count]; + + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + OutPacket(packet, ThrottleOutPacketType.Task, true); + } + + if (terseUpdateBlocks.IsValueCreated) + { + List blocks = terseUpdateBlocks.Value; + + ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; + packet.RegionData.TimeDilation = timeDilation; + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; + + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + OutPacket(packet, ThrottleOutPacketType.Task, true); + } + + #endregion Packet Sending } - public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { - PriorityQueue.UpdatePriorityHandler terse_update_priority_handler = - delegate(ref double priority, uint local_id) - { - priority = handler(new UpdatePriorityData(priority, local_id)); - return priority != double.NaN; - }; - PriorityQueue.UpdatePriorityHandler update_priority_handler = + //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName); + + PriorityQueue.UpdatePriorityHandler update_priority_handler = delegate(ref double priority, uint local_id) { priority = handler(new UpdatePriorityData(priority, local_id)); return priority != double.NaN; }; - if ((type & StateUpdateTypes.AvatarTerse) != 0) - { - lock (m_avatarTerseUpdates.SyncRoot) - m_avatarTerseUpdates.Reprioritize(terse_update_priority_handler); - } - - if ((type & StateUpdateTypes.PrimitiveFull) != 0) - { - lock (m_primFullUpdates.SyncRoot) - m_primFullUpdates.Reprioritize(update_priority_handler); - } - - if ((type & StateUpdateTypes.PrimitiveTerse) != 0) - { - lock (m_primTerseUpdates.SyncRoot) - m_primTerseUpdates.Reprioritize(terse_update_priority_handler); - } + lock (m_entityUpdates.SyncRoot) + m_entityUpdates.Reprioritize(update_priority_handler); } public void FlushPrimUpdates() { - while (m_primFullUpdates.Count > 0) - { - ProcessPrimFullUpdates(); - } - while (m_primTerseUpdates.Count > 0) - { - ProcessPrimTerseUpdates(); - } - while (m_avatarTerseUpdates.Count > 0) - { - ProcessAvatarTerseUpdates(); - } + m_log.Debug("[CLIENT]: Flushing prim updates to " + m_firstName + " " + m_lastName); + + while (m_entityUpdates.Count > 0) + ProcessEntityUpdates(-1); } #endregion Primitive Packet/Data Sending Methods @@ -3726,26 +3665,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { - lock (m_avatarTerseUpdates.SyncRoot) - { - if (m_avatarTerseUpdates.Count > 0) - ProcessAvatarTerseUpdates(); - } - } - - if ((categories & ThrottleOutPacketTypeFlags.State) != 0) - { - lock (m_primFullUpdates.SyncRoot) - { - if (m_primFullUpdates.Count > 0) - ProcessPrimFullUpdates(); - } - - lock (m_primTerseUpdates.SyncRoot) - { - if (m_primTerseUpdates.Count > 0) - ProcessPrimTerseUpdates(); - } + if (m_entityUpdates.Count > 0) + ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4403,22 +4324,51 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Helper Methods - protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(SendAvatarTerseData data) + protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(ISceneEntity entity, bool sendTexture) { - return CreateImprovedTerseBlock(true, data.LocalID, 0, data.CollisionPlane, data.Position, data.Velocity, - data.Acceleration, data.Rotation, Vector3.Zero, data.TextureEntry); - } + #region ScenePresence/SOP Handling - protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(SendPrimitiveTerseData data) - { - return CreateImprovedTerseBlock(false, data.LocalID, data.AttachPoint, Vector4.Zero, data.Position, data.Velocity, - data.Acceleration, data.Rotation, data.AngularVelocity, data.TextureEntry); - } + bool avatar = (entity is ScenePresence); + uint localID = entity.LocalId; + uint attachPoint; + Vector4 collisionPlane; + Vector3 position, velocity, acceleration, angularVelocity; + Quaternion rotation; + byte[] textureEntry; + + if (entity is ScenePresence) + { + ScenePresence presence = (ScenePresence)entity; + + attachPoint = 0; + collisionPlane = presence.CollisionPlane; + position = presence.OffsetPosition; + velocity = presence.Velocity; + acceleration = Vector3.Zero; + angularVelocity = Vector3.Zero; + rotation = presence.Rotation; + + if (sendTexture) + textureEntry = presence.Appearance.Texture.GetBytes(); + else + textureEntry = null; + } + else + { + SceneObjectPart part = (SceneObjectPart)entity; + + attachPoint = part.AttachmentPoint; + collisionPlane = Vector4.Zero; + position = part.RelativePosition; + velocity = part.Velocity; + acceleration = part.Acceleration; + angularVelocity = part.AngularVelocity; + rotation = part.RotationOffset; + textureEntry = part.Shape.TextureEntry; + } + + #endregion ScenePresence/SOP Handling - protected ImprovedTerseObjectUpdatePacket.ObjectDataBlock CreateImprovedTerseBlock(bool avatar, uint localID, int attachPoint, - Vector4 collisionPlane, Vector3 position, Vector3 velocity, Vector3 acceleration, Quaternion rotation, - Vector3 angularVelocity, byte[] textureEntry) - { int pos = 0; byte[] data = new byte[(avatar ? 60 : 44)]; @@ -4490,12 +4440,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } - protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(SendAvatarData data) + protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) { byte[] objectData = new byte[76]; - Vector4.UnitW.ToBytes(objectData, 0); // TODO: Collision plane support - data.Position.ToBytes(objectData, 16); + data.CollisionPlane.ToBytes(objectData, 0); + data.OffsetPosition.ToBytes(objectData, 16); //data.Velocity.ToBytes(objectData, 28); //data.Acceleration.ToBytes(objectData, 40); data.Rotation.ToBytes(objectData, 52); @@ -4505,12 +4455,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP update.Data = Utils.EmptyBytes; update.ExtraParams = new byte[1]; - update.FullID = data.AvatarID; - update.ID = data.AvatarLocalID; + update.FullID = data.UUID; + update.ID = data.LocalId; update.Material = (byte)Material.Flesh; update.MediaURL = Utils.EmptyBytes; - update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.FirstName + "\nLastName STRING RW SV " + - data.LastName + "\nTitle STRING RW SV " + data.GroupTitle); + update.NameValue = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + + data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); update.ObjectData = objectData; update.ParentID = data.ParentID; update.PathCurve = 16; @@ -4519,102 +4469,116 @@ namespace OpenSim.Region.ClientStack.LindenUDP update.PCode = (byte)PCode.Avatar; update.ProfileCurve = 1; update.PSBlock = Utils.EmptyBytes; - update.Scale = new Vector3(0.45f,0.6f,1.9f); + update.Scale = new Vector3(0.45f, 0.6f, 1.9f); update.Text = Utils.EmptyBytes; update.TextColor = new byte[4]; update.TextureAnim = Utils.EmptyBytes; - update.TextureEntry = data.TextureEntry ?? Utils.EmptyBytes; - update.UpdateFlags = (uint)(PrimFlags.Physics | PrimFlags.ObjectModify | PrimFlags.ObjectCopy | PrimFlags.ObjectAnyOwner | PrimFlags.ObjectYouOwner | PrimFlags.ObjectMove | PrimFlags.InventoryEmpty | PrimFlags.ObjectTransfer | PrimFlags.ObjectOwnerModify);//61 + (9 << 8) + (130 << 16) + (16 << 24); // TODO: Replace these numbers with PrimFlags + update.TextureEntry = (data.Appearance.Texture != null) ? data.Appearance.Texture.GetBytes() : Utils.EmptyBytes; + update.UpdateFlags = (uint)( + PrimFlags.Physics | PrimFlags.ObjectModify | PrimFlags.ObjectCopy | PrimFlags.ObjectAnyOwner | + PrimFlags.ObjectYouOwner | PrimFlags.ObjectMove | PrimFlags.InventoryEmpty | PrimFlags.ObjectTransfer | + PrimFlags.ObjectOwnerModify); return update; } - protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SendPrimitiveData data) + protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart data, UUID recipientID) { byte[] objectData = new byte[60]; - data.pos.ToBytes(objectData, 0); - data.vel.ToBytes(objectData, 12); - data.acc.ToBytes(objectData, 24); - data.rotation.ToBytes(objectData, 36); - data.rvel.ToBytes(objectData, 48); + data.RelativePosition.ToBytes(objectData, 0); + data.Velocity.ToBytes(objectData, 12); + data.Acceleration.ToBytes(objectData, 24); + data.RotationOffset.ToBytes(objectData, 36); + data.AngularVelocity.ToBytes(objectData, 48); ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); - update.ClickAction = (byte)data.clickAction; + update.ClickAction = (byte)data.ClickAction; update.CRC = 0; - update.ExtraParams = data.primShape.ExtraParams ?? Utils.EmptyBytes; - update.FullID = data.objectID; - update.ID = data.localID; + update.ExtraParams = data.Shape.ExtraParams ?? Utils.EmptyBytes; + update.FullID = data.UUID; + update.ID = data.LocalId; //update.JointAxisOrAnchor = Vector3.Zero; // These are deprecated //update.JointPivot = Vector3.Zero; //update.JointType = 0; - update.Material = data.material; + update.Material = data.Material; update.MediaURL = Utils.EmptyBytes; // FIXME: Support this in OpenSim - if (data.attachment) + if (data.IsAttachment) { - update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.AssetId); - update.State = (byte)((data.AttachPoint % 16) * 16 + (data.AttachPoint / 16)); + update.NameValue = Util.StringToBytes256("AttachItemID STRING RW SV " + data.FromItemID); + update.State = (byte)((data.AttachmentPoint % 16) * 16 + (data.AttachmentPoint / 16)); } else { update.NameValue = Utils.EmptyBytes; - update.State = data.primShape.State; + update.State = data.Shape.State; } - update.ObjectData = objectData; - update.ParentID = data.parentID; - update.PathBegin = data.primShape.PathBegin; - update.PathCurve = data.primShape.PathCurve; - update.PathEnd = data.primShape.PathEnd; - update.PathRadiusOffset = data.primShape.PathRadiusOffset; - update.PathRevolutions = data.primShape.PathRevolutions; - update.PathScaleX = data.primShape.PathScaleX; - update.PathScaleY = data.primShape.PathScaleY; - update.PathShearX = data.primShape.PathShearX; - update.PathShearY = data.primShape.PathShearY; - update.PathSkew = data.primShape.PathSkew; - update.PathTaperX = data.primShape.PathTaperX; - update.PathTaperY = data.primShape.PathTaperY; - update.PathTwist = data.primShape.PathTwist; - update.PathTwistBegin = data.primShape.PathTwistBegin; - update.PCode = data.primShape.PCode; - update.ProfileBegin = data.primShape.ProfileBegin; - update.ProfileCurve = data.primShape.ProfileCurve; - update.ProfileEnd = data.primShape.ProfileEnd; - update.ProfileHollow = data.primShape.ProfileHollow; - update.PSBlock = data.particleSystem ?? Utils.EmptyBytes; - update.TextColor = data.color ?? Color4.Black.GetBytes(true); - update.TextureAnim = data.textureanim ?? Utils.EmptyBytes; - update.TextureEntry = data.primShape.TextureEntry ?? Utils.EmptyBytes; - update.Scale = data.primShape.Scale; - update.Text = Util.StringToBytes256(data.text); - update.UpdateFlags = (uint)data.flags; - if (data.SoundId != UUID.Zero) + update.ObjectData = objectData; + update.ParentID = data.ParentID; + update.PathBegin = data.Shape.PathBegin; + update.PathCurve = data.Shape.PathCurve; + update.PathEnd = data.Shape.PathEnd; + update.PathRadiusOffset = data.Shape.PathRadiusOffset; + update.PathRevolutions = data.Shape.PathRevolutions; + update.PathScaleX = data.Shape.PathScaleX; + update.PathScaleY = data.Shape.PathScaleY; + update.PathShearX = data.Shape.PathShearX; + update.PathShearY = data.Shape.PathShearY; + update.PathSkew = data.Shape.PathSkew; + update.PathTaperX = data.Shape.PathTaperX; + update.PathTaperY = data.Shape.PathTaperY; + update.PathTwist = data.Shape.PathTwist; + update.PathTwistBegin = data.Shape.PathTwistBegin; + update.PCode = data.Shape.PCode; + update.ProfileBegin = data.Shape.ProfileBegin; + update.ProfileCurve = data.Shape.ProfileCurve; + update.ProfileEnd = data.Shape.ProfileEnd; + update.ProfileHollow = data.Shape.ProfileHollow; + update.PSBlock = data.ParticleSystem ?? Utils.EmptyBytes; + update.TextColor = data.GetTextColor().GetBytes(false); + update.TextureAnim = data.TextureAnimation ?? Utils.EmptyBytes; + update.TextureEntry = data.Shape.TextureEntry ?? Utils.EmptyBytes; + update.Scale = data.Shape.Scale; + update.Text = Util.StringToBytes256(data.Text); + + #region PrimFlags + + PrimFlags flags = (PrimFlags)m_scene.Permissions.GenerateClientFlags(recipientID, data.UUID); + + // Don't send the CreateSelected flag to everyone + flags &= ~PrimFlags.CreateSelected; + + if (recipientID == data.OwnerID) { - update.Sound = data.SoundId; - update.OwnerID = data.ownerID; - update.Gain = (float)data.SoundVolume; + if ((data.Flags & PrimFlags.CreateSelected) != 0) + { + // Only send this flag once, then unset it + flags |= PrimFlags.CreateSelected; + data.Flags &= ~PrimFlags.CreateSelected; + } + } + + update.UpdateFlags = (uint)flags; + + #endregion PrimFlags + + if (data.Sound != UUID.Zero) + { + update.Sound = data.Sound; + update.OwnerID = data.OwnerID; + update.Gain = (float)data.SoundGain; update.Radius = (float)data.SoundRadius; update.Flags = data.SoundFlags; } - switch ((PCode)data.primShape.PCode) + switch ((PCode)data.Shape.PCode) { case PCode.Grass: case PCode.Tree: case PCode.NewTree: - update.Data = new byte[] { data.primShape.State }; + update.Data = new byte[] { data.Shape.State }; break; default: - // TODO: Support ScratchPad - //if (prim.ScratchPad != null) - //{ - // update.Data = new byte[prim.ScratchPad.Length]; - // Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); - //} - //else - //{ - // update.Data = Utils.EmptyBytes; - //} update.Data = Utils.EmptyBytes; break; } @@ -4622,6 +4586,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP return update; } + protected ObjectUpdateCompressedPacket.ObjectDataBlock CreateCompressedUpdateBlock(SceneObjectPart part, PrimUpdateFlags updateFlags) + { + // TODO: Implement this + return null; + } + public void SendNameReply(UUID profileId, string firstname, string lastname) { UUIDNameReplyPacket packet = (UUIDNameReplyPacket)PacketPool.Instance.GetPacket(PacketType.UUIDNameReply); @@ -11644,7 +11614,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); } - internal TValue Dequeue() + internal bool TryDequeue(out TValue value) { for (int i = 0; i < m_heaps.Length; ++i) { @@ -11652,10 +11622,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP { MinHeapItem item = m_heaps[i].RemoveMin(); m_lookupTable.Remove(item.LocalID); - return item.Value; + value = item.Value; + return true; } } - throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); + + value = default(TValue); + return false; } internal void Reprioritize(UpdatePriorityHandler handler) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 41e41e47a7..1b81105248 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -99,15 +99,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// The measured resolution of Environment.TickCount public readonly float TickCountResolution; - /// Number of terse prim updates to put on the queue each time the + /// Number of prim updates to put on the queue each time the /// OnQueueEmpty event is triggered for updates - public readonly int PrimTerseUpdatesPerPacket; - /// Number of terse avatar updates to put on the queue each time the - /// OnQueueEmpty event is triggered for updates - public readonly int AvatarTerseUpdatesPerPacket; - /// Number of full prim updates to put on the queue each time the - /// OnQueueEmpty event is triggered for updates - public readonly int PrimFullUpdatesPerPacket; + public readonly int PrimUpdatesPerCallback; /// Number of texture packets to put on the queue each time the /// OnQueueEmpty event is triggered for textures public readonly int TextureSendLimit; @@ -191,9 +185,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); - PrimTerseUpdatesPerPacket = config.GetInt("PrimTerseUpdatesPerPacket", 25); - AvatarTerseUpdatesPerPacket = config.GetInt("AvatarTerseUpdatesPerPacket", 10); - PrimFullUpdatesPerPacket = config.GetInt("PrimFullUpdatesPerPacket", 100); + PrimUpdatesPerCallback = config.GetInt("PrimUpdatesPerCallback", 100); TextureSendLimit = config.GetInt("TextureSendLimit", 20); m_defaultRTO = config.GetInt("DefaultRTO", 0); @@ -201,9 +193,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - PrimTerseUpdatesPerPacket = 25; - AvatarTerseUpdatesPerPacket = 10; - PrimFullUpdatesPerPacket = 100; + PrimUpdatesPerCallback = 100; TextureSendLimit = 20; } diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 09611af887..aac47d19a3 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -528,14 +528,6 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public virtual void SendAvatarData(SendAvatarData data) - { - } - - public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - } - public virtual void SendCoarseLocationUpdate(List users, List CoarseLocations) { } @@ -548,15 +540,15 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public virtual void SendPrimitiveToClient(SendPrimitiveData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { } - public virtual void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { } - public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 46eadee1c3..71c8018717 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -104,7 +104,7 @@ namespace OpenSim.Region.Framework.Scenes #endregion Enumerations - public class SceneObjectPart : IScriptHost + public class SceneObjectPart : IScriptHost, ISceneEntity { /// /// Denote all sides of the prim @@ -712,6 +712,24 @@ namespace OpenSim.Region.Framework.Scenes } } + public Vector3 RelativePosition + { + get + { + if (IsRoot) + { + if (IsAttachment) + return AttachedPos; + else + return AbsolutePosition; + } + else + { + return OffsetPosition; + } + } + } + public Quaternion RotationOffset { get @@ -973,7 +991,6 @@ namespace OpenSim.Region.Framework.Scenes get { return AggregateScriptEvents; } } - public Quaternion SitTargetOrientation { get { return m_sitTargetOrientation; } @@ -2925,11 +2942,7 @@ namespace OpenSim.Region.Framework.Scenes //if (LocalId != ParentGroup.RootPart.LocalId) //isattachment = ParentGroup.RootPart.IsAttachment; - byte[] color = new byte[] {m_color.R, m_color.G, m_color.B, m_color.A}; - remoteClient.SendPrimitiveToClient(new SendPrimitiveData(m_regionHandle, m_parentGroup.GetTimeDilation(), LocalId, m_shape, - lPos, Velocity, Acceleration, RotationOffset, AngularVelocity, clientFlags, m_uuid, _ownerID, - m_text, color, _parentID, m_particleSystem, m_clickAction, (byte)m_material, m_TextureAnimation, IsAttachment, - AttachmentPoint,FromItemID, Sound, SoundGain, SoundFlags, SoundRadius, ParentGroup.GetUpdatePriority(remoteClient))); + remoteClient.SendPrimUpdate(this, PrimUpdateFlags.FullUpdate); } /// @@ -4640,11 +4653,7 @@ namespace OpenSim.Region.Framework.Scenes // Causes this thread to dig into the Client Thread Data. // Remember your locking here! - remoteClient.SendPrimTerseUpdate(new SendPrimitiveTerseData(m_regionHandle, - m_parentGroup.GetTimeDilation(), LocalId, lPos, - RotationOffset, Velocity, Acceleration, - AngularVelocity, FromItemID, - OwnerID, (int)AttachmentPoint, null, ParentGroup.GetUpdatePriority(remoteClient))); + remoteClient.SendPrimUpdate(this, PrimUpdateFlags.Position | PrimUpdateFlags.Rotation | PrimUpdateFlags.Velocity | PrimUpdateFlags.Acceleration | PrimUpdateFlags.AngularVelocity); } public void AddScriptLPS(int count) @@ -4694,7 +4703,8 @@ namespace OpenSim.Region.Framework.Scenes public Color4 GetTextColor() { - return new Color4((byte)Color.R, (byte)Color.G, (byte)Color.B, (byte)(0xFF - Color.A)); + Color color = Color; + return new Color4(color.R, color.G, color.B, (byte)(0xFF - color.A)); } } } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 30eafd7c65..ee0eb07265 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -67,7 +67,7 @@ namespace OpenSim.Region.Framework.Scenes public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence); - public class ScenePresence : EntityBase + public class ScenePresence : EntityBase, ISceneEntity { // ~ScenePresence() // { @@ -478,6 +478,12 @@ namespace OpenSim.Region.Framework.Scenes } } + public Vector3 OffsetPosition + { + get { return m_pos; } + set { m_pos = value; } + } + /// /// Current velocity of the avatar. /// @@ -1036,8 +1042,9 @@ namespace OpenSim.Region.Framework.Scenes AbsolutePosition = AbsolutePosition + new Vector3(0f, 0f, (1.56f / 6f)); } - ControllingClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_rootRegionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, - AbsolutePosition, Velocity, Vector3.Zero, m_bodyRot, new Vector4(0,0,1,AbsolutePosition.Z - 0.5f), m_uuid, null, GetUpdatePriority(ControllingClient))); + ControllingClient.SendPrimUpdate(this, PrimUpdateFlags.Position); + //ControllingClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_rootRegionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, + // AbsolutePosition, Velocity, Vector3.Zero, m_bodyRot, new Vector4(0,0,1,AbsolutePosition.Z - 0.5f), m_uuid, null, GetUpdatePriority(ControllingClient))); } public void AddNeighbourRegion(ulong regionHandle, string cap) @@ -2360,8 +2367,7 @@ namespace OpenSim.Region.Framework.Scenes //m_log.DebugFormat("[SCENEPRESENCE]: TerseUpdate: Pos={0} Rot={1} Vel={2}", m_pos, m_bodyRot, m_velocity); - remoteClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_rootRegionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, - pos, velocity, Vector3.Zero, m_bodyRot, CollisionPlane, m_uuid, null, GetUpdatePriority(remoteClient))); + remoteClient.SendPrimUpdate(this, PrimUpdateFlags.Position | PrimUpdateFlags.Rotation | PrimUpdateFlags.Velocity | PrimUpdateFlags.Acceleration | PrimUpdateFlags.AngularVelocity); m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); m_scene.StatsReporter.AddAgentUpdates(1); @@ -2457,9 +2463,7 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = m_pos; pos.Z += m_appearance.HipOffset; - remoteAvatar.m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, - LocalId, pos, m_appearance.Texture.GetBytes(), - m_parentID, m_bodyRot)); + remoteAvatar.m_controllingClient.SendAvatarDataImmediate(this); m_scene.StatsReporter.AddAgentUpdates(1); } @@ -2527,8 +2531,7 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = m_pos; pos.Z += m_appearance.HipOffset; - m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, - pos, m_appearance.Texture.GetBytes(), m_parentID, m_bodyRot)); + m_controllingClient.SendAvatarDataImmediate(this); SendInitialFullUpdateToAllClients(); SendAppearanceToAllOtherAgents(); @@ -2638,9 +2641,7 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pos = m_pos; pos.Z += m_appearance.HipOffset; - m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, - pos, m_appearance.Texture.GetBytes(), m_parentID, m_bodyRot)); - + m_controllingClient.SendAvatarDataImmediate(this); } public void SetWearable(int wearableId, AvatarWearable wearable) @@ -3906,7 +3907,7 @@ namespace OpenSim.Region.Framework.Scenes private void Reprioritize(object sender, ElapsedEventArgs e) { - m_controllingClient.ReprioritizeUpdates(StateUpdateTypes.All, UpdatePriority); + m_controllingClient.ReprioritizeUpdates(UpdatePriority); lock (m_reprioritization_timer) { diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 69e78b36ca..84faac0e00 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1045,16 +1045,6 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendAvatarData(SendAvatarData data) - { - - } - - public void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - - } - public void SendCoarseLocationUpdate(List users, List CoarseLocations) { @@ -1065,22 +1055,22 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SetChildAgentThrottle(byte[] throttle) + public void SendAvatarDataImmediate(ISceneEntity avatar) { - + } - public void SendPrimitiveToClient(SendPrimitiveData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { - + } - public void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { - + } - public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void FlushPrimUpdates() { } @@ -1090,11 +1080,6 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void FlushPrimUpdates() - { - - } - public void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item) { @@ -1420,6 +1405,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } + public virtual void SetChildAgentThrottle(byte[] throttle) + { + + } + public byte[] GetThrottlesPacked(float multiplier) { return new byte[0]; diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 6360c9918d..906669187c 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -618,14 +618,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public virtual void SendAvatarData(SendAvatarData data) - { - } - - public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - } - public virtual void SendCoarseLocationUpdate(List users, List CoarseLocations) { } @@ -638,15 +630,15 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public virtual void SendPrimitiveToClient(SendPrimitiveData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { } - public virtual void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { } - public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index b07a0727a3..edb76428b4 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -621,14 +621,6 @@ namespace OpenSim.Tests.Common.Mock { } - public virtual void SendAvatarData(SendAvatarData data) - { - } - - public virtual void SendAvatarTerseUpdate(SendAvatarTerseData data) - { - } - public virtual void SendCoarseLocationUpdate(List users, List CoarseLocations) { } @@ -641,15 +633,15 @@ namespace OpenSim.Tests.Common.Mock { } - public virtual void SendPrimitiveToClient(SendPrimitiveData data) + public void SendAvatarDataImmediate(ISceneEntity avatar) { } - public virtual void SendPrimTerseUpdate(SendPrimitiveTerseData data) + public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { } - public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + public void ReprioritizeUpdates(UpdatePriorityHandler handler) { } From 5d8638ed882681bdfac6c79015089bcaaedfc3ab Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 May 2010 16:05:48 -0700 Subject: [PATCH 116/260] Minor tweak in ProcessEntityUpdates (mostly just confirming the git push is working) --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 6154da4728..11dca8db89 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3476,13 +3476,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP Lazy> compressedUpdateBlocks = new Lazy>(); Lazy> terseUpdateBlocks = new Lazy>(); + if (maxUpdates <= 0) maxUpdates = Int32.MaxValue; int updatesThisCall = 0; lock (m_entityUpdates.SyncRoot) { EntityUpdate update; - while (m_entityUpdates.TryDequeue(out update)) + while (updatesThisCall < maxUpdates && m_entityUpdates.TryDequeue(out update)) { + ++updatesThisCall; + #region UpdateFlags to packet type conversion PrimUpdateFlags updateFlags = update.Flags; @@ -3552,10 +3555,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion Block Construction - - ++updatesThisCall; - if (maxUpdates > 0 && updatesThisCall >= maxUpdates) - break; } } From 4e7013d5d565bf6e9dab8a1c88a5b7c992be6114 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 13 May 2010 07:59:30 -0700 Subject: [PATCH 117/260] Made fields consistently protected. --- .../Services/LLLoginService/LLLoginService.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index f97222ef63..9e24a396c1 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -58,21 +58,21 @@ namespace OpenSim.Services.LLLoginService protected IInventoryService m_InventoryService; protected IGridService m_GridService; protected IPresenceService m_PresenceService; - private ISimulationService m_LocalSimulationService; - private ISimulationService m_RemoteSimulationService; + protected ISimulationService m_LocalSimulationService; + protected ISimulationService m_RemoteSimulationService; protected ILibraryService m_LibraryService; protected IFriendsService m_FriendsService; protected IAvatarService m_AvatarService; - private IUserAgentService m_UserAgentService; + protected IUserAgentService m_UserAgentService; - private GatekeeperServiceConnector m_GatekeeperConnector; + protected GatekeeperServiceConnector m_GatekeeperConnector; - private string m_DefaultRegionName; + protected string m_DefaultRegionName; protected string m_WelcomeMessage; - private bool m_RequireInventory; + protected bool m_RequireInventory; protected int m_MinLoginLevel; - private string m_GatekeeperURL; - private bool m_AllowRemoteSetLoginLevel; + protected string m_GatekeeperURL; + protected bool m_AllowRemoteSetLoginLevel; IConfig m_LoginServerConfig; From 4c740e1717f8071d48e34c584728fddcf05afdb2 Mon Sep 17 00:00:00 2001 From: OpenSim Master Date: Thu, 29 Apr 2010 11:57:30 -0700 Subject: [PATCH 118/260] Implements three new OSSL functions for parcel management: osParcelJoin joins parcels in an area, osParcelSubdivide splits parcels in an area, osParcelSetDetails sets parcel name, description, owner and group owner. Join and Subdivide methods in LandChannel are exposed. --- .../CoreModules/World/Land/LandChannel.cs | 16 ++++ .../World/Land/LandManagementModule.cs | 10 +++ .../Framework/Interfaces/ILandChannel.cs | 3 + .../RegionCombinerLargeLandChannel.cs | 10 +++ .../Shared/Api/Implementation/OSSL_Api.cs | 82 +++++++++++++++++++ .../Shared/Api/Interface/IOSSL_Api.cs | 4 + .../Shared/Api/Runtime/OSSL_Stub.cs | 15 ++++ OpenSim/Tests/Common/Mock/TestLandChannel.cs | 4 + 8 files changed, 144 insertions(+) diff --git a/OpenSim/Region/CoreModules/World/Land/LandChannel.cs b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs index 1fbc733704..1ad4db2404 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandChannel.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs @@ -154,6 +154,22 @@ namespace OpenSim.Region.CoreModules.World.Land m_landManagementModule.UpdateLandObject(localID, data); } } + + public void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) + { + if (m_landManagementModule != null) + { + m_landManagementModule.Join(start_x, start_y, end_x, end_y, attempting_user_id); + } + } + + public void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) + { + if (m_landManagementModule != null) + { + m_landManagementModule.Subdivide(start_x, start_y, end_x, end_y, attempting_user_id); + } + } public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient) { diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index 139e6ff13d..4ccd0f03cb 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -948,6 +948,16 @@ namespace OpenSim.Region.CoreModules.World.Land masterLandObject.SendLandUpdateToAvatarsOverMe(); } + public void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) + { + join(start_x, start_y, end_x, end_y, attempting_user_id); + } + + public void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) + { + subdivide(start_x, start_y, end_x, end_y, attempting_user_id); + } + #endregion #region Parcel Updating diff --git a/OpenSim/Region/Framework/Interfaces/ILandChannel.cs b/OpenSim/Region/Framework/Interfaces/ILandChannel.cs index f71e31dcfa..20b8ab6b6d 100644 --- a/OpenSim/Region/Framework/Interfaces/ILandChannel.cs +++ b/OpenSim/Region/Framework/Interfaces/ILandChannel.cs @@ -76,5 +76,8 @@ namespace OpenSim.Region.Framework.Interfaces void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel); void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel); void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime); + + void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id); + void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id); } } diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs index 9da818a7eb..33ff707e9f 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerLargeLandChannel.cs @@ -140,6 +140,16 @@ public class RegionCombinerLargeLandChannel : ILandChannel RootRegionLandChannel.UpdateLandObject(localID, data); } + public void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) + { + RootRegionLandChannel.Join(start_x, start_y, end_x, end_y, attempting_user_id); + } + + public void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) + { + RootRegionLandChannel.Subdivide(start_x, start_y, end_x, end_y, attempting_user_id); + } + public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient) { RootRegionLandChannel.ReturnObjectsInParcel(localID, returnType, agentIDs, taskIDs, remoteClient); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 7e68cc749c..15469db94f 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -1129,7 +1129,89 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return 0.0f; } + // Routines for creating and managing parcels programmatically + public void osParcelJoin(LSL_Vector pos1, LSL_Vector pos2) + { + CheckThreatLevel(ThreatLevel.High, "osParcelJoin"); + m_host.AddScriptLPS(1); + int startx = (int)(pos1.x < pos2.x ? pos1.x : pos2.x); + int starty = (int)(pos1.y < pos2.y ? pos1.y : pos2.y); + int endx = (int)(pos1.x > pos2.x ? pos1.x : pos2.x); + int endy = (int)(pos1.y > pos2.y ? pos1.y : pos2.y); + + World.LandChannel.Join(startx,starty,endx,endy,m_host.OwnerID); + } + + public void osParcelSubdivide(LSL_Vector pos1, LSL_Vector pos2) + { + CheckThreatLevel(ThreatLevel.High, "osParcelSubdivide"); + m_host.AddScriptLPS(1); + + int startx = (int)(pos1.x < pos2.x ? pos1.x : pos2.x); + int starty = (int)(pos1.y < pos2.y ? pos1.y : pos2.y); + int endx = (int)(pos1.x > pos2.x ? pos1.x : pos2.x); + int endy = (int)(pos1.y > pos2.y ? pos1.y : pos2.y); + + World.LandChannel.Subdivide(startx,starty,endx,endy,m_host.OwnerID); + } + + public void osParcelSetDetails(LSL_Vector pos, LSL_List rules) + { + CheckThreatLevel(ThreatLevel.High, "osParcelSetDetails"); + m_host.AddScriptLPS(1); + + // Get a reference to the land data and make sure the owner of the script + // can modify it + + ILandObject startLandObject = World.LandChannel.GetLandObject((int)pos.x, (int)pos.y); + if (startLandObject == null) + { + OSSLShoutError("There is no land at that location"); + return; + } + + if (! World.Permissions.CanEditParcel(m_host.OwnerID, startLandObject)) + { + OSSLShoutError("You do not have permission to modify the parcel"); + return; + } + + // Create a new land data object we can modify + LandData newLand = startLandObject.LandData.Copy(); + UUID uuid; + + // Process the rules, not sure what the impact would be of changing owner or group + for (int idx = 0; idx < rules.Length; ) + { + int code = rules.GetLSLIntegerItem(idx++); + string arg = rules.GetLSLStringItem(idx++); + switch (code) + { + case 0: + newLand.Name = arg; + break; + + case 1: + newLand.Description = arg; + break; + + case 2: + CheckThreatLevel(ThreatLevel.VeryHigh, "osParcelSetDetails"); + if (UUID.TryParse(arg , out uuid)) + newLand.OwnerID = uuid; + break; + + case 3: + CheckThreatLevel(ThreatLevel.VeryHigh, "osParcelSetDetails"); + if (UUID.TryParse(arg , out uuid)) + newLand.GroupID = uuid; + break; + } + } + + World.LandChannel.UpdateLandObject(newLand.LocalID,newLand); + } public double osList2Double(LSL_Types.list src, int index) { diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 60b8050cd1..7a8f469746 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -123,6 +123,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces void osWindParamSet(string plugin, string param, float value); float osWindParamGet(string plugin, string param); + // Parcel commands + void osParcelJoin(vector pos1, vector pos2); + void osParcelSubdivide(vector pos1, vector pos2); + void osParcelSetDetails(vector pos, LSL_List rules); string osGetScriptEngineName(); string osGetSimulatorVersion(); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 3870af3213..fd9309aefc 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -106,6 +106,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase // return m_OSSL_Functions.osWindParamGet(plugin, param); // } + public void osParcelJoin(vector pos1, vector pos2) + { + m_OSSL_Functions.osParcelJoin(pos1,pos2); + } + + public void osParcelSubdivide(vector pos1, vector pos2) + { + m_OSSL_Functions.osParcelSubdivide(pos1, pos2); + } + + public void osParcelSetDetails(vector pos, LSL_List rules) + { + m_OSSL_Functions.osParcelSetDetails(pos,rules); + } + public double osList2Double(LSL_Types.list src, int index) { return m_OSSL_Functions.osList2Double(src, index); diff --git a/OpenSim/Tests/Common/Mock/TestLandChannel.cs b/OpenSim/Tests/Common/Mock/TestLandChannel.cs index be28c270df..159764cff8 100644 --- a/OpenSim/Tests/Common/Mock/TestLandChannel.cs +++ b/OpenSim/Tests/Common/Mock/TestLandChannel.cs @@ -85,5 +85,9 @@ namespace OpenSim.Tests.Common.Mock public void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel) {} public void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel) {} public void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime) {} + + public void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) {} + public void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) {} + } } From 3d72a73b22b0cf0dbe951a3e0ae28473728b01a0 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey Date: Fri, 14 May 2010 21:22:53 +0100 Subject: [PATCH 119/260] Apply patch from http://opensimulator.org/mantis/bug_view_page.php?bug_id=4671 Fixes a bug where the viewer didn't recieve the uuid of a chat broadcasting object Thanks crystalsgalicia! --- OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs index 6dacbba0df..02f0968c40 100644 --- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs @@ -264,6 +264,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat fromName = avatar.Name; sourceType = ChatSourceType.Agent; } + else if (c.SenderUUID != UUID.Zero) + { + fromID = c.SenderUUID; + } // m_log.DebugFormat("[CHAT] Broadcast: fromID {0} fromName {1}, cType {2}, sType {3}", fromID, fromName, cType, sourceType); From 98f2b798ff9e23c3cbc2fdf33d6ea4956b80ba8a Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 3 May 2010 21:34:38 +0100 Subject: [PATCH 120/260] Address symptom of Mantis 4588 (though not the cause) by moving the avatar dereference inside the exception catch --- OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs index 9df60743ae..a895d6e838 100644 --- a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs @@ -143,10 +143,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Combat.CombatModule } private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID) - { - ILandObject obj = avatar.Scene.LandChannel.GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); + { try { + ILandObject obj = avatar.Scene.LandChannel.GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); + if ((obj.LandData.Flags & (uint)ParcelFlags.AllowDamage) != 0) { avatar.Invulnerable = false; From df2f69f295a16a0e3de510de566d8bb607c9664d Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey Date: Sat, 15 May 2010 02:07:07 +0100 Subject: [PATCH 121/260] Make "nant distbin" remove BUILDING.txt --- .nant/local.include | 1 + 1 file changed, 1 insertion(+) diff --git a/.nant/local.include b/.nant/local.include index b78b3efcdc..97353e05f2 100644 --- a/.nant/local.include +++ b/.nant/local.include @@ -20,6 +20,7 @@ + From 4b755c6d80b3161dda7a7e70fed0e1f5e81ea8ad Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Sat, 15 May 2010 02:17:10 +0100 Subject: [PATCH 122/260] delete now unused MessageServerInfo --- .../Framework/Servers/MessageServerInfo.cs | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 OpenSim/Framework/Servers/MessageServerInfo.cs diff --git a/OpenSim/Framework/Servers/MessageServerInfo.cs b/OpenSim/Framework/Servers/MessageServerInfo.cs deleted file mode 100644 index 57ceb7184c..0000000000 --- a/OpenSim/Framework/Servers/MessageServerInfo.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 OpenSimulator 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.Collections.Generic; - -namespace OpenSim.Framework.Servers -{ - public class MessageServerInfo - { - public string URI; - public string sendkey; - public string recvkey; - public List responsibleForRegions; - - public MessageServerInfo() - { - } - - public override string ToString() - { - return URI; - } - } -} From 36bcab5f075089f18a5c80f3f988c1e6605c16e5 Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Tue, 4 May 2010 16:49:46 -0700 Subject: [PATCH 123/260] Refactor scene presence list for lockless iteration. Lock contention will now only be for simultaneous add/removes of scene presences from the scene. --- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 114 ++++++++---------- 1 file changed, 53 insertions(+), 61 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index ce11267196..ef13c9834a 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -68,8 +68,9 @@ namespace OpenSim.Region.Framework.Scenes #region Fields - protected Dictionary m_scenePresences = new Dictionary(); - protected ScenePresence[] m_scenePresenceArray = new ScenePresence[0]; + protected object m_presenceLock = new object(); + protected Dictionary m_scenePresenceMap = new Dictionary(); + protected List m_scenePresenceArray = new List(); // SceneObjects is not currently populated or used. //public Dictionary SceneObjects; @@ -132,10 +133,12 @@ namespace OpenSim.Region.Framework.Scenes protected internal void Close() { - lock (m_scenePresences) + lock (m_presenceLock) { - m_scenePresences.Clear(); - m_scenePresenceArray = new ScenePresence[0]; + Dictionary newmap = new Dictionary(); + List newlist = new List(); + m_scenePresenceMap = newmap; + m_scenePresenceArray = newlist; } lock (m_dictionary_lock) @@ -518,34 +521,29 @@ namespace OpenSim.Region.Framework.Scenes Entities[presence.UUID] = presence; - lock (m_scenePresences) + lock (m_presenceLock) { - if (!m_scenePresences.ContainsKey(presence.UUID)) - { - m_scenePresences.Add(presence.UUID, presence); + Dictionary newmap = new Dictionary(m_scenePresenceMap); + List newlist = new List(m_scenePresenceArray); - // Create a new array of ScenePresence references - int oldLength = m_scenePresenceArray.Length; - ScenePresence[] newArray = new ScenePresence[oldLength + 1]; - Array.Copy(m_scenePresenceArray, newArray, oldLength); - newArray[oldLength] = presence; - m_scenePresenceArray = newArray; + if (!newmap.ContainsKey(presence.UUID)) + { + newmap.Add(presence.UUID, presence); + newlist.Add(presence); } else { - m_scenePresences[presence.UUID] = presence; - - // Do a linear search through the array of ScenePresence references - // and update the modified entry - for (int i = 0; i < m_scenePresenceArray.Length; i++) - { - if (m_scenePresenceArray[i].UUID == presence.UUID) - { - m_scenePresenceArray[i] = presence; - break; - } - } + // Remember the old presene reference from the dictionary + ScenePresence oldref = newmap[presence.UUID]; + // Replace the presence reference in the dictionary with the new value + newmap[presence.UUID] = presence; + // Find the index in the list where the old ref was stored and update the reference + newlist[newlist.IndexOf(oldref)] = presence; } + + // Swap out the dictionary and list with new references + m_scenePresenceMap = newmap; + m_scenePresenceArray = newlist; } } @@ -561,25 +559,21 @@ namespace OpenSim.Region.Framework.Scenes agentID); } - lock (m_scenePresences) + lock (m_presenceLock) { - if (m_scenePresences.Remove(agentID)) + Dictionary newmap = new Dictionary(m_scenePresenceMap); + List newlist = new List(m_scenePresenceArray); + + // Remember the old presene reference from the dictionary + ScenePresence oldref = newmap[agentID]; + // Remove the presence reference from the dictionary + if (newmap.Remove(agentID)) { - // Copy all of the elements from the previous array - // into the new array except the removed element - int oldLength = m_scenePresenceArray.Length; - ScenePresence[] newArray = new ScenePresence[oldLength - 1]; - int j = 0; - for (int i = 0; i < m_scenePresenceArray.Length; i++) - { - ScenePresence presence = m_scenePresenceArray[i]; - if (presence.UUID != agentID) - { - newArray[j] = presence; - ++j; - } - } - m_scenePresenceArray = newArray; + // Find the index in the list where the old ref was stored and remove the reference + newlist.RemoveAt(newlist.IndexOf(oldref)); + // Swap out the dictionary and list with new references + m_scenePresenceMap = newmap; + m_scenePresenceArray = newlist; } else { @@ -698,7 +692,7 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// Request a copy of m_scenePresences in this World + /// Get a reference to the scene presence list. Changes to the list will be done in a copy /// There is no guarantee that presences will remain in the scene after the list is returned. /// This list should remain private to SceneGraph. Callers wishing to iterate should instead /// pass a delegate to ForEachScenePresence. @@ -706,8 +700,7 @@ namespace OpenSim.Region.Framework.Scenes /// private List GetScenePresences() { - lock (m_scenePresences) - return new List(m_scenePresenceArray); + return m_scenePresenceArray; } /// @@ -717,12 +710,10 @@ namespace OpenSim.Region.Framework.Scenes /// null if the presence was not found protected internal ScenePresence GetScenePresence(UUID agentID) { - ScenePresence sp; - lock (m_scenePresences) - { - m_scenePresences.TryGetValue(agentID, out sp); - } - return sp; + Dictionary presences = m_scenePresenceMap; + ScenePresence presence; + presences.TryGetValue(agentID, out presence); + return presence; } /// @@ -733,7 +724,8 @@ namespace OpenSim.Region.Framework.Scenes /// null if the presence was not found protected internal ScenePresence GetScenePresence(string firstName, string lastName) { - foreach (ScenePresence presence in GetScenePresences()) + List presences = GetScenePresences(); + foreach (ScenePresence presence in presences) { if (presence.Firstname == firstName && presence.Lastname == lastName) return presence; @@ -748,7 +740,8 @@ namespace OpenSim.Region.Framework.Scenes /// null if the presence was not found protected internal ScenePresence GetScenePresence(uint localID) { - foreach (ScenePresence presence in GetScenePresences()) + List presences = GetScenePresences(); + foreach (ScenePresence presence in presences) if (presence.LocalId == localID) return presence; return null; @@ -756,10 +749,8 @@ namespace OpenSim.Region.Framework.Scenes protected internal bool TryGetScenePresence(UUID agentID, out ScenePresence avatar) { - lock (m_scenePresences) - { - m_scenePresences.TryGetValue(agentID, out avatar); - } + Dictionary presences = m_scenePresenceMap; + presences.TryGetValue(agentID, out avatar); return (avatar != null); } @@ -1036,8 +1027,9 @@ namespace OpenSim.Region.Framework.Scenes }); Parallel.ForEach(GetScenePresences(), protectedAction); */ - // For now, perform actiona serially - foreach (ScenePresence sp in GetScenePresences()) + // For now, perform actions serially + List presences = GetScenePresences(); + foreach (ScenePresence sp in presences) { try { From 2a1e45f65736214a9e8d782be1f92bb78725121f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sat, 15 May 2010 19:25:14 -0700 Subject: [PATCH 124/260] Finalized the client's TCP IP address verification process for HG1.5. --- OpenSim/Framework/AgentCircuitManager.cs | 26 +++++ OpenSim/Framework/Capabilities/Caps.cs | 12 ++- OpenSim/Framework/IScene.cs | 2 + .../Agent/Capabilities/CapabilitiesModule.cs | 2 +- OpenSim/Region/Framework/Scenes/Scene.cs | 95 ++++++++++++++----- OpenSim/Region/Framework/Scenes/SceneBase.cs | 1 + .../Framework/Scenes/Tests/SceneBaseTests.cs | 5 + .../Hypergrid/UserAgentServiceConnector.cs | 5 + .../HypergridService/UserAgentService.cs | 27 +++--- .../Services/Interfaces/IGatekeeperService.cs | 1 + .../Services/LLLoginService/LLLoginService.cs | 17 ++-- 11 files changed, 148 insertions(+), 45 deletions(-) diff --git a/OpenSim/Framework/AgentCircuitManager.cs b/OpenSim/Framework/AgentCircuitManager.cs index e5dbb5a81a..49d7822416 100644 --- a/OpenSim/Framework/AgentCircuitManager.cs +++ b/OpenSim/Framework/AgentCircuitManager.cs @@ -36,6 +36,7 @@ namespace OpenSim.Framework public class AgentCircuitManager { public Dictionary AgentCircuits = new Dictionary(); + public Dictionary AgentCircuitsByUUID = new Dictionary(); public virtual AuthenticateResponse AuthenticateSession(UUID sessionID, UUID agentID, uint circuitcode) { @@ -86,10 +87,12 @@ namespace OpenSim.Framework if (AgentCircuits.ContainsKey(circuitCode)) { AgentCircuits[circuitCode] = agentData; + AgentCircuitsByUUID[agentData.AgentID] = agentData; } else { AgentCircuits.Add(circuitCode, agentData); + AgentCircuitsByUUID.Add(agentData.AgentID, agentData); } } } @@ -99,10 +102,26 @@ namespace OpenSim.Framework lock (AgentCircuits) { if (AgentCircuits.ContainsKey(circuitCode)) + { + UUID agentID = AgentCircuits[circuitCode].AgentID; AgentCircuits.Remove(circuitCode); + AgentCircuitsByUUID.Remove(agentID); + } } } + public virtual void RemoveCircuit(UUID agentID) + { + lock (AgentCircuits) + { + if (AgentCircuitsByUUID.ContainsKey(agentID)) + { + uint circuitCode = AgentCircuitsByUUID[agentID].circuitcode; + AgentCircuits.Remove(circuitCode); + AgentCircuitsByUUID.Remove(agentID); + } + } + } public AgentCircuitData GetAgentCircuitData(uint circuitCode) { AgentCircuitData agentCircuit = null; @@ -110,6 +129,13 @@ namespace OpenSim.Framework return agentCircuit; } + public AgentCircuitData GetAgentCircuitData(UUID agentID) + { + AgentCircuitData agentCircuit = null; + AgentCircuitsByUUID.TryGetValue(agentID, out agentCircuit); + return agentCircuit; + } + public void UpdateAgentData(AgentCircuitData agentData) { if (AgentCircuits.ContainsKey((uint) agentData.circuitcode)) diff --git a/OpenSim/Framework/Capabilities/Caps.cs b/OpenSim/Framework/Capabilities/Caps.cs index b27d0112af..62a1e17f50 100644 --- a/OpenSim/Framework/Capabilities/Caps.cs +++ b/OpenSim/Framework/Capabilities/Caps.cs @@ -99,6 +99,7 @@ namespace OpenSim.Framework.Capabilities // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. //private string eventQueue = "0100/"; + private IScene m_Scene; private IHttpServer m_httpListener; private UUID m_agentID; private IAssetService m_assetCache; @@ -130,9 +131,10 @@ namespace OpenSim.Framework.Capabilities public FetchInventoryDescendentsCAPS CAPSFetchInventoryDescendents = null; public GetClientDelegate GetClient = null; - public Caps(IAssetService assetCache, IHttpServer httpServer, string httpListen, uint httpPort, string capsPath, + public Caps(IScene scene, IAssetService assetCache, IHttpServer httpServer, string httpListen, uint httpPort, string capsPath, UUID agent, bool dumpAssetsToFile, string regionName) { + m_Scene = scene; m_assetCache = assetCache; m_capsObjectPath = capsPath; m_httpListener = httpServer; @@ -278,7 +280,13 @@ namespace OpenSim.Framework.Capabilities public string CapsRequest(string request, string path, string param, OSHttpRequest httpRequest, OSHttpResponse httpResponse) { - //m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); + m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); + + if (!m_Scene.CheckClient(m_agentID, httpRequest.RemoteIPEndPoint)) + { + m_log.DebugFormat("[CAPS]: Unauthorized CAPS client"); + return string.Empty; + } string result = LLSDHelpers.SerialiseLLSDReply(m_capsHandlers.CapsDetails); diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 19ab409098..6798b7be8a 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -102,5 +102,7 @@ namespace OpenSim.Framework void AddCommand(object module, string command, string shorthelp, string longhelp, CommandDelegate callback); ISceneObject DeserializeObject(string representation); + + bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); } } diff --git a/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs index 2a1355b9ef..a6f5d97ad2 100644 --- a/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs +++ b/OpenSim/Region/CoreModules/Agent/Capabilities/CapabilitiesModule.cs @@ -107,7 +107,7 @@ namespace OpenSim.Region.CoreModules.Agent.Capabilities } Caps caps - = new Caps( + = new Caps(m_scene, m_scene.AssetService, MainServer.Instance, m_scene.RegionInfo.ExternalHostName, MainServer.Instance.Port, capsObjectPath, agentId, m_scene.DumpAssetsToFile, m_scene.RegionInfo.RegionName); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index edbef4c884..401551de36 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2629,34 +2629,23 @@ namespace OpenSim.Region.Framework.Scenes AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); // Do the verification here - System.Net.EndPoint ep = client.GetClientEP(); + System.Net.IPEndPoint ep = (System.Net.IPEndPoint)client.GetClientEP(); if (aCircuit != null) { - if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0) + if (!VerifyClient(aCircuit, ep, out vialogin)) { - m_log.DebugFormat("[Scene]: Incoming client {0} {1} in region {2} via Login", aCircuit.firstname, aCircuit.lastname, RegionInfo.RegionName); - vialogin = true; - IUserAgentVerificationModule userVerification = RequestModuleInterface(); - if (userVerification != null && ep != null) + // uh-oh, this is fishy + m_log.WarnFormat("[Scene]: Agent {0} with session {1} connecting with unidentified end point {2}. Refusing service.", + client.AgentId, client.SessionId, ep.ToString()); + try { - if (!userVerification.VerifyClient(aCircuit, ep.ToString())) - { - // uh-oh, this is fishy - m_log.WarnFormat("[Scene]: Agent {0} with session {1} connecting with unidentified end point {2}. Refusing service.", - client.AgentId, client.SessionId, ep.ToString()); - try - { - client.Close(); - } - catch (Exception e) - { - m_log.DebugFormat("[Scene]: Exception while closing aborted client: {0}", e.StackTrace); - } - return; - } - else - m_log.DebugFormat("[Scene]: User Client Verification for {0} {1} returned true", aCircuit.firstname, aCircuit.lastname); + client.Close(); } + catch (Exception e) + { + m_log.DebugFormat("[Scene]: Exception while closing aborted client: {0}", e.StackTrace); + } + return; } } @@ -2682,7 +2671,65 @@ namespace OpenSim.Region.Framework.Scenes EventManager.TriggerOnClientLogin(client); } - + private bool VerifyClient(AgentCircuitData aCircuit, System.Net.IPEndPoint ep, out bool vialogin) + { + vialogin = false; + + // Do the verification here + if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0) + { + m_log.DebugFormat("[Scene]: Incoming client {0} {1} in region {2} via Login", aCircuit.firstname, aCircuit.lastname, RegionInfo.RegionName); + vialogin = true; + IUserAgentVerificationModule userVerification = RequestModuleInterface(); + if (userVerification != null && ep != null) + { + if (!userVerification.VerifyClient(aCircuit, ep.Address.ToString())) + { + // uh-oh, this is fishy + m_log.DebugFormat("[Scene]: User Client Verification for {0} {1} in {2} returned false", aCircuit.firstname, aCircuit.lastname, RegionInfo.RegionName); + return false; + } + else + m_log.DebugFormat("[Scene]: User Client Verification for {0} {1} in {2} returned true", aCircuit.firstname, aCircuit.lastname, RegionInfo.RegionName); + } + } + + return true; + } + + // Called by Caps, on the first HTTP contact from the client + public override bool CheckClient(UUID agentID, System.Net.IPEndPoint ep) + { + AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(agentID); + if (aCircuit != null) + { + bool vialogin = false; + if (!VerifyClient(aCircuit, ep, out vialogin)) + { + // if it doesn't pass, we remove the agentcircuitdata altogether + // and the scene presence and the client, if they exist + try + { + ScenePresence sp = GetScenePresence(agentID); + if (sp != null) + sp.ControllingClient.Close(); + + // BANG! SLASH! + m_authenticateHandler.RemoveCircuit(agentID); + + return false; + } + catch (Exception e) + { + m_log.DebugFormat("[Scene]: Exception while closing aborted client: {0}", e.StackTrace); + } + } + else + return true; + } + + return false; + } /// /// Register for events from the client diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 3218dadc24..bfc19b754c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -536,5 +536,6 @@ namespace OpenSim.Region.Framework.Scenes get { return false; } } + public abstract bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); } } diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs index dd9f8f6654..42587c1116 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneBaseTests.cs @@ -70,6 +70,11 @@ namespace OpenSim.Region.Framework.Scenes.Tests { throw new NotImplementedException(); } + + public override bool CheckClient(UUID agentID, System.Net.IPEndPoint ep) + { + throw new NotImplementedException(); + } } [Test] diff --git a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs index 3e91e3a113..42eca05ec5 100644 --- a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs @@ -204,6 +204,11 @@ namespace OpenSim.Services.Connectors.Hypergrid return args; } + public void SetClientToken(UUID sessionID, string token) + { + // no-op + } + public GridRegion GetHomeRegion(UUID userID, out Vector3 position, out Vector3 lookAt) { position = Vector3.UnitY; lookAt = Vector3.UnitY; diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 64f7e8aaca..01729607fa 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -148,6 +148,15 @@ namespace OpenSim.Services.HypergridService return true; } + public void SetClientToken(UUID sessionID, string token) + { + if (m_TravelingAgents.ContainsKey(sessionID)) + { + m_log.DebugFormat("[USER AGENT SERVICE]: Setting token {0} for session {1}", token, sessionID); + m_TravelingAgents[sessionID].ClientToken = token; + } + } + TravelingAgentInfo UpdateTravelInfo(AgentCircuitData agentCircuit, GridRegion region) { TravelingAgentInfo travel = new TravelingAgentInfo(); @@ -203,22 +212,16 @@ namespace OpenSim.Services.HypergridService public bool VerifyClient(UUID sessionID, string token) { - return true; + m_log.DebugFormat("[USER AGENT SERVICE]: Verifying Client session {0} with token {1}", sessionID, token); + //return true; // Commenting this for now until I understand better what part of a sender's // info stays unchanged throughout a session // - //if (m_TravelingAgents.ContainsKey(sessionID)) - //{ - // // Aquiles heel. Must trust the first grid upon login - // if (m_TravelingAgents[sessionID].ClientToken == string.Empty) - // { - // m_TravelingAgents[sessionID].ClientToken = token; - // return true; - // } - // return m_TravelingAgents[sessionID].ClientToken == token; - //} - //return false; + if (m_TravelingAgents.ContainsKey(sessionID)) + return m_TravelingAgents[sessionID].ClientToken == token; + + return false; } public bool VerifyAgent(UUID sessionID, string token) diff --git a/OpenSim/Services/Interfaces/IGatekeeperService.cs b/OpenSim/Services/Interfaces/IGatekeeperService.cs index ca7b9b3324..2d397bc03d 100644 --- a/OpenSim/Services/Interfaces/IGatekeeperService.cs +++ b/OpenSim/Services/Interfaces/IGatekeeperService.cs @@ -49,6 +49,7 @@ namespace OpenSim.Services.Interfaces public interface IUserAgentService { bool LoginAgentToGrid(AgentCircuitData agent, GridRegion gatekeeper, GridRegion finalDestination, out string reason); + void SetClientToken(UUID sessionID, string token); void LogoutAgent(UUID userID, UUID sessionID); GridRegion GetHomeRegion(UUID userID, out Vector3 position, out Vector3 lookAt); diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 9e24a396c1..7471c7721d 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -329,7 +329,7 @@ namespace OpenSim.Services.LLLoginService // Instantiate/get the simulation interface and launch an agent at the destination // string reason = string.Empty; - AgentCircuitData aCircuit = LaunchAgentAtGrid(gatekeeper, destination, account, avatar, session, secureSession, position, where, clientVersion, out where, out reason); + AgentCircuitData aCircuit = LaunchAgentAtGrid(gatekeeper, destination, account, avatar, session, secureSession, position, where, clientVersion, clientIP, out where, out reason); if (aCircuit == null) { @@ -589,7 +589,7 @@ namespace OpenSim.Services.LLLoginService } protected AgentCircuitData LaunchAgentAtGrid(GridRegion gatekeeper, GridRegion destination, UserAccount account, AvatarData avatar, - UUID session, UUID secureSession, Vector3 position, string currentWhere, string viewer, out string where, out string reason) + UUID session, UUID secureSession, Vector3 position, string currentWhere, string viewer, IPEndPoint clientIP, out string where, out string reason) { where = currentWhere; ISimulationService simConnector = null; @@ -655,7 +655,7 @@ namespace OpenSim.Services.LLLoginService { circuitCode = (uint)Util.RandomClass.Next(); ; aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, viewer); - success = LaunchAgentIndirectly(gatekeeper, destination, aCircuit, out reason); + success = LaunchAgentIndirectly(gatekeeper, destination, aCircuit, clientIP, out reason); if (!success && m_GridService != null) { // Try the fallback regions @@ -664,7 +664,7 @@ namespace OpenSim.Services.LLLoginService { foreach (GridRegion r in fallbacks) { - success = LaunchAgentIndirectly(gatekeeper, r, aCircuit, out reason); + success = LaunchAgentIndirectly(gatekeeper, r, aCircuit, clientIP, out reason); if (success) { where = "safe"; @@ -741,10 +741,15 @@ namespace OpenSim.Services.LLLoginService return simConnector.CreateAgent(region, aCircuit, (int)Constants.TeleportFlags.ViaLogin, out reason); } - private bool LaunchAgentIndirectly(GridRegion gatekeeper, GridRegion destination, AgentCircuitData aCircuit, out string reason) + private bool LaunchAgentIndirectly(GridRegion gatekeeper, GridRegion destination, AgentCircuitData aCircuit, IPEndPoint clientIP, out string reason) { m_log.Debug("[LLOGIN SERVICE] Launching agent at " + destination.RegionName); - return m_UserAgentService.LoginAgentToGrid(aCircuit, gatekeeper, destination, out reason); + if (m_UserAgentService.LoginAgentToGrid(aCircuit, gatekeeper, destination, out reason)) + { + m_UserAgentService.SetClientToken(aCircuit.SessionID, clientIP.Address.ToString()); + return true; + } + return false; } #region Console Commands From 74b23ff9c644f20e04ebda1c03eb69af29c417cc Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sat, 15 May 2010 19:58:30 -0700 Subject: [PATCH 125/260] Almost, but not quite. Commenting verification again, until I understand where 127.0.0.1 is being transformed to the local IP address. I suspect it's Adam's NAT snippets. --- OpenSim/Services/HypergridService/UserAgentService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 01729607fa..4a275c6f58 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -213,15 +213,15 @@ namespace OpenSim.Services.HypergridService public bool VerifyClient(UUID sessionID, string token) { m_log.DebugFormat("[USER AGENT SERVICE]: Verifying Client session {0} with token {1}", sessionID, token); - //return true; + return true; // Commenting this for now until I understand better what part of a sender's // info stays unchanged throughout a session // - if (m_TravelingAgents.ContainsKey(sessionID)) - return m_TravelingAgents[sessionID].ClientToken == token; + //if (m_TravelingAgents.ContainsKey(sessionID)) + // return m_TravelingAgents[sessionID].ClientToken == token; - return false; + //return false; } public bool VerifyAgent(UUID sessionID, string token) From dc1a3e9787d15f98e815b10935481606edc38f3f Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sat, 15 May 2010 23:21:36 -0400 Subject: [PATCH 126/260] * Add User Friendly Configuration File Exists check. If OpenSim.ini and either StandaloneCommon.ini or GridCommon.ini don't exist in various casings then offer to copy the files for the user while warning them that they're missing out if they don't read the files. --- OpenSim/Region/Application/Application.cs | 116 +++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Application/Application.cs b/OpenSim/Region/Application/Application.cs index 7721cdf491..951d66f81d 100644 --- a/OpenSim/Region/Application/Application.cs +++ b/OpenSim/Region/Application/Application.cs @@ -105,7 +105,7 @@ namespace OpenSim // Check if the system is compatible with OpenSimulator. // Ensures that the minimum system requirements are met - m_log.Info("Performing compatibility checks... "); + m_log.Info("Performing compatibility checks... \n"); string supported = String.Empty; if (Util.IsEnvironmentSupported(ref supported)) { @@ -120,6 +120,112 @@ namespace OpenSim Culture.SetCurrentCulture(); + // Validate that the user has the most basic configuration done + // If not, offer to do the most basic configuration for them warning them along the way of the importance of + // reading these files. + m_log.Info("Checking for reguired configuration...\n"); + + bool OpenSim_Ini = (File.Exists(Path.Combine(Util.configDir(), "OpenSim.ini"))) + || (File.Exists(Path.Combine(Util.configDir(), "opensim.ini"))) + || (File.Exists(Path.Combine(Util.configDir(), "openSim.ini"))) + || (File.Exists(Path.Combine(Util.configDir(), "Opensim.ini"))); + + bool StanaloneCommon_ProperCased = File.Exists(Path.Combine(Path.Combine(Util.configDir(), "config-include"), "StandaloneCommon.ini")); + bool StanaloneCommon_lowercased = File.Exists(Path.Combine(Path.Combine(Util.configDir(), "config-include"), "standalonecommon.ini")); + bool GridCommon_ProperCased = File.Exists(Path.Combine(Path.Combine(Util.configDir(), "config-include"), "GridCommon.ini")); + bool GridCommon_lowerCased = File.Exists(Path.Combine(Path.Combine(Util.configDir(), "config-include"), "gridcommon.ini")); + + if ((OpenSim_Ini) + && ( + (StanaloneCommon_ProperCased + || StanaloneCommon_lowercased + || GridCommon_ProperCased + || GridCommon_lowerCased + ))) + { + m_log.Info("Required Configuration Files Found\n"); + } + else + { + MainConsole.Instance = new LocalConsole("Region"); + string resp = MainConsole.Instance.CmdPrompt( + "\n\n*************Required Configuration files not found.*************\n\n OpenSimulator will not run without these files.\n\nRemember, these file names are Case Sensitive in Linux and Proper Cased.\n1. ./OpenSim.ini\nand\n2. ./config-include/StandaloneCommon.ini \nor\n3. ./config-include/GridCommon.ini\n\nAlso, you will want to examine these files in great detail because only the basic system will load by default. OpenSimulator can do a LOT more if you spend a little time going through these files.\n\n" + ": " + "Do you want to copy the most basic Defaults from standalone?", + "yes"); + if (resp == "yes") + { + + if (!(OpenSim_Ini)) + { + try + { + File.Copy(Path.Combine(Util.configDir(), "OpenSim.ini.example"), + Path.Combine(Util.configDir(), "OpenSim.ini")); + } catch (UnauthorizedAccessException) + { + MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, Make sure OpenSim has have the required permissions\n"); + } catch (ArgumentException) + { + MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, The current directory is invalid.\n"); + } catch (System.IO.PathTooLongException) + { + MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, the Path to these files is too long.\n"); + } catch (System.IO.DirectoryNotFoundException) + { + MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, the current directory is reporting as not found.\n"); + } catch (System.IO.FileNotFoundException) + { + MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, the example is not found, please make sure that the example files exist.\n"); + } catch (System.IO.IOException) + { + // Destination file exists already or a hard drive failure... .. so we can just drop this one + //MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, the example is not found, please make sure that the example files exist.\n"); + } catch (System.NotSupportedException) + { + MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, The current directory is invalid.\n"); + } + + } + if (!(StanaloneCommon_ProperCased || StanaloneCommon_lowercased)) + { + try + { + File.Copy(Path.Combine(Path.Combine(Util.configDir(), "config-include"), "StandaloneCommon.ini.example"), + Path.Combine(Path.Combine(Util.configDir(), "config-include"), "StandaloneCommon.ini")); + } + catch (UnauthorizedAccessException) + { + MainConsole.Instance.Output("Unable to Copy StandaloneCommon.ini.example to StandaloneCommon.ini, Make sure OpenSim has the required permissions\n"); + } + catch (ArgumentException) + { + MainConsole.Instance.Output("Unable to Copy StandaloneCommon.ini.example to StandaloneCommon.ini, The current directory is invalid.\n"); + } + catch (System.IO.PathTooLongException) + { + MainConsole.Instance.Output("Unable to Copy StandaloneCommon.ini.example to StandaloneCommon.ini, the Path to these files is too long.\n"); + } + catch (System.IO.DirectoryNotFoundException) + { + MainConsole.Instance.Output("Unable to Copy StandaloneCommon.ini.example to StandaloneCommon.ini, the current directory is reporting as not found.\n"); + } + catch (System.IO.FileNotFoundException) + { + MainConsole.Instance.Output("Unable to Copy StandaloneCommon.ini.example to StandaloneCommon.ini, the example is not found, please make sure that the example files exist.\n"); + } + catch (System.IO.IOException) + { + // Destination file exists already or a hard drive failure... .. so we can just drop this one + //MainConsole.Instance.Output("Unable to Copy OpenSim.ini.example to OpenSim.ini, the example is not found, please make sure that the example files exist.\n"); + } + catch (System.NotSupportedException) + { + MainConsole.Instance.Output("Unable to Copy StandaloneCommon.ini.example to StandaloneCommon.ini, The current directory is invalid.\n"); + } + } + } + MainConsole.Instance = null; + } + configSource.Alias.AddAlias("On", true); configSource.Alias.AddAlias("Off", false); configSource.Alias.AddAlias("True", true); @@ -145,6 +251,8 @@ namespace OpenSim // load Crash directory config m_crashDir = configSource.Configs["Startup"].GetString("crash_dir", m_crashDir); + + if (background) { m_sim = new OpenSimBackground(configSource); @@ -152,8 +260,14 @@ namespace OpenSim } else { + + + + m_sim = new OpenSim(configSource); + + m_sim.Startup(); while (true) From e5e52e4072f8eab2a44f79dd4a31403d5eb2d5a7 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sat, 15 May 2010 20:27:25 -0700 Subject: [PATCH 127/260] This combination is working. It all points to not being able to use 127.0.0.1/localhost in testing HG situations. The login server must have the LAN IP address, and the regions must show ExternalHostname as SYSTEMIP. Working, but this needs more testing. --- OpenSim/Services/HypergridService/UserAgentService.cs | 10 +++++----- OpenSim/Services/LLLoginService/LLLoginService.cs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 4a275c6f58..2f1fed40db 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -213,15 +213,15 @@ namespace OpenSim.Services.HypergridService public bool VerifyClient(UUID sessionID, string token) { m_log.DebugFormat("[USER AGENT SERVICE]: Verifying Client session {0} with token {1}", sessionID, token); - return true; + //return true; // Commenting this for now until I understand better what part of a sender's // info stays unchanged throughout a session - // - //if (m_TravelingAgents.ContainsKey(sessionID)) - // return m_TravelingAgents[sessionID].ClientToken == token; - //return false; + if (m_TravelingAgents.ContainsKey(sessionID)) + return m_TravelingAgents[sessionID].ClientToken == token; + + return false; } public bool VerifyAgent(UUID sessionID, string token) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 7471c7721d..9d151dfe18 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -746,6 +746,7 @@ namespace OpenSim.Services.LLLoginService m_log.Debug("[LLOGIN SERVICE] Launching agent at " + destination.RegionName); if (m_UserAgentService.LoginAgentToGrid(aCircuit, gatekeeper, destination, out reason)) { + //IPAddress addr = NetworkUtil.GetIPFor(clientIP.Address, gatekeeper.ExternalEndPoint.Address); m_UserAgentService.SetClientToken(aCircuit.SessionID, clientIP.Address.ToString()); return true; } From d4192dcb2efd0e3a3a3ed887337344ada5b9b4c6 Mon Sep 17 00:00:00 2001 From: "Teravus Ovares (Dan Olivares)" Date: Sun, 16 May 2010 01:47:53 -0400 Subject: [PATCH 128/260] * Revert last commit for now at Melanie_T's request. * Additional ways of configuring opensim break with this --- OpenSim/Region/Application/Application.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Application/Application.cs b/OpenSim/Region/Application/Application.cs index 951d66f81d..b860cf6bc0 100644 --- a/OpenSim/Region/Application/Application.cs +++ b/OpenSim/Region/Application/Application.cs @@ -123,6 +123,7 @@ namespace OpenSim // Validate that the user has the most basic configuration done // If not, offer to do the most basic configuration for them warning them along the way of the importance of // reading these files. + /* m_log.Info("Checking for reguired configuration...\n"); bool OpenSim_Ini = (File.Exists(Path.Combine(Util.configDir(), "OpenSim.ini"))) @@ -225,7 +226,7 @@ namespace OpenSim } MainConsole.Instance = null; } - + */ configSource.Alias.AddAlias("On", true); configSource.Alias.AddAlias("Off", false); configSource.Alias.AddAlias("True", true); From 8b6a295874124f70146ae79f01281f2067068a82 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 16 May 2010 16:22:38 +0300 Subject: [PATCH 129/260] Migration.cs supports single-file migration history format Scans for migration resources in either old-style "scattered" (one file per version) or new-style "integrated" format (single file "Resources/{StoreName}.migrations[.nnn]") with ":VERSION nnn" sections). In the new-style migrations it also recognizes ':GO' separators for parts of the SQL script that must be sent to the server separately. The old-style migrations are loaded each in one piece and don't support the ':GO' feature. Status: TESTED and works fine in all modes! --- OpenSim/Data/Migration.cs | 347 ++++++++++++++++++++++++++------------ 1 file changed, 238 insertions(+), 109 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 06defe4e36..7980c35ba0 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -70,61 +70,111 @@ namespace OpenSim.Data public class Migration { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private string _type; - private DbConnection _conn; - // private string _subtype; - private Assembly _assem; - private Regex _match; + protected string _type; + protected DbConnection _conn; + protected Assembly _assem; - private static readonly string _migrations_create = "create table migrations(name varchar(100), version int)"; - // private static readonly string _migrations_init = "insert into migrations values('migrations', 1)"; - // private static readonly string _migrations_find = "select version from migrations where name='migrations'"; + private Regex _match_old; + private Regex _match_new; - - public Migration(DbConnection conn, Assembly assem, string type) - { - _type = type; - _conn = conn; - _assem = assem; - _match = new Regex(@"\.(\d\d\d)_" + _type + @"\.sql"); - Initialize(); + /// Have the parameterless constructor just so we can specify it as a generic parameter with the new() constraint. + /// Currently this is only used in the tests. A Migration instance created this way must be then + /// initialized with Initialize(). Regular creation should be through the parameterized constructors. + /// + public Migration() + { } public Migration(DbConnection conn, Assembly assem, string subtype, string type) + { + Initialize(conn, assem, type, subtype); + } + + public Migration(DbConnection conn, Assembly assem, string type) + { + Initialize(conn, assem, type, ""); + } + + /// Must be called after creating with the parameterless constructor. + /// NOTE that the Migration class now doesn't access database in any way during initialization. + /// Specifically, it won't check if the [migrations] table exists. Such checks are done later: + /// automatically on Update(), or you can explicitly call InitMigrationsTable(). + /// + /// + /// + /// + /// + public void Initialize (DbConnection conn, Assembly assem, string type, string subtype) { _type = type; _conn = conn; _assem = assem; - _match = new Regex(subtype + @"\.(\d\d\d)_" + _type + @"\.sql"); - Initialize(); + _match_old = new Regex(subtype + @"\.(\d\d\d)_" + _type + @"\.sql"); + string s = String.IsNullOrEmpty(subtype) ? _type : _type + @"\." + subtype; + _match_new = new Regex(@"\." + s + @"\.migrations(?:\.(?\d+)$|.*)"); } - private void Initialize() + public void InitMigrationsTable() { - // clever, eh, we figure out which migrations version we are - int migration_version = FindVersion(_conn, "migrations"); - - if (migration_version > 0) - return; - - // If not, create the migration tables - using (DbCommand cmd = _conn.CreateCommand()) + // NOTE: normally when the [migrations] table is created, the version record for 'migrations' is + // added immediately. However, if for some reason the table is there but empty, we want to handle that as well. + int ver = FindVersion(_conn, "migrations"); + if (ver <= 0) // -1 = no table, 0 = no version record { - cmd.CommandText = _migrations_create; - cmd.ExecuteNonQuery(); + if( ver < 0 ) + ExecuteScript("create table migrations(name varchar(100), version int)"); + InsertVersion("migrations", 1); } - - InsertVersion("migrations", 1); } + /// Executes a script, possibly in a database-specific way. + /// It can be redefined for a specific DBMS, if necessary. Specifically, + /// to avoid problems with proc definitions in MySQL, we must use + /// MySqlScript class instead of just DbCommand. We don't want to bring + /// MySQL references here, so instead define a MySQLMigration class + /// in OpenSim.Data.MySQL + /// + /// + /// Array of strings, one-per-batch (often just one) + protected virtual void ExecuteScript(DbConnection conn, string[] script) + { + using (DbCommand cmd = conn.CreateCommand()) + { + cmd.CommandTimeout = 0; + foreach (string sql in script) + { + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + } + } + + protected void ExecuteScript(DbConnection conn, string sql) + { + ExecuteScript(conn, new string[]{sql}); + } + + protected void ExecuteScript(string sql) + { + ExecuteScript(_conn, sql); + } + + protected void ExecuteScript(string[] script) + { + ExecuteScript(_conn, script); + } + + + public void Update() { - int version = 0; - version = FindVersion(_conn, _type); + InitMigrationsTable(); - SortedList migrations = GetMigrationsAfter(version); + int version = FindVersion(_conn, _type); + + SortedList migrations = GetMigrationsAfter(version); if (migrations.Count < 1) return; @@ -132,57 +182,41 @@ namespace OpenSim.Data m_log.InfoFormat("[MIGRATIONS] Upgrading {0} to latest revision {1}.", _type, migrations.Keys[migrations.Count - 1]); m_log.Info("[MIGRATIONS] NOTE: this may take a while, don't interupt this process!"); - using (DbCommand cmd = _conn.CreateCommand()) + foreach (KeyValuePair kvp in migrations) { - foreach (KeyValuePair kvp in migrations) - { - int newversion = kvp.Key; - cmd.CommandText = kvp.Value; - // we need to up the command timeout to infinite as we might be doing long migrations. - cmd.CommandTimeout = 0; - try - { - cmd.ExecuteNonQuery(); - } - catch (Exception e) - { - m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", cmd.CommandText); - m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); - cmd.CommandText = "ROLLBACK;"; - cmd.ExecuteNonQuery(); - } + int newversion = kvp.Key; + // we need to up the command timeout to infinite as we might be doing long migrations. - if (version == 0) - { - InsertVersion(_type, newversion); - } - else - { - UpdateVersion(_type, newversion); - } - version = newversion; + /* [AlexRa 01-May-10]: We can't always just run any SQL in a single batch (= ExecuteNonQuery()). Things like + * stored proc definitions might have to be sent to the server each in a separate batch. + * This is certainly so for MS SQL; not sure how the MySQL connector sorts out the mess + * with 'delimiter @@'/'delimiter ;' around procs. So each "script" this code executes now is not + * a single string, but an array of strings, executed separately. + */ + try + { + ExecuteScript(kvp.Value); } + catch (Exception e) + { + m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", kvp.Value.ToString()); + m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Migration aborted.", e.Message); + ExecuteScript("ROLLBACK;"); + return; + } + + if (version == 0) + { + InsertVersion(_type, newversion); + } + else + { + UpdateVersion(_type, newversion); + } + version = newversion; } } - // private int MaxVersion() - // { - // int max = 0; - // string[] names = _assem.GetManifestResourceNames(); - - // foreach (string s in names) - // { - // Match m = _match.Match(s); - // if (m.Success) - // { - // int MigrationVersion = int.Parse(m.Groups[1].ToString()); - // if (MigrationVersion > max) - // max = MigrationVersion; - // } - // } - // return max; - // } - public int Version { get { return FindVersion(_conn, _type); } @@ -206,7 +240,7 @@ namespace OpenSim.Data try { cmd.CommandText = "select version from migrations where name='" + type + "' order by version desc"; - using (IDataReader reader = cmd.ExecuteReader()) + using (DbDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { @@ -217,7 +251,8 @@ namespace OpenSim.Data } catch { - // Something went wrong, so we're version 0 + // Something went wrong (probably no table), so we're at version -1 + version = -1; } } return version; @@ -225,57 +260,151 @@ namespace OpenSim.Data private void InsertVersion(string type, int version) { - using (DbCommand cmd = _conn.CreateCommand()) - { - cmd.CommandText = "insert into migrations(name, version) values('" + type + "', " + version + ")"; - m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); - cmd.ExecuteNonQuery(); - } + m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); + ExecuteScript("insert into migrations(name, version) values('" + type + "', " + version + ")"); } private void UpdateVersion(string type, int version) { - using (DbCommand cmd = _conn.CreateCommand()) - { - cmd.CommandText = "update migrations set version=" + version + " where name='" + type + "'"; - m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); - cmd.ExecuteNonQuery(); - } + m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); + ExecuteScript("update migrations set version=" + version + " where name='" + type + "'"); } - // private SortedList GetAllMigrations() - // { - // return GetMigrationsAfter(0); - // } + private delegate void FlushProc(); - private SortedList GetMigrationsAfter(int after) + /// Scans for migration resources in either old-style "scattered" (one file per version) + /// or new-style "integrated" format (single file with ":VERSION nnn" sections). + /// In the new-style migrations it also recognizes ':GO' separators for parts of the SQL script + /// that must be sent to the server separately. The old-style migrations are loaded each in one piece + /// and don't support the ':GO' feature. + /// + /// The version we are currently at. Scan for any higher versions + /// A list of string arrays, representing the scripts. + private SortedList GetMigrationsAfter(int after) { - string[] names = _assem.GetManifestResourceNames(); - SortedList migrations = new SortedList(); - // because life is funny if we don't - Array.Sort(names); + SortedList migrations = new SortedList(); + string[] names = _assem.GetManifestResourceNames(); + if( names.Length == 0 ) // should never happen + return migrations; + + Array.Sort(names); // we want all the migrations ordered + + int nLastVerFound = 0; + Match m = null; + string sFile = Array.FindLast(names, nm => { m = _match_new.Match(nm); return m.Success; }); // ; nm.StartsWith(sPrefix, StringComparison.InvariantCultureIgnoreCase + + if( (m != null) && !String.IsNullOrEmpty(sFile) ) + { + /* The filename should be '.migrations[.NNN]' where NNN + * is the last version number defined in the file. If the '.NNN' part is recognized, the code can skip + * the file without looking inside if we have a higher version already. Without the suffix we read + * the file anyway and use the version numbers inside. Any unrecognized suffix (such as '.sql') + * is valid but ignored. + * + * NOTE that we expect only one 'merged' migration file. If there are several, we take the last one. + * If you are numbering them, leave only the latest one in the project or at least make sure they numbered + * to come up in the correct order (e.g. 'SomeStore.migrations.001' rather than 'SomeStore.migrations.1') + */ + + if (m.Groups.Count > 1 && int.TryParse(m.Groups[1].Value, out nLastVerFound)) + { + if( nLastVerFound <= after ) + goto scan_old_style; + } + + System.Text.StringBuilder sb = new System.Text.StringBuilder(4096); + int nVersion = -1; + + List script = new List(); + + FlushProc flush = delegate() + { + if (sb.Length > 0) // last SQL stmt to script list + { + script.Add(sb.ToString()); + sb.Length = 0; + } + + if ( (nVersion > 0) && (nVersion > after) && (script.Count > 0) && !migrations.ContainsKey(nVersion)) // script to the versioned script list + { + migrations[nVersion] = script.ToArray(); + } + script.Clear(); + }; + + using (Stream resource = _assem.GetManifestResourceStream(sFile)) + using (StreamReader resourceReader = new StreamReader(resource)) + { + int nLineNo = 0; + while (!resourceReader.EndOfStream) + { + string sLine = resourceReader.ReadLine(); + nLineNo++; + + if( String.IsNullOrEmpty(sLine) || sLine.StartsWith("#") ) // ignore a comment or empty line + continue; + + if (sLine.Trim().Equals(":GO", StringComparison.InvariantCultureIgnoreCase)) + { + if (sb.Length == 0) continue; + if (nVersion > after) + script.Add(sb.ToString()); + sb.Length = 0; + continue; + } + + if (sLine.StartsWith(":VERSION ", StringComparison.InvariantCultureIgnoreCase)) // ":VERSION nnn" + { + flush(); + + int n = sLine.IndexOf('#'); // Comment is allowed in version sections, ignored + if (n >= 0) + sLine = sLine.Substring(0, n); + + if (!int.TryParse(sLine.Substring(9).Trim(), out nVersion)) + { + m_log.ErrorFormat("[MIGRATIONS]: invalid version marker at {0}: line {1}. Migration failed!", sFile, nLineNo); + break; + } + } + else + { + sb.AppendLine(sLine); + } + } + flush(); + + // If there are scattered migration files as well, only look for those with higher version numbers. + if (after < nVersion) + after = nVersion; + } + } + +scan_old_style: + // scan "old style" migration pieces anyway, ignore any versions already filled from the single file foreach (string s in names) { - Match m = _match.Match(s); + m = _match_old.Match(s); if (m.Success) { int version = int.Parse(m.Groups[1].ToString()); - if (version > after) + if ( (version > after) && !migrations.ContainsKey(version) ) { using (Stream resource = _assem.GetManifestResourceStream(s)) { using (StreamReader resourceReader = new StreamReader(resource)) { - string resourceString = resourceReader.ReadToEnd(); - migrations.Add(version, resourceString); + string sql = resourceReader.ReadToEnd(); + migrations.Add(version, new string[]{sql}); } } } } } - - if (migrations.Count < 1) { + + if (migrations.Count < 1) + { m_log.InfoFormat("[MIGRATIONS]: {0} up to date, no migrations to apply", _type); } return migrations; From a49716dc2f9a6beed410e1d5811f33acf63ed72f Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 16 May 2010 16:24:50 +0300 Subject: [PATCH 130/260] Embedded MySql.Data.dll updated to 6.2.3.0. This is necessary to correct a known problem with the DELIMITER command in previous versions of the client library. --- bin/MySql.Data.dll | Bin 294912 -> 335872 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/MySql.Data.dll b/bin/MySql.Data.dll index a94dd3def8deda655b3e0a9304dfb05e135bd398..7aa95ec345a5d23b2cb6a7da21c534328e65da90 100644 GIT binary patch literal 335872 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

    ^6&qHKa`LQKA!SalzK~7> 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 literal 294912 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

    ^abKQk1yq46uo8E;QUde6j9mXrk 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 z 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 From b49fb3db7ca665abf9753c74aa1e2b28eeb70946 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 5 May 2010 22:34:41 +0300 Subject: [PATCH 131/260] Added MySqlMigrations.cs (supports stored proc/funcs) Uses MySqlScript class to correctly run proc/func definitions that need delimiter change. Requires MySql.Data.dll 6.2 or later. --- OpenSim/Data/MySQL/MySQLMigrations.cs | 85 +++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 OpenSim/Data/MySQL/MySQLMigrations.cs diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs new file mode 100644 index 0000000000..b16655d1cb --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -0,0 +1,85 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Data.Common; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using log4net; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + ///

    This is a MySQL-customized migration processor. The only difference is in how + /// it executes SQL scripts (using MySqlScript instead of MyCommand) + /// + /// + public class MySqlMigration : Migration + { + public MySqlMigration() + : base() + { + } + + public MySqlMigration(DbConnection conn, Assembly assem, string subtype, string type) : + base(conn, assem, subtype, type) + { + } + + public MySqlMigration(DbConnection conn, Assembly assem, string type) : + base(conn, assem, type) + { + } + + protected override void ExecuteScript(DbConnection conn, string[] script) + { + if (!(conn is MySqlConnection)) + { + base.ExecuteScript(conn, script); + return; + } + + MySqlScript scr = new MySqlScript((MySqlConnection)conn); + { + foreach (string sql in script) + { + scr.Query = sql; + scr.Error += delegate(object sender, MySqlScriptErrorEventArgs args) + { + m_log.ErrorFormat("[MySQL MIGRATION]: Error {0}", args.Exception.Message); + m_log.ErrorFormat("[MySQL MIGRATION]: In SQL: {0}", args.StatementText); + throw args.Exception; + }; + scr.Execute(); + } + } + } + } +} From ee713cb253c1ef2d09de235964e02e7621649dd8 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sat, 1 May 2010 17:43:10 +0300 Subject: [PATCH 132/260] Converted MySQL migration history to the new format Replaced all NNN_StoreName.sql migration resources with a more readable, single-file-per-store --- OpenSim/Data/MSSQL/Resources/002_Presence.sql | 6 - .../Data/MySQL/Resources/001_AssetStore.sql | 15 - OpenSim/Data/MySQL/Resources/001_Avatar.sql | 5 - OpenSim/Data/MySQL/Resources/001_Friends.sql | 9 - .../Data/MySQL/Resources/001_FriendsStore.sql | 5 - .../MySQL/Resources/001_InventoryStore.sql | 40 - OpenSim/Data/MySQL/Resources/001_Presence.sql | 13 - .../Data/MySQL/Resources/001_RegionStore.sql | 154 ---- .../Data/MySQL/Resources/001_UserAccount.sql | 13 - .../Data/MySQL/Resources/002_AssetStore.sql | 9 - .../Data/MySQL/Resources/002_AuthStore.sql | 5 - OpenSim/Data/MySQL/Resources/002_Friends.sql | 5 - .../Data/MySQL/Resources/002_FriendsStore.sql | 5 - .../Data/MySQL/Resources/002_GridStore.sql | 5 - .../MySQL/Resources/002_InventoryStore.sql | 31 - .../Data/MySQL/Resources/002_RegionStore.sql | 6 - .../Data/MySQL/Resources/002_UserAccount.sql | 5 - .../Data/MySQL/Resources/002_UserStore.sql | 5 - .../Data/MySQL/Resources/003_AssetStore.sql | 9 - .../Data/MySQL/Resources/003_AuthStore.sql | 5 - .../Data/MySQL/Resources/003_GridStore.sql | 7 - .../MySQL/Resources/003_InventoryStore.sql | 5 - .../Data/MySQL/Resources/003_RegionStore.sql | 5 - .../Data/MySQL/Resources/003_UserAccount.sql | 9 - .../Data/MySQL/Resources/003_UserStore.sql | 6 - .../Data/MySQL/Resources/004_AssetStore.sql | 5 - .../Data/MySQL/Resources/004_GridStore.sql | 6 - .../MySQL/Resources/004_InventoryStore.sql | 7 - .../Data/MySQL/Resources/004_RegionStore.sql | 5 - .../Data/MySQL/Resources/004_UserAccount.sql | 8 - .../Data/MySQL/Resources/004_UserStore.sql | 6 - .../Data/MySQL/Resources/005_AssetStore.sql | 6 - .../Data/MySQL/Resources/005_GridStore.sql | 6 - .../Data/MySQL/Resources/005_RegionStore.sql | 40 - .../Data/MySQL/Resources/005_UserStore.sql | 5 - .../Data/MySQL/Resources/006_AssetStore.sql | 1 - .../Data/MySQL/Resources/006_GridStore.sql | 5 - .../Data/MySQL/Resources/006_RegionStore.sql | 12 - .../Data/MySQL/Resources/006_UserStore.sql | 5 - .../Data/MySQL/Resources/007_GridStore.sql | 7 - .../Data/MySQL/Resources/007_RegionStore.sql | 25 - .../Data/MySQL/Resources/007_UserStore.sql | 5 - .../Data/MySQL/Resources/008_RegionStore.sql | 9 - .../Data/MySQL/Resources/008_UserStore.sql | 5 - .../Data/MySQL/Resources/009_RegionStore.sql | 31 - .../Data/MySQL/Resources/010_RegionStore.sql | 9 - .../Data/MySQL/Resources/011_RegionStore.sql | 9 - .../Data/MySQL/Resources/012_RegionStore.sql | 5 - .../Data/MySQL/Resources/013_RegionStore.sql | 103 --- .../Data/MySQL/Resources/014_RegionStore.sql | 8 - .../Data/MySQL/Resources/015_RegionStore.sql | 6 - .../Data/MySQL/Resources/016_RegionStore.sql | 27 - .../Data/MySQL/Resources/017_RegionStore.sql | 9 - .../Data/MySQL/Resources/018_RegionStore.sql | 6 - .../Data/MySQL/Resources/019_RegionStore.sql | 6 - .../Data/MySQL/Resources/020_RegionStore.sql | 7 - .../Data/MySQL/Resources/021_RegionStore.sql | 8 - .../Data/MySQL/Resources/022_RegionStore.sql | 6 - .../Data/MySQL/Resources/023_RegionStore.sql | 6 - .../Data/MySQL/Resources/024_RegionStore.sql | 18 - .../Data/MySQL/Resources/025_RegionStore.sql | 46 - .../Data/MySQL/Resources/026_RegionStore.sql | 41 - .../Data/MySQL/Resources/027_RegionStore.sql | 5 - .../Data/MySQL/Resources/028_RegionStore.sql | 79 -- .../Data/MySQL/Resources/029_RegionStore.sql | 5 - .../Data/MySQL/Resources/030_RegionStore.sql | 7 - .../Data/MySQL/Resources/031_RegionStore.sql | 7 - .../Data/MySQL/Resources/032_RegionStore.sql | 3 - .../MySQL/Resources/AssetStore.migrations | 69 ++ ...001_AuthStore.sql => AuthStore.migrations} | 18 + .../Data/MySQL/Resources/Avatar.migrations | 12 + .../MySQL/Resources/FriendsStore.migrations | 25 + ...001_GridStore.sql => GridStore.migrations} | 57 ++ .../MySQL/Resources/InventoryStore.migrations | 93 ++ .../{001_LogStore.sql => LogStore.migrations} | 3 + .../Data/MySQL/Resources/Presence.migrations | 36 + .../MySQL/Resources/RegionStore.migrations | 806 ++++++++++++++++++ .../MySQL/Resources/UserAccount.migrations | 47 + ...001_UserStore.sql => UserStore.migrations} | 63 +- 79 files changed, 1228 insertions(+), 1008 deletions(-) delete mode 100644 OpenSim/Data/MSSQL/Resources/002_Presence.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_Avatar.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_Friends.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_FriendsStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_Presence.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_AuthStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_Friends.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_FriendsStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_AuthStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/007_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/007_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/007_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/008_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/008_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/009_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/010_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/011_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/012_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/013_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/014_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/015_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/016_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/017_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/018_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/019_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/020_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/021_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/022_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/023_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/024_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/025_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/026_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/027_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/028_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/029_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/030_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/031_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/032_RegionStore.sql create mode 100644 OpenSim/Data/MySQL/Resources/AssetStore.migrations rename OpenSim/Data/MySQL/Resources/{001_AuthStore.sql => AuthStore.migrations} (51%) create mode 100644 OpenSim/Data/MySQL/Resources/Avatar.migrations create mode 100644 OpenSim/Data/MySQL/Resources/FriendsStore.migrations rename OpenSim/Data/MySQL/Resources/{001_GridStore.sql => GridStore.migrations} (64%) create mode 100644 OpenSim/Data/MySQL/Resources/InventoryStore.migrations rename OpenSim/Data/MySQL/Resources/{001_LogStore.sql => LogStore.migrations} (95%) create mode 100644 OpenSim/Data/MySQL/Resources/Presence.migrations create mode 100644 OpenSim/Data/MySQL/Resources/RegionStore.migrations create mode 100644 OpenSim/Data/MySQL/Resources/UserAccount.migrations rename OpenSim/Data/MySQL/Resources/{001_UserStore.sql => UserStore.migrations} (72%) diff --git a/OpenSim/Data/MSSQL/Resources/002_Presence.sql b/OpenSim/Data/MSSQL/Resources/002_Presence.sql deleted file mode 100644 index a67671ddf6..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_Presence.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -CREATE UNIQUE INDEX SessionID ON Presence(SessionID); -CREATE INDEX UserID ON Presence(UserID); - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_AssetStore.sql b/OpenSim/Data/MySQL/Resources/001_AssetStore.sql deleted file mode 100644 index 6a9a127b8f..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_AssetStore.sql +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN; - -CREATE TABLE `assets` ( - `id` binary(16) NOT NULL, - `name` varchar(64) NOT NULL, - `description` varchar(64) NOT NULL, - `assetType` tinyint(4) NOT NULL, - `invType` tinyint(4) NOT NULL, - `local` tinyint(1) NOT NULL, - `temporary` tinyint(1) NOT NULL, - `data` longblob NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_Avatar.sql b/OpenSim/Data/MySQL/Resources/001_Avatar.sql deleted file mode 100644 index 27a307244b..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_Avatar.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE Avatars (PrincipalID CHAR(36) NOT NULL, Name VARCHAR(32) NOT NULL, Value VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY(PrincipalID, Name), KEY(PrincipalID)); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_Friends.sql b/OpenSim/Data/MySQL/Resources/001_Friends.sql deleted file mode 100644 index e158a2c802..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_Friends.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -CREATE TABLE `Friends` ( - `PrincipalID` CHAR(36) NOT NULL, - `FriendID` VARCHAR(255) NOT NULL, - `Flags` CHAR(16) NOT NULL DEFAULT '0' -) ENGINE=InnoDB; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_FriendsStore.sql b/OpenSim/Data/MySQL/Resources/001_FriendsStore.sql deleted file mode 100644 index da2c59c6d0..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_FriendsStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE `Friends` (`PrincipalID` CHAR(36) NOT NULL, `Friend` VARCHAR(255) NOT NULL, `Flags` VARCHAR(16) NOT NULL DEFAULT 0, `Offered` VARCHAR(32) NOT NULL DEFAULT 0, PRIMARY KEY(`PrincipalID`, `Friend`), KEY(`PrincipalID`)); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/001_InventoryStore.sql deleted file mode 100644 index 40dc91cdc6..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_InventoryStore.sql +++ /dev/null @@ -1,40 +0,0 @@ -BEGIN; - -CREATE TABLE `inventoryfolders` ( - `folderID` varchar(36) NOT NULL default '', - `agentID` varchar(36) default NULL, - `parentFolderID` varchar(36) default NULL, - `folderName` varchar(64) default NULL, - `type` smallint NOT NULL default 0, - `version` int NOT NULL default 0, - PRIMARY KEY (`folderID`), - KEY `owner` (`agentID`), - KEY `parent` (`parentFolderID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `inventoryitems` ( - `inventoryID` varchar(36) NOT NULL default '', - `assetID` varchar(36) default NULL, - `assetType` int(11) default NULL, - `parentFolderID` varchar(36) default NULL, - `avatarID` varchar(36) default NULL, - `inventoryName` varchar(64) default NULL, - `inventoryDescription` varchar(128) default NULL, - `inventoryNextPermissions` int(10) unsigned default NULL, - `inventoryCurrentPermissions` int(10) unsigned default NULL, - `invType` int(11) default NULL, - `creatorID` varchar(36) default NULL, - `inventoryBasePermissions` int(10) unsigned NOT NULL default 0, - `inventoryEveryOnePermissions` int(10) unsigned NOT NULL default 0, - `salePrice` int(11) NOT NULL default 0, - `saleType` tinyint(4) NOT NULL default 0, - `creationDate` int(11) NOT NULL default 0, - `groupID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - `groupOwned` tinyint(4) NOT NULL default 0, - `flags` int(11) unsigned NOT NULL default 0, - PRIMARY KEY (`inventoryID`), - KEY `owner` (`avatarID`), - KEY `folder` (`parentFolderID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_Presence.sql b/OpenSim/Data/MySQL/Resources/001_Presence.sql deleted file mode 100644 index 84fa05794c..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_Presence.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE `Presence` ( - `UserID` VARCHAR(255) NOT NULL, - `RegionID` CHAR(36) NOT NULL, - `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', - `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' -) ENGINE=InnoDB; - -CREATE UNIQUE INDEX SessionID ON Presence(SessionID); -CREATE INDEX UserID ON Presence(UserID); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_RegionStore.sql b/OpenSim/Data/MySQL/Resources/001_RegionStore.sql deleted file mode 100644 index 31164b35ed..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_RegionStore.sql +++ /dev/null @@ -1,154 +0,0 @@ -BEGIN; - -CREATE TABLE `prims` ( - `UUID` varchar(255) NOT NULL, - `RegionUUID` varchar(255) default NULL, - `ParentID` int(11) default NULL, - `CreationDate` int(11) default NULL, - `Name` varchar(255) default NULL, - `SceneGroupID` varchar(255) default NULL, - `Text` varchar(255) default NULL, - `Description` varchar(255) default NULL, - `SitName` varchar(255) default NULL, - `TouchName` varchar(255) default NULL, - `ObjectFlags` int(11) default NULL, - `CreatorID` varchar(255) default NULL, - `OwnerID` varchar(255) default NULL, - `GroupID` varchar(255) default NULL, - `LastOwnerID` varchar(255) default NULL, - `OwnerMask` int(11) default NULL, - `NextOwnerMask` int(11) default NULL, - `GroupMask` int(11) default NULL, - `EveryoneMask` int(11) default NULL, - `BaseMask` int(11) default NULL, - `PositionX` float default NULL, - `PositionY` float default NULL, - `PositionZ` float default NULL, - `GroupPositionX` float default NULL, - `GroupPositionY` float default NULL, - `GroupPositionZ` float default NULL, - `VelocityX` float default NULL, - `VelocityY` float default NULL, - `VelocityZ` float default NULL, - `AngularVelocityX` float default NULL, - `AngularVelocityY` float default NULL, - `AngularVelocityZ` float default NULL, - `AccelerationX` float default NULL, - `AccelerationY` float default NULL, - `AccelerationZ` float default NULL, - `RotationX` float default NULL, - `RotationY` float default NULL, - `RotationZ` float default NULL, - `RotationW` float default NULL, - `SitTargetOffsetX` float default NULL, - `SitTargetOffsetY` float default NULL, - `SitTargetOffsetZ` float default NULL, - `SitTargetOrientW` float default NULL, - `SitTargetOrientX` float default NULL, - `SitTargetOrientY` float default NULL, - `SitTargetOrientZ` float default NULL, - PRIMARY KEY (`UUID`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `primshapes` ( - `UUID` varchar(255) NOT NULL, - `Shape` int(11) default NULL, - `ScaleX` float default NULL, - `ScaleY` float default NULL, - `ScaleZ` float default NULL, - `PCode` int(11) default NULL, - `PathBegin` int(11) default NULL, - `PathEnd` int(11) default NULL, - `PathScaleX` int(11) default NULL, - `PathScaleY` int(11) default NULL, - `PathShearX` int(11) default NULL, - `PathShearY` int(11) default NULL, - `PathSkew` int(11) default NULL, - `PathCurve` int(11) default NULL, - `PathRadiusOffset` int(11) default NULL, - `PathRevolutions` int(11) default NULL, - `PathTaperX` int(11) default NULL, - `PathTaperY` int(11) default NULL, - `PathTwist` int(11) default NULL, - `PathTwistBegin` int(11) default NULL, - `ProfileBegin` int(11) default NULL, - `ProfileEnd` int(11) default NULL, - `ProfileCurve` int(11) default NULL, - `ProfileHollow` int(11) default NULL, - `State` int(11) default NULL, - `Texture` longblob, - `ExtraParams` longblob, - PRIMARY KEY (`UUID`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `primitems` ( - `itemID` varchar(255) NOT NULL, - `primID` varchar(255) default NULL, - `assetID` varchar(255) default NULL, - `parentFolderID` varchar(255) default NULL, - `invType` int(11) default NULL, - `assetType` int(11) default NULL, - `name` varchar(255) default NULL, - `description` varchar(255) default NULL, - `creationDate` bigint(20) default NULL, - `creatorID` varchar(255) default NULL, - `ownerID` varchar(255) default NULL, - `lastOwnerID` varchar(255) default NULL, - `groupID` varchar(255) default NULL, - `nextPermissions` int(11) default NULL, - `currentPermissions` int(11) default NULL, - `basePermissions` int(11) default NULL, - `everyonePermissions` int(11) default NULL, - `groupPermissions` int(11) default NULL, - PRIMARY KEY (`itemID`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `terrain` ( - `RegionUUID` varchar(255) default NULL, - `Revision` int(11) default NULL, - `Heightfield` longblob -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `land` ( - `UUID` varchar(255) NOT NULL, - `RegionUUID` varchar(255) default NULL, - `LocalLandID` int(11) default NULL, - `Bitmap` longblob, - `Name` varchar(255) default NULL, - `Description` varchar(255) default NULL, - `OwnerUUID` varchar(255) default NULL, - `IsGroupOwned` int(11) default NULL, - `Area` int(11) default NULL, - `AuctionID` int(11) default NULL, - `Category` int(11) default NULL, - `ClaimDate` int(11) default NULL, - `ClaimPrice` int(11) default NULL, - `GroupUUID` varchar(255) default NULL, - `SalePrice` int(11) default NULL, - `LandStatus` int(11) default NULL, - `LandFlags` int(11) default NULL, - `LandingType` int(11) default NULL, - `MediaAutoScale` int(11) default NULL, - `MediaTextureUUID` varchar(255) default NULL, - `MediaURL` varchar(255) default NULL, - `MusicURL` varchar(255) default NULL, - `PassHours` float default NULL, - `PassPrice` int(11) default NULL, - `SnapshotUUID` varchar(255) default NULL, - `UserLocationX` float default NULL, - `UserLocationY` float default NULL, - `UserLocationZ` float default NULL, - `UserLookAtX` float default NULL, - `UserLookAtY` float default NULL, - `UserLookAtZ` float default NULL, - `AuthbuyerID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - PRIMARY KEY (`UUID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `landaccesslist` ( - `LandUUID` varchar(255) default NULL, - `AccessUUID` varchar(255) default NULL, - `Flags` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_UserAccount.sql b/OpenSim/Data/MySQL/Resources/001_UserAccount.sql deleted file mode 100644 index 07da57118d..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_UserAccount.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE `UserAccounts` ( - `PrincipalID` CHAR(36) NOT NULL, - `ScopeID` CHAR(36) NOT NULL, - `FirstName` VARCHAR(64) NOT NULL, - `LastName` VARCHAR(64) NOT NULL, - `Email` VARCHAR(64), - `ServiceURLs` TEXT, - `Created` INT(11) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_AssetStore.sql b/OpenSim/Data/MySQL/Resources/002_AssetStore.sql deleted file mode 100644 index a7d7fca8c5..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_AssetStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE assets change id oldid binary(16); -ALTER TABLE assets add id varchar(36) not null default ''; -UPDATE assets set id = concat(substr(hex(oldid),1,8),"-",substr(hex(oldid),9,4),"-",substr(hex(oldid),13,4),"-",substr(hex(oldid),17,4),"-",substr(hex(oldid),21,12)); -ALTER TABLE assets drop oldid; -ALTER TABLE assets add constraint primary key(id); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/002_AuthStore.sql b/OpenSim/Data/MySQL/Resources/002_AuthStore.sql deleted file mode 100644 index dc7dfe0115..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_AuthStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_Friends.sql b/OpenSim/Data/MySQL/Resources/002_Friends.sql deleted file mode 100644 index 5ff64389f1..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_Friends.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO Friends (PrincipalID, FriendID, Flags) SELECT ownerID, friendID, friendPerms FROM userfriends; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_FriendsStore.sql b/OpenSim/Data/MySQL/Resources/002_FriendsStore.sql deleted file mode 100644 index a3638678c8..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_FriendsStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_GridStore.sql b/OpenSim/Data/MySQL/Resources/002_GridStore.sql deleted file mode 100644 index 35b9be122e..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_GridStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE regions add column access integer unsigned default 1; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/002_InventoryStore.sql deleted file mode 100644 index c161a687e2..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_InventoryStore.sql +++ /dev/null @@ -1,31 +0,0 @@ -BEGIN; - -ALTER TABLE inventoryfolders change folderID folderIDold varchar(36); -ALTER TABLE inventoryfolders change agentID agentIDold varchar(36); -ALTER TABLE inventoryfolders change parentFolderID parentFolderIDold varchar(36); -ALTER TABLE inventoryfolders add folderID char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE inventoryfolders add agentID char(36) default NULL; -ALTER TABLE inventoryfolders add parentFolderID char(36) default NULL; -UPDATE inventoryfolders set folderID = folderIDold, agentID = agentIDold, parentFolderID = parentFolderIDold; -ALTER TABLE inventoryfolders drop folderIDold; -ALTER TABLE inventoryfolders drop agentIDold; -ALTER TABLE inventoryfolders drop parentFolderIDold; -ALTER TABLE inventoryfolders add constraint primary key(folderID); -ALTER TABLE inventoryfolders add index inventoryfolders_agentid(agentID); -ALTER TABLE inventoryfolders add index inventoryfolders_parentFolderid(parentFolderID); - -ALTER TABLE inventoryitems change inventoryID inventoryIDold varchar(36); -ALTER TABLE inventoryitems change avatarID avatarIDold varchar(36); -ALTER TABLE inventoryitems change parentFolderID parentFolderIDold varchar(36); -ALTER TABLE inventoryitems add inventoryID char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE inventoryitems add avatarID char(36) default NULL; -ALTER TABLE inventoryitems add parentFolderID char(36) default NULL; -UPDATE inventoryitems set inventoryID = inventoryIDold, avatarID = avatarIDold, parentFolderID = parentFolderIDold; -ALTER TABLE inventoryitems drop inventoryIDold; -ALTER TABLE inventoryitems drop avatarIDold; -ALTER TABLE inventoryitems drop parentFolderIDold; -ALTER TABLE inventoryitems add constraint primary key(inventoryID); -ALTER TABLE inventoryitems add index inventoryitems_avatarid(avatarID); -ALTER TABLE inventoryitems add index inventoryitems_parentFolderid(parentFolderID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/002_RegionStore.sql b/OpenSim/Data/MySQL/Resources/002_RegionStore.sql deleted file mode 100644 index 45bf959d40..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -CREATE index prims_regionuuid on prims(RegionUUID); -CREATE index primitems_primid on primitems(primID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/002_UserAccount.sql b/OpenSim/Data/MySQL/Resources/002_UserAccount.sql deleted file mode 100644 index ad2ddda239..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_UserAccount.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, lastname AS LastName, email as Email, CONCAT('AssetServerURI=', userAssetURI, ' InventoryServerURI=', userInventoryURI, ' GatewayURI= HomeURI=') AS ServiceURLs, created as Created FROM users; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_UserStore.sql b/OpenSim/Data/MySQL/Resources/002_UserStore.sql deleted file mode 100644 index 393cea0f12..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add homeRegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_AssetStore.sql b/OpenSim/Data/MySQL/Resources/003_AssetStore.sql deleted file mode 100644 index d489278f13..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_AssetStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE assets change id oldid varchar(36); -ALTER TABLE assets add id char(36) not null default '00000000-0000-0000-0000-000000000000'; -UPDATE assets set id = oldid; -ALTER TABLE assets drop oldid; -ALTER TABLE assets add constraint primary key(id); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/003_AuthStore.sql b/OpenSim/Data/MySQL/Resources/003_AuthStore.sql deleted file mode 100644 index af9ffe6f1d..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_AuthStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE `auth` ADD COLUMN `accountType` VARCHAR(32) NOT NULL DEFAULT 'UserAccount'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_GridStore.sql b/OpenSim/Data/MySQL/Resources/003_GridStore.sql deleted file mode 100644 index bc3fe7df7a..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_GridStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE regions add column ScopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; - -create index ScopeID on regions(ScopeID); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/003_InventoryStore.sql deleted file mode 100644 index 4c6da91aab..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_InventoryStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_RegionStore.sql b/OpenSim/Data/MySQL/Resources/003_RegionStore.sql deleted file mode 100644 index cb0a6141cc..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - - CREATE TABLE regionban (regionUUID VARCHAR(36) NOT NULL, bannedUUID VARCHAR(36) NOT NULL, bannedIp VARCHAR(16) NOT NULL, bannedIpHostMask VARCHAR(16) NOT NULL) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/003_UserAccount.sql b/OpenSim/Data/MySQL/Resources/003_UserAccount.sql deleted file mode 100644 index e42d93b92c..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_UserAccount.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); -CREATE INDEX Email ON UserAccounts(Email); -CREATE INDEX FirstName ON UserAccounts(FirstName); -CREATE INDEX LastName ON UserAccounts(LastName); -CREATE INDEX Name ON UserAccounts(FirstName,LastName); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_UserStore.sql b/OpenSim/Data/MySQL/Resources/003_UserStore.sql deleted file mode 100644 index 6f890eeec1..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add userFlags integer NOT NULL default 0; -ALTER TABLE users add godLevel integer NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_AssetStore.sql b/OpenSim/Data/MySQL/Resources/004_AssetStore.sql deleted file mode 100644 index ae1951df39..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_AssetStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE assets drop InvType; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_GridStore.sql b/OpenSim/Data/MySQL/Resources/004_GridStore.sql deleted file mode 100644 index 2238a888ea..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_GridStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE regions add column sizeX integer not null default 0; -ALTER TABLE regions add column sizeY integer not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/004_InventoryStore.sql deleted file mode 100644 index c45f773904..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_InventoryStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID is NULL; -update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID = ''; -alter table inventoryitems modify column creatorID varchar(36) not NULL default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_RegionStore.sql b/OpenSim/Data/MySQL/Resources/004_RegionStore.sql deleted file mode 100644 index 4db2f7587d..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE primitems add flags integer not null default 0; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/004_UserAccount.sql b/OpenSim/Data/MySQL/Resources/004_UserAccount.sql deleted file mode 100644 index 8abcd53a16..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_UserAccount.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -ALTER TABLE UserAccounts ADD COLUMN UserLevel integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD COLUMN UserFlags integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD COLUMN UserTitle varchar(64) NOT NULL DEFAULT ''; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/004_UserStore.sql b/OpenSim/Data/MySQL/Resources/004_UserStore.sql deleted file mode 100644 index 03142afa37..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add customType varchar(32) not null default ''; -ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_AssetStore.sql b/OpenSim/Data/MySQL/Resources/005_AssetStore.sql deleted file mode 100644 index bfeb6525af..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_AssetStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE assets add create_time integer default 0; -ALTER TABLE assets add access_time integer default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_GridStore.sql b/OpenSim/Data/MySQL/Resources/005_GridStore.sql deleted file mode 100644 index 835ba89369..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_GridStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE `regions` ADD COLUMN `flags` integer NOT NULL DEFAULT 0; -CREATE INDEX flags ON regions(flags); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_RegionStore.sql b/OpenSim/Data/MySQL/Resources/005_RegionStore.sql deleted file mode 100644 index c4a9527403..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_RegionStore.sql +++ /dev/null @@ -1,40 +0,0 @@ -BEGIN; - -create table regionsettings ( - regionUUID char(36) not null, - block_terraform integer not null, - block_fly integer not null, - allow_damage integer not null, - restrict_pushing integer not null, - allow_land_resell integer not null, - allow_land_join_divide integer not null, - block_show_in_search integer not null, - agent_limit integer not null, - object_bonus float not null, - maturity integer not null, - disable_scripts integer not null, - disable_collisions integer not null, - disable_physics integer not null, - terrain_texture_1 char(36) not null, - terrain_texture_2 char(36) not null, - terrain_texture_3 char(36) not null, - terrain_texture_4 char(36) not null, - elevation_1_nw float not null, - elevation_2_nw float not null, - elevation_1_ne float not null, - elevation_2_ne float not null, - elevation_1_se float not null, - elevation_2_se float not null, - elevation_1_sw float not null, - elevation_2_sw float not null, - water_height float not null, - terrain_raise_limit float not null, - terrain_lower_limit float not null, - use_estate_sun integer not null, - fixed_sun integer not null, - sun_position float not null, - covenant char(36), - primary key(regionUUID) -); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_UserStore.sql b/OpenSim/Data/MySQL/Resources/005_UserStore.sql deleted file mode 100644 index 55896bc9a0..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL, `attachpoint` int(11) NOT NULL, `item` char(36) NOT NULL, `asset` char(36) NOT NULL) ENGINE=InnoDB; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/006_AssetStore.sql b/OpenSim/Data/MySQL/Resources/006_AssetStore.sql deleted file mode 100644 index 31043537a3..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_AssetStore.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621' diff --git a/OpenSim/Data/MySQL/Resources/006_GridStore.sql b/OpenSim/Data/MySQL/Resources/006_GridStore.sql deleted file mode 100644 index 91322d6431..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_GridStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE `regions` ADD COLUMN `last_seen` integer NOT NULL DEFAULT 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/006_RegionStore.sql b/OpenSim/Data/MySQL/Resources/006_RegionStore.sql deleted file mode 100644 index c1ba5b808d..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_RegionStore.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN; - -alter table landaccesslist ENGINE = InnoDB; -alter table migrations ENGINE = InnoDB; -alter table primitems ENGINE = InnoDB; -alter table prims ENGINE = InnoDB; -alter table primshapes ENGINE = InnoDB; -alter table regionsettings ENGINE = InnoDB; -alter table terrain ENGINE = InnoDB; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/006_UserStore.sql b/OpenSim/Data/MySQL/Resources/006_UserStore.sql deleted file mode 100644 index 10b321e601..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE agents add currentLookAt varchar(36) not null default ''; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/007_GridStore.sql b/OpenSim/Data/MySQL/Resources/007_GridStore.sql deleted file mode 100644 index dbec58432e..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_GridStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE `regions` ADD COLUMN `PrincipalID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; -ALTER TABLE `regions` ADD COLUMN `Token` varchar(255) NOT NULL; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/007_RegionStore.sql b/OpenSim/Data/MySQL/Resources/007_RegionStore.sql deleted file mode 100644 index 404d248e6b..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_RegionStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN; - -ALTER TABLE prims change UUID UUIDold varchar(255); -ALTER TABLE prims change RegionUUID RegionUUIDold varchar(255); -ALTER TABLE prims change CreatorID CreatorIDold varchar(255); -ALTER TABLE prims change OwnerID OwnerIDold varchar(255); -ALTER TABLE prims change GroupID GroupIDold varchar(255); -ALTER TABLE prims change LastOwnerID LastOwnerIDold varchar(255); -ALTER TABLE prims add UUID char(36); -ALTER TABLE prims add RegionUUID char(36); -ALTER TABLE prims add CreatorID char(36); -ALTER TABLE prims add OwnerID char(36); -ALTER TABLE prims add GroupID char(36); -ALTER TABLE prims add LastOwnerID char(36); -UPDATE prims set UUID = UUIDold, RegionUUID = RegionUUIDold, CreatorID = CreatorIDold, OwnerID = OwnerIDold, GroupID = GroupIDold, LastOwnerID = LastOwnerIDold; -ALTER TABLE prims drop UUIDold; -ALTER TABLE prims drop RegionUUIDold; -ALTER TABLE prims drop CreatorIDold; -ALTER TABLE prims drop OwnerIDold; -ALTER TABLE prims drop GroupIDold; -ALTER TABLE prims drop LastOwnerIDold; -ALTER TABLE prims add constraint primary key(UUID); -ALTER TABLE prims add index prims_regionuuid(RegionUUID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/007_UserStore.sql b/OpenSim/Data/MySQL/Resources/007_UserStore.sql deleted file mode 100644 index 3ab5261373..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add email varchar(250); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/008_RegionStore.sql b/OpenSim/Data/MySQL/Resources/008_RegionStore.sql deleted file mode 100644 index 7bc61c04e4..0000000000 --- a/OpenSim/Data/MySQL/Resources/008_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE primshapes change UUID UUIDold varchar(255); -ALTER TABLE primshapes add UUID char(36); -UPDATE primshapes set UUID = UUIDold; -ALTER TABLE primshapes drop UUIDold; -ALTER TABLE primshapes add constraint primary key(UUID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/008_UserStore.sql b/OpenSim/Data/MySQL/Resources/008_UserStore.sql deleted file mode 100644 index 4500bd5d7b..0000000000 --- a/OpenSim/Data/MySQL/Resources/008_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add scopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/009_RegionStore.sql b/OpenSim/Data/MySQL/Resources/009_RegionStore.sql deleted file mode 100644 index 284732aa6f..0000000000 --- a/OpenSim/Data/MySQL/Resources/009_RegionStore.sql +++ /dev/null @@ -1,31 +0,0 @@ -BEGIN; - -ALTER TABLE primitems change itemID itemIDold varchar(255); -ALTER TABLE primitems change primID primIDold varchar(255); -ALTER TABLE primitems change assetID assetIDold varchar(255); -ALTER TABLE primitems change parentFolderID parentFolderIDold varchar(255); -ALTER TABLE primitems change creatorID creatorIDold varchar(255); -ALTER TABLE primitems change ownerID ownerIDold varchar(255); -ALTER TABLE primitems change groupID groupIDold varchar(255); -ALTER TABLE primitems change lastOwnerID lastOwnerIDold varchar(255); -ALTER TABLE primitems add itemID char(36); -ALTER TABLE primitems add primID char(36); -ALTER TABLE primitems add assetID char(36); -ALTER TABLE primitems add parentFolderID char(36); -ALTER TABLE primitems add creatorID char(36); -ALTER TABLE primitems add ownerID char(36); -ALTER TABLE primitems add groupID char(36); -ALTER TABLE primitems add lastOwnerID char(36); -UPDATE primitems set itemID = itemIDold, primID = primIDold, assetID = assetIDold, parentFolderID = parentFolderIDold, creatorID = creatorIDold, ownerID = ownerIDold, groupID = groupIDold, lastOwnerID = lastOwnerIDold; -ALTER TABLE primitems drop itemIDold; -ALTER TABLE primitems drop primIDold; -ALTER TABLE primitems drop assetIDold; -ALTER TABLE primitems drop parentFolderIDold; -ALTER TABLE primitems drop creatorIDold; -ALTER TABLE primitems drop ownerIDold; -ALTER TABLE primitems drop groupIDold; -ALTER TABLE primitems drop lastOwnerIDold; -ALTER TABLE primitems add constraint primary key(itemID); -ALTER TABLE primitems add index primitems_primid(primID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/010_RegionStore.sql b/OpenSim/Data/MySQL/Resources/010_RegionStore.sql deleted file mode 100644 index 031a746aeb..0000000000 --- a/OpenSim/Data/MySQL/Resources/010_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -# 1 "010_RegionStore.sql" -# 1 "" -# 1 "" -# 1 "010_RegionStore.sql" -BEGIN; - -DELETE FROM regionsettings; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/011_RegionStore.sql b/OpenSim/Data/MySQL/Resources/011_RegionStore.sql deleted file mode 100644 index ab0196934a..0000000000 --- a/OpenSim/Data/MySQL/Resources/011_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE prims change SceneGroupID SceneGroupIDold varchar(255); -ALTER TABLE prims add SceneGroupID char(36); -UPDATE prims set SceneGroupID = SceneGroupIDold; -ALTER TABLE prims drop SceneGroupIDold; -ALTER TABLE prims add index prims_scenegroupid(SceneGroupID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/012_RegionStore.sql b/OpenSim/Data/MySQL/Resources/012_RegionStore.sql deleted file mode 100644 index 95c27573de..0000000000 --- a/OpenSim/Data/MySQL/Resources/012_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims add index prims_parentid(ParentID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/013_RegionStore.sql b/OpenSim/Data/MySQL/Resources/013_RegionStore.sql deleted file mode 100644 index a6bd30da50..0000000000 --- a/OpenSim/Data/MySQL/Resources/013_RegionStore.sql +++ /dev/null @@ -1,103 +0,0 @@ -begin; - -drop table regionsettings; - -CREATE TABLE `regionsettings` ( - `regionUUID` char(36) NOT NULL, - `block_terraform` int(11) NOT NULL, - `block_fly` int(11) NOT NULL, - `allow_damage` int(11) NOT NULL, - `restrict_pushing` int(11) NOT NULL, - `allow_land_resell` int(11) NOT NULL, - `allow_land_join_divide` int(11) NOT NULL, - `block_show_in_search` int(11) NOT NULL, - `agent_limit` int(11) NOT NULL, - `object_bonus` float NOT NULL, - `maturity` int(11) NOT NULL, - `disable_scripts` int(11) NOT NULL, - `disable_collisions` int(11) NOT NULL, - `disable_physics` int(11) NOT NULL, - `terrain_texture_1` char(36) NOT NULL, - `terrain_texture_2` char(36) NOT NULL, - `terrain_texture_3` char(36) NOT NULL, - `terrain_texture_4` char(36) NOT NULL, - `elevation_1_nw` float NOT NULL, - `elevation_2_nw` float NOT NULL, - `elevation_1_ne` float NOT NULL, - `elevation_2_ne` float NOT NULL, - `elevation_1_se` float NOT NULL, - `elevation_2_se` float NOT NULL, - `elevation_1_sw` float NOT NULL, - `elevation_2_sw` float NOT NULL, - `water_height` float NOT NULL, - `terrain_raise_limit` float NOT NULL, - `terrain_lower_limit` float NOT NULL, - `use_estate_sun` int(11) NOT NULL, - `fixed_sun` int(11) NOT NULL, - `sun_position` float NOT NULL, - `covenant` char(36) default NULL, - `Sandbox` tinyint(4) NOT NULL, - PRIMARY KEY (`regionUUID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_managers` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_groups` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_users` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estateban` ( - `EstateID` int(10) unsigned NOT NULL, - `bannedUUID` varchar(36) NOT NULL, - `bannedIp` varchar(16) NOT NULL, - `bannedIpHostMask` varchar(16) NOT NULL, - `bannedNameMask` varchar(64) default NULL, - KEY `estateban_EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_settings` ( - `EstateID` int(10) unsigned NOT NULL auto_increment, - `EstateName` varchar(64) default NULL, - `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, - `DenyAnonymous` tinyint(4) NOT NULL, - `ResetHomeOnTeleport` tinyint(4) NOT NULL, - `FixedSun` tinyint(4) NOT NULL, - `DenyTransacted` tinyint(4) NOT NULL, - `BlockDwell` tinyint(4) NOT NULL, - `DenyIdentified` tinyint(4) NOT NULL, - `AllowVoice` tinyint(4) NOT NULL, - `UseGlobalTime` tinyint(4) NOT NULL, - `PricePerMeter` int(11) NOT NULL, - `TaxFree` tinyint(4) NOT NULL, - `AllowDirectTeleport` tinyint(4) NOT NULL, - `RedirectGridX` int(11) NOT NULL, - `RedirectGridY` int(11) NOT NULL, - `ParentEstateID` int(10) unsigned NOT NULL, - `SunPosition` double NOT NULL, - `EstateSkipScripts` tinyint(4) NOT NULL, - `BillableFactor` float NOT NULL, - `PublicAccess` tinyint(4) NOT NULL, - PRIMARY KEY (`EstateID`) -) ENGINE=InnoDB AUTO_INCREMENT=100; - -CREATE TABLE `estate_map` ( - `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - `EstateID` int(11) NOT NULL, - PRIMARY KEY (`RegionID`), - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/014_RegionStore.sql b/OpenSim/Data/MySQL/Resources/014_RegionStore.sql deleted file mode 100644 index 788fd63c42..0000000000 --- a/OpenSim/Data/MySQL/Resources/014_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -alter table estate_settings add column AbuseEmail varchar(255) not null; - -alter table estate_settings add column EstateOwner varchar(36) not null; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/015_RegionStore.sql b/OpenSim/Data/MySQL/Resources/015_RegionStore.sql deleted file mode 100644 index 6d4f9f332f..0000000000 --- a/OpenSim/Data/MySQL/Resources/015_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -alter table estate_settings add column DenyMinors tinyint not null; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/016_RegionStore.sql b/OpenSim/Data/MySQL/Resources/016_RegionStore.sql deleted file mode 100644 index 9bc265e5ff..0000000000 --- a/OpenSim/Data/MySQL/Resources/016_RegionStore.sql +++ /dev/null @@ -1,27 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN PayPrice integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton1 integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton2 integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton3 integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton4 integer not null default 0; -ALTER TABLE prims ADD COLUMN LoopedSound char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN LoopedSoundGain float not null default 0.0; -ALTER TABLE prims ADD COLUMN TextureAnimation blob; -ALTER TABLE prims ADD COLUMN OmegaX float not null default 0.0; -ALTER TABLE prims ADD COLUMN OmegaY float not null default 0.0; -ALTER TABLE prims ADD COLUMN OmegaZ float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetX float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetY float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float not null default 0.0; -ALTER TABLE prims ADD COLUMN ForceMouselook tinyint not null default 0; -ALTER TABLE prims ADD COLUMN ScriptAccessPin integer not null default 0; -ALTER TABLE prims ADD COLUMN AllowedDrop tinyint not null default 0; -ALTER TABLE prims ADD COLUMN DieAtEdge tinyint not null default 0; -ALTER TABLE prims ADD COLUMN SalePrice integer not null default 10; -ALTER TABLE prims ADD COLUMN SaleType tinyint not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/017_RegionStore.sql b/OpenSim/Data/MySQL/Resources/017_RegionStore.sql deleted file mode 100644 index 0304f30b72..0000000000 --- a/OpenSim/Data/MySQL/Resources/017_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; -ALTER TABLE prims ADD COLUMN ParticleSystem blob; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/018_RegionStore.sql b/OpenSim/Data/MySQL/Resources/018_RegionStore.sql deleted file mode 100644 index 68c489c7bf..0000000000 --- a/OpenSim/Data/MySQL/Resources/018_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -ALTER TABLE prims ADD COLUMN ClickAction tinyint NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/019_RegionStore.sql b/OpenSim/Data/MySQL/Resources/019_RegionStore.sql deleted file mode 100644 index 4c14f2a5f4..0000000000 --- a/OpenSim/Data/MySQL/Resources/019_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -ALTER TABLE prims ADD COLUMN Material tinyint NOT NULL default 3; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/020_RegionStore.sql b/OpenSim/Data/MySQL/Resources/020_RegionStore.sql deleted file mode 100644 index 814ef48bb6..0000000000 --- a/OpenSim/Data/MySQL/Resources/020_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -begin; - -ALTER TABLE land ADD COLUMN OtherCleanTime integer NOT NULL default 0; -ALTER TABLE land ADD COLUMN Dwell integer NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/021_RegionStore.sql b/OpenSim/Data/MySQL/Resources/021_RegionStore.sql deleted file mode 100644 index c59b27e745..0000000000 --- a/OpenSim/Data/MySQL/Resources/021_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/022_RegionStore.sql b/OpenSim/Data/MySQL/Resources/022_RegionStore.sql deleted file mode 100644 index df0bb7dac9..0000000000 --- a/OpenSim/Data/MySQL/Resources/022_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN CollisionSoundVolume float not null default 0.0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/023_RegionStore.sql b/OpenSim/Data/MySQL/Resources/023_RegionStore.sql deleted file mode 100644 index 559591fba5..0000000000 --- a/OpenSim/Data/MySQL/Resources/023_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN LinkNumber integer not null default 0; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/024_RegionStore.sql b/OpenSim/Data/MySQL/Resources/024_RegionStore.sql deleted file mode 100644 index defbf5cbb1..0000000000 --- a/OpenSim/Data/MySQL/Resources/024_RegionStore.sql +++ /dev/null @@ -1,18 +0,0 @@ -BEGIN; - -alter table regionsettings change column `object_bonus` `object_bonus` double NOT NULL; -alter table regionsettings change column `elevation_1_nw` `elevation_1_nw` double NOT NULL; -alter table regionsettings change column `elevation_2_nw` `elevation_2_nw` double NOT NULL; -alter table regionsettings change column `elevation_1_ne` `elevation_1_ne` double NOT NULL; -alter table regionsettings change column `elevation_2_ne` `elevation_2_ne` double NOT NULL; -alter table regionsettings change column `elevation_1_se` `elevation_1_se` double NOT NULL; -alter table regionsettings change column `elevation_2_se` `elevation_2_se` double NOT NULL; -alter table regionsettings change column `elevation_1_sw` `elevation_1_sw` double NOT NULL; -alter table regionsettings change column `elevation_2_sw` `elevation_2_sw` double NOT NULL; -alter table regionsettings change column `water_height` `water_height` double NOT NULL; -alter table regionsettings change column `terrain_raise_limit` `terrain_raise_limit` double NOT NULL; -alter table regionsettings change column `terrain_lower_limit` `terrain_lower_limit` double NOT NULL; -alter table regionsettings change column `sun_position` `sun_position` double NOT NULL; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/025_RegionStore.sql b/OpenSim/Data/MySQL/Resources/025_RegionStore.sql deleted file mode 100644 index e8f5d70e47..0000000000 --- a/OpenSim/Data/MySQL/Resources/025_RegionStore.sql +++ /dev/null @@ -1,46 +0,0 @@ -BEGIN; - -alter table prims change column `PositionX` `PositionX` double default NULL; -alter table prims change column `PositionY` `PositionY` double default NULL; -alter table prims change column `PositionZ` `PositionZ` double default NULL; -alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; -alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; -alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; -alter table prims change column `VelocityX` `VelocityX` double default NULL; -alter table prims change column `VelocityY` `VelocityY` double default NULL; -alter table prims change column `VelocityZ` `VelocityZ` double default NULL; -alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; -alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; -alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; -alter table prims change column `AccelerationX` `AccelerationX` double default NULL; -alter table prims change column `AccelerationY` `AccelerationY` double default NULL; -alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; -alter table prims change column `RotationX` `RotationX` double default NULL; -alter table prims change column `RotationY` `RotationY` double default NULL; -alter table prims change column `RotationZ` `RotationZ` double default NULL; -alter table prims change column `RotationW` `RotationW` double default NULL; -alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; -alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; -alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; -alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; -alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; -alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; -alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; -alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; -alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; -alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; -alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; -alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; - -alter table primshapes change column `ScaleX` `ScaleX` double NOT NULL default '0'; -alter table primshapes change column `ScaleY` `ScaleY` double NOT NULL default '0'; -alter table primshapes change column `ScaleZ` `ScaleZ` double NOT NULL default '0'; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/026_RegionStore.sql b/OpenSim/Data/MySQL/Resources/026_RegionStore.sql deleted file mode 100644 index 91af8a84c8..0000000000 --- a/OpenSim/Data/MySQL/Resources/026_RegionStore.sql +++ /dev/null @@ -1,41 +0,0 @@ -begin; - -alter table prims change column `PositionX` `PositionX` double default NULL; -alter table prims change column `PositionY` `PositionY` double default NULL; -alter table prims change column `PositionZ` `PositionZ` double default NULL; -alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; -alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; -alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; -alter table prims change column `VelocityX` `VelocityX` double default NULL; -alter table prims change column `VelocityY` `VelocityY` double default NULL; -alter table prims change column `VelocityZ` `VelocityZ` double default NULL; -alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; -alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; -alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; -alter table prims change column `AccelerationX` `AccelerationX` double default NULL; -alter table prims change column `AccelerationY` `AccelerationY` double default NULL; -alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; -alter table prims change column `RotationX` `RotationX` double default NULL; -alter table prims change column `RotationY` `RotationY` double default NULL; -alter table prims change column `RotationZ` `RotationZ` double default NULL; -alter table prims change column `RotationW` `RotationW` double default NULL; -alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; -alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; -alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; -alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; -alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; -alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; -alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; -alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; -alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; -alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; -alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; -alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; - -commit; diff --git a/OpenSim/Data/MySQL/Resources/027_RegionStore.sql b/OpenSim/Data/MySQL/Resources/027_RegionStore.sql deleted file mode 100644 index e1efab31c1..0000000000 --- a/OpenSim/Data/MySQL/Resources/027_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims DROP COLUMN ParentID; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/028_RegionStore.sql b/OpenSim/Data/MySQL/Resources/028_RegionStore.sql deleted file mode 100644 index 078394f80d..0000000000 --- a/OpenSim/Data/MySQL/Resources/028_RegionStore.sql +++ /dev/null @@ -1,79 +0,0 @@ -BEGIN; - -update terrain - set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) - where RegionUUID not like '%-%'; - - -update landaccesslist - set LandUUID = concat(substr(LandUUID, 1, 8), "-", substr(LandUUID, 9, 4), "-", substr(LandUUID, 13, 4), "-", substr(LandUUID, 17, 4), "-", substr(LandUUID, 21, 12)) - where LandUUID not like '%-%'; - -update landaccesslist - set AccessUUID = concat(substr(AccessUUID, 1, 8), "-", substr(AccessUUID, 9, 4), "-", substr(AccessUUID, 13, 4), "-", substr(AccessUUID, 17, 4), "-", substr(AccessUUID, 21, 12)) - where AccessUUID not like '%-%'; - - -update prims - set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) - where UUID not like '%-%'; - -update prims - set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) - where RegionUUID not like '%-%'; - -update prims - set SceneGroupID = concat(substr(SceneGroupID, 1, 8), "-", substr(SceneGroupID, 9, 4), "-", substr(SceneGroupID, 13, 4), "-", substr(SceneGroupID, 17, 4), "-", substr(SceneGroupID, 21, 12)) - where SceneGroupID not like '%-%'; - -update prims - set CreatorID = concat(substr(CreatorID, 1, 8), "-", substr(CreatorID, 9, 4), "-", substr(CreatorID, 13, 4), "-", substr(CreatorID, 17, 4), "-", substr(CreatorID, 21, 12)) - where CreatorID not like '%-%'; - -update prims - set OwnerID = concat(substr(OwnerID, 1, 8), "-", substr(OwnerID, 9, 4), "-", substr(OwnerID, 13, 4), "-", substr(OwnerID, 17, 4), "-", substr(OwnerID, 21, 12)) - where OwnerID not like '%-%'; - -update prims - set GroupID = concat(substr(GroupID, 1, 8), "-", substr(GroupID, 9, 4), "-", substr(GroupID, 13, 4), "-", substr(GroupID, 17, 4), "-", substr(GroupID, 21, 12)) - where GroupID not like '%-%'; - -update prims - set LastOwnerID = concat(substr(LastOwnerID, 1, 8), "-", substr(LastOwnerID, 9, 4), "-", substr(LastOwnerID, 13, 4), "-", substr(LastOwnerID, 17, 4), "-", substr(LastOwnerID, 21, 12)) - where LastOwnerID not like '%-%'; - - -update primshapes - set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) - where UUID not like '%-%'; - - -update land - set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) - where UUID not like '%-%'; - -update land - set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) - where RegionUUID not like '%-%'; - -update land - set OwnerUUID = concat(substr(OwnerUUID, 1, 8), "-", substr(OwnerUUID, 9, 4), "-", substr(OwnerUUID, 13, 4), "-", substr(OwnerUUID, 17, 4), "-", substr(OwnerUUID, 21, 12)) - where OwnerUUID not like '%-%'; - -update land - set GroupUUID = concat(substr(GroupUUID, 1, 8), "-", substr(GroupUUID, 9, 4), "-", substr(GroupUUID, 13, 4), "-", substr(GroupUUID, 17, 4), "-", substr(GroupUUID, 21, 12)) - where GroupUUID not like '%-%'; - -update land - set MediaTextureUUID = concat(substr(MediaTextureUUID, 1, 8), "-", substr(MediaTextureUUID, 9, 4), "-", substr(MediaTextureUUID, 13, 4), "-", substr(MediaTextureUUID, 17, 4), "-", substr(MediaTextureUUID, 21, 12)) - where MediaTextureUUID not like '%-%'; - -update land - set SnapshotUUID = concat(substr(SnapshotUUID, 1, 8), "-", substr(SnapshotUUID, 9, 4), "-", substr(SnapshotUUID, 13, 4), "-", substr(SnapshotUUID, 17, 4), "-", substr(SnapshotUUID, 21, 12)) - where SnapshotUUID not like '%-%'; - -update land - set AuthbuyerID = concat(substr(AuthbuyerID, 1, 8), "-", substr(AuthbuyerID, 9, 4), "-", substr(AuthbuyerID, 13, 4), "-", substr(AuthbuyerID, 17, 4), "-", substr(AuthbuyerID, 21, 12)) - where AuthbuyerID not like '%-%'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/029_RegionStore.sql b/OpenSim/Data/MySQL/Resources/029_RegionStore.sql deleted file mode 100644 index b5962a2e10..0000000000 --- a/OpenSim/Data/MySQL/Resources/029_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN PassTouches tinyint not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/030_RegionStore.sql b/OpenSim/Data/MySQL/Resources/030_RegionStore.sql deleted file mode 100644 index dfdcf6d710..0000000000 --- a/OpenSim/Data/MySQL/Resources/030_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE regionsettings ADD COLUMN loaded_creation_date varchar(20) default NULL; -ALTER TABLE regionsettings ADD COLUMN loaded_creation_time varchar(20) default NULL; -ALTER TABLE regionsettings ADD COLUMN loaded_creation_id varchar(64) default NULL; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/031_RegionStore.sql b/OpenSim/Data/MySQL/Resources/031_RegionStore.sql deleted file mode 100644 index d069296039..0000000000 --- a/OpenSim/Data/MySQL/Resources/031_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE regionsettings DROP COLUMN loaded_creation_date; -ALTER TABLE regionsettings DROP COLUMN loaded_creation_time; -ALTER TABLE regionsettings ADD COLUMN loaded_creation_datetime int unsigned NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/032_RegionStore.sql b/OpenSim/Data/MySQL/Resources/032_RegionStore.sql deleted file mode 100644 index dca5de7144..0000000000 --- a/OpenSim/Data/MySQL/Resources/032_RegionStore.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -ALTER TABLE estate_settings AUTO_INCREMENT = 100; -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/AssetStore.migrations b/OpenSim/Data/MySQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..b9595f0f47 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/AssetStore.migrations @@ -0,0 +1,69 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE `assets` ( + `id` binary(16) NOT NULL, + `name` varchar(64) NOT NULL, + `description` varchar(64) NOT NULL, + `assetType` tinyint(4) NOT NULL, + `invType` tinyint(4) NOT NULL, + `local` tinyint(1) NOT NULL, + `temporary` tinyint(1) NOT NULL, + `data` longblob NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; + +COMMIT; + +# ----------------- +:VERSION 2 + +BEGIN; + +ALTER TABLE assets change id oldid binary(16); +ALTER TABLE assets add id varchar(36) not null default ''; +UPDATE assets set id = concat(substr(hex(oldid),1,8),"-",substr(hex(oldid),9,4),"-",substr(hex(oldid),13,4),"-",substr(hex(oldid),17,4),"-",substr(hex(oldid),21,12)); +ALTER TABLE assets drop oldid; +ALTER TABLE assets add constraint primary key(id); + +COMMIT; + +# ----------------- +:VERSION 3 + +BEGIN; + +ALTER TABLE assets change id oldid varchar(36); +ALTER TABLE assets add id char(36) not null default '00000000-0000-0000-0000-000000000000'; +UPDATE assets set id = oldid; +ALTER TABLE assets drop oldid; +ALTER TABLE assets add constraint primary key(id); + +COMMIT; + +# ----------------- +:VERSION 4 + +BEGIN; + +ALTER TABLE assets drop InvType; + +COMMIT; + +# ----------------- +:VERSION 5 + +BEGIN; + +ALTER TABLE assets add create_time integer default 0; +ALTER TABLE assets add access_time integer default 0; + +COMMIT; + +# ----------------- +:VERSION 6 + +DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621' + diff --git a/OpenSim/Data/MySQL/Resources/001_AuthStore.sql b/OpenSim/Data/MySQL/Resources/AuthStore.migrations similarity index 51% rename from OpenSim/Data/MySQL/Resources/001_AuthStore.sql rename to OpenSim/Data/MySQL/Resources/AuthStore.migrations index c7e16fbdfb..023c786aa5 100644 --- a/OpenSim/Data/MySQL/Resources/001_AuthStore.sql +++ b/OpenSim/Data/MySQL/Resources/AuthStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 # ------------------------------- + begin; CREATE TABLE `auth` ( @@ -19,3 +21,19 @@ CREATE TABLE `tokens` ( ) ENGINE=InnoDB; commit; + +:VERSION 2 # ------------------------------- + +BEGIN; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; + +:VERSION 3 # ------------------------------- + +BEGIN; + +ALTER TABLE `auth` ADD COLUMN `accountType` VARCHAR(32) NOT NULL DEFAULT 'UserAccount'; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/Avatar.migrations b/OpenSim/Data/MySQL/Resources/Avatar.migrations new file mode 100644 index 0000000000..8d0eee68a1 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/Avatar.migrations @@ -0,0 +1,12 @@ +:VERSION 1 + +BEGIN; + +CREATE TABLE Avatars ( + PrincipalID CHAR(36) NOT NULL, + Name VARCHAR(32) NOT NULL, + Value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY(PrincipalID, Name), + KEY(PrincipalID)); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations new file mode 100644 index 0000000000..ce713bdb89 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations @@ -0,0 +1,25 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `Friends` ( + `PrincipalID` CHAR(36) NOT NULL, + `Friend` VARCHAR(255) NOT NULL, + `Flags` VARCHAR(16) NOT NULL DEFAULT 0, + `Offered` VARCHAR(32) NOT NULL DEFAULT 0, + PRIMARY KEY(`PrincipalID`, `Friend`), + KEY(`PrincipalID`) +); + +COMMIT; + +:VERSION 2 # ------------------------- + +BEGIN; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; + + + diff --git a/OpenSim/Data/MySQL/Resources/001_GridStore.sql b/OpenSim/Data/MySQL/Resources/GridStore.migrations similarity index 64% rename from OpenSim/Data/MySQL/Resources/001_GridStore.sql rename to OpenSim/Data/MySQL/Resources/GridStore.migrations index cb0f9bd2cd..523a8ac7dc 100644 --- a/OpenSim/Data/MySQL/Resources/001_GridStore.sql +++ b/OpenSim/Data/MySQL/Resources/GridStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + CREATE TABLE `regions` ( `uuid` varchar(36) NOT NULL, `regionHandle` bigint(20) unsigned NOT NULL, @@ -30,3 +32,58 @@ CREATE TABLE `regions` ( KEY `regionHandle` (`regionHandle`), KEY `overrideHandles` (`eastOverrideHandle`,`westOverrideHandle`,`southOverrideHandle`,`northOverrideHandle`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Rev. 3'; + +:VERSION 2 + +BEGIN; + +ALTER TABLE regions add column access integer unsigned default 1; + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE regions add column ScopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; + +create index ScopeID on regions(ScopeID); + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE regions add column sizeX integer not null default 0; +ALTER TABLE regions add column sizeY integer not null default 0; + +COMMIT; + +:VERSION 5 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `flags` integer NOT NULL DEFAULT 0; +CREATE INDEX flags ON regions(flags); + +COMMIT; + +:VERSION 6 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `last_seen` integer NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 7 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `PrincipalID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE `regions` ADD COLUMN `Token` varchar(255) NOT NULL; + +COMMIT; + + diff --git a/OpenSim/Data/MySQL/Resources/InventoryStore.migrations b/OpenSim/Data/MySQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..8c5864e97d --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/InventoryStore.migrations @@ -0,0 +1,93 @@ +:VERSION 1 # ------------ +BEGIN; + +CREATE TABLE `inventoryfolders` ( + `folderID` varchar(36) NOT NULL default '', + `agentID` varchar(36) default NULL, + `parentFolderID` varchar(36) default NULL, + `folderName` varchar(64) default NULL, + `type` smallint NOT NULL default 0, + `version` int NOT NULL default 0, + PRIMARY KEY (`folderID`), + KEY `owner` (`agentID`), + KEY `parent` (`parentFolderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `inventoryitems` ( + `inventoryID` varchar(36) NOT NULL default '', + `assetID` varchar(36) default NULL, + `assetType` int(11) default NULL, + `parentFolderID` varchar(36) default NULL, + `avatarID` varchar(36) default NULL, + `inventoryName` varchar(64) default NULL, + `inventoryDescription` varchar(128) default NULL, + `inventoryNextPermissions` int(10) unsigned default NULL, + `inventoryCurrentPermissions` int(10) unsigned default NULL, + `invType` int(11) default NULL, + `creatorID` varchar(36) default NULL, + `inventoryBasePermissions` int(10) unsigned NOT NULL default 0, + `inventoryEveryOnePermissions` int(10) unsigned NOT NULL default 0, + `salePrice` int(11) NOT NULL default 0, + `saleType` tinyint(4) NOT NULL default 0, + `creationDate` int(11) NOT NULL default 0, + `groupID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `groupOwned` tinyint(4) NOT NULL default 0, + `flags` int(11) unsigned NOT NULL default 0, + PRIMARY KEY (`inventoryID`), + KEY `owner` (`avatarID`), + KEY `folder` (`parentFolderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 2 # ------------ + +BEGIN; + +ALTER TABLE inventoryfolders change folderID folderIDold varchar(36); +ALTER TABLE inventoryfolders change agentID agentIDold varchar(36); +ALTER TABLE inventoryfolders change parentFolderID parentFolderIDold varchar(36); +ALTER TABLE inventoryfolders add folderID char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE inventoryfolders add agentID char(36) default NULL; +ALTER TABLE inventoryfolders add parentFolderID char(36) default NULL; +UPDATE inventoryfolders set folderID = folderIDold, agentID = agentIDold, parentFolderID = parentFolderIDold; +ALTER TABLE inventoryfolders drop folderIDold; +ALTER TABLE inventoryfolders drop agentIDold; +ALTER TABLE inventoryfolders drop parentFolderIDold; +ALTER TABLE inventoryfolders add constraint primary key(folderID); +ALTER TABLE inventoryfolders add index inventoryfolders_agentid(agentID); +ALTER TABLE inventoryfolders add index inventoryfolders_parentFolderid(parentFolderID); + +ALTER TABLE inventoryitems change inventoryID inventoryIDold varchar(36); +ALTER TABLE inventoryitems change avatarID avatarIDold varchar(36); +ALTER TABLE inventoryitems change parentFolderID parentFolderIDold varchar(36); +ALTER TABLE inventoryitems add inventoryID char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE inventoryitems add avatarID char(36) default NULL; +ALTER TABLE inventoryitems add parentFolderID char(36) default NULL; +UPDATE inventoryitems set inventoryID = inventoryIDold, avatarID = avatarIDold, parentFolderID = parentFolderIDold; +ALTER TABLE inventoryitems drop inventoryIDold; +ALTER TABLE inventoryitems drop avatarIDold; +ALTER TABLE inventoryitems drop parentFolderIDold; +ALTER TABLE inventoryitems add constraint primary key(inventoryID); +ALTER TABLE inventoryitems add index inventoryitems_avatarid(avatarID); +ALTER TABLE inventoryitems add index inventoryitems_parentFolderid(parentFolderID); + +COMMIT; + +:VERSION 3 # ------------ + +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; + +:VERSION 4 # ------------ + +BEGIN; + +update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID is NULL; +update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID = ''; +alter table inventoryitems modify column creatorID varchar(36) not NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_LogStore.sql b/OpenSim/Data/MySQL/Resources/LogStore.migrations similarity index 95% rename from OpenSim/Data/MySQL/Resources/001_LogStore.sql rename to OpenSim/Data/MySQL/Resources/LogStore.migrations index b4c29fbef5..9ac26ac722 100644 --- a/OpenSim/Data/MySQL/Resources/001_LogStore.sql +++ b/OpenSim/Data/MySQL/Resources/LogStore.migrations @@ -1,3 +1,6 @@ + +:VERSION 1 + CREATE TABLE `logs` ( `logID` int(10) unsigned NOT NULL auto_increment, `target` varchar(36) default NULL, diff --git a/OpenSim/Data/MySQL/Resources/Presence.migrations b/OpenSim/Data/MySQL/Resources/Presence.migrations new file mode 100644 index 0000000000..d513024987 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/Presence.migrations @@ -0,0 +1,36 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `Presence` ( + `UserID` VARCHAR(255) NOT NULL, + `RegionID` CHAR(36) NOT NULL, + `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `Online` CHAR(5) NOT NULL DEFAULT 'false', + `Login` CHAR(16) NOT NULL DEFAULT '0', + `Logout` CHAR(16) NOT NULL DEFAULT '0', + `Position` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `LookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>' +) ENGINE=InnoDB; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +ALTER TABLE Presence ADD COLUMN `HomeRegionID` CHAR(36) NOT NULL; +ALTER TABLE Presence ADD COLUMN `HomePosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; +ALTER TABLE Presence ADD COLUMN `HomeLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; + +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +CREATE UNIQUE INDEX SessionID ON Presence(SessionID); +CREATE INDEX UserID ON Presence(UserID); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..3dab67e58e --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -0,0 +1,806 @@ + +:VERSION 1 #--------------------- + +BEGIN; + +CREATE TABLE `prims` ( + `UUID` varchar(255) NOT NULL, + `RegionUUID` varchar(255) default NULL, + `ParentID` int(11) default NULL, + `CreationDate` int(11) default NULL, + `Name` varchar(255) default NULL, + `SceneGroupID` varchar(255) default NULL, + `Text` varchar(255) default NULL, + `Description` varchar(255) default NULL, + `SitName` varchar(255) default NULL, + `TouchName` varchar(255) default NULL, + `ObjectFlags` int(11) default NULL, + `CreatorID` varchar(255) default NULL, + `OwnerID` varchar(255) default NULL, + `GroupID` varchar(255) default NULL, + `LastOwnerID` varchar(255) default NULL, + `OwnerMask` int(11) default NULL, + `NextOwnerMask` int(11) default NULL, + `GroupMask` int(11) default NULL, + `EveryoneMask` int(11) default NULL, + `BaseMask` int(11) default NULL, + `PositionX` float default NULL, + `PositionY` float default NULL, + `PositionZ` float default NULL, + `GroupPositionX` float default NULL, + `GroupPositionY` float default NULL, + `GroupPositionZ` float default NULL, + `VelocityX` float default NULL, + `VelocityY` float default NULL, + `VelocityZ` float default NULL, + `AngularVelocityX` float default NULL, + `AngularVelocityY` float default NULL, + `AngularVelocityZ` float default NULL, + `AccelerationX` float default NULL, + `AccelerationY` float default NULL, + `AccelerationZ` float default NULL, + `RotationX` float default NULL, + `RotationY` float default NULL, + `RotationZ` float default NULL, + `RotationW` float default NULL, + `SitTargetOffsetX` float default NULL, + `SitTargetOffsetY` float default NULL, + `SitTargetOffsetZ` float default NULL, + `SitTargetOrientW` float default NULL, + `SitTargetOrientX` float default NULL, + `SitTargetOrientY` float default NULL, + `SitTargetOrientZ` float default NULL, + PRIMARY KEY (`UUID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `primshapes` ( + `UUID` varchar(255) NOT NULL, + `Shape` int(11) default NULL, + `ScaleX` float default NULL, + `ScaleY` float default NULL, + `ScaleZ` float default NULL, + `PCode` int(11) default NULL, + `PathBegin` int(11) default NULL, + `PathEnd` int(11) default NULL, + `PathScaleX` int(11) default NULL, + `PathScaleY` int(11) default NULL, + `PathShearX` int(11) default NULL, + `PathShearY` int(11) default NULL, + `PathSkew` int(11) default NULL, + `PathCurve` int(11) default NULL, + `PathRadiusOffset` int(11) default NULL, + `PathRevolutions` int(11) default NULL, + `PathTaperX` int(11) default NULL, + `PathTaperY` int(11) default NULL, + `PathTwist` int(11) default NULL, + `PathTwistBegin` int(11) default NULL, + `ProfileBegin` int(11) default NULL, + `ProfileEnd` int(11) default NULL, + `ProfileCurve` int(11) default NULL, + `ProfileHollow` int(11) default NULL, + `State` int(11) default NULL, + `Texture` longblob, + `ExtraParams` longblob, + PRIMARY KEY (`UUID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `primitems` ( + `itemID` varchar(255) NOT NULL, + `primID` varchar(255) default NULL, + `assetID` varchar(255) default NULL, + `parentFolderID` varchar(255) default NULL, + `invType` int(11) default NULL, + `assetType` int(11) default NULL, + `name` varchar(255) default NULL, + `description` varchar(255) default NULL, + `creationDate` bigint(20) default NULL, + `creatorID` varchar(255) default NULL, + `ownerID` varchar(255) default NULL, + `lastOwnerID` varchar(255) default NULL, + `groupID` varchar(255) default NULL, + `nextPermissions` int(11) default NULL, + `currentPermissions` int(11) default NULL, + `basePermissions` int(11) default NULL, + `everyonePermissions` int(11) default NULL, + `groupPermissions` int(11) default NULL, + PRIMARY KEY (`itemID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `terrain` ( + `RegionUUID` varchar(255) default NULL, + `Revision` int(11) default NULL, + `Heightfield` longblob +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `land` ( + `UUID` varchar(255) NOT NULL, + `RegionUUID` varchar(255) default NULL, + `LocalLandID` int(11) default NULL, + `Bitmap` longblob, + `Name` varchar(255) default NULL, + `Description` varchar(255) default NULL, + `OwnerUUID` varchar(255) default NULL, + `IsGroupOwned` int(11) default NULL, + `Area` int(11) default NULL, + `AuctionID` int(11) default NULL, + `Category` int(11) default NULL, + `ClaimDate` int(11) default NULL, + `ClaimPrice` int(11) default NULL, + `GroupUUID` varchar(255) default NULL, + `SalePrice` int(11) default NULL, + `LandStatus` int(11) default NULL, + `LandFlags` int(11) default NULL, + `LandingType` int(11) default NULL, + `MediaAutoScale` int(11) default NULL, + `MediaTextureUUID` varchar(255) default NULL, + `MediaURL` varchar(255) default NULL, + `MusicURL` varchar(255) default NULL, + `PassHours` float default NULL, + `PassPrice` int(11) default NULL, + `SnapshotUUID` varchar(255) default NULL, + `UserLocationX` float default NULL, + `UserLocationY` float default NULL, + `UserLocationZ` float default NULL, + `UserLookAtX` float default NULL, + `UserLookAtY` float default NULL, + `UserLookAtZ` float default NULL, + `AuthbuyerID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + PRIMARY KEY (`UUID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `landaccesslist` ( + `LandUUID` varchar(255) default NULL, + `AccessUUID` varchar(255) default NULL, + `Flags` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +COMMIT; + +:VERSION 2 #--------------------- + +BEGIN; + +CREATE index prims_regionuuid on prims(RegionUUID); +CREATE index primitems_primid on primitems(primID); + +COMMIT; + +:VERSION 3 #--------------------- + +BEGIN; + CREATE TABLE regionban (regionUUID VARCHAR(36) NOT NULL, bannedUUID VARCHAR(36) NOT NULL, bannedIp VARCHAR(16) NOT NULL, bannedIpHostMask VARCHAR(16) NOT NULL) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; +COMMIT; + +:VERSION 4 #--------------------- + +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; + +:VERSION 5 #--------------------- +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36), + primary key(regionUUID) +); + +COMMIT; + + +:VERSION 6 #--------------------- + +BEGIN; + +alter table landaccesslist ENGINE = InnoDB; +alter table migrations ENGINE = InnoDB; +alter table primitems ENGINE = InnoDB; +alter table prims ENGINE = InnoDB; +alter table primshapes ENGINE = InnoDB; +alter table regionsettings ENGINE = InnoDB; +alter table terrain ENGINE = InnoDB; + +COMMIT; + +:VERSION 7 #--------------------- + +BEGIN; + +ALTER TABLE prims change UUID UUIDold varchar(255); +ALTER TABLE prims change RegionUUID RegionUUIDold varchar(255); +ALTER TABLE prims change CreatorID CreatorIDold varchar(255); +ALTER TABLE prims change OwnerID OwnerIDold varchar(255); +ALTER TABLE prims change GroupID GroupIDold varchar(255); +ALTER TABLE prims change LastOwnerID LastOwnerIDold varchar(255); +ALTER TABLE prims add UUID char(36); +ALTER TABLE prims add RegionUUID char(36); +ALTER TABLE prims add CreatorID char(36); +ALTER TABLE prims add OwnerID char(36); +ALTER TABLE prims add GroupID char(36); +ALTER TABLE prims add LastOwnerID char(36); +UPDATE prims set UUID = UUIDold, RegionUUID = RegionUUIDold, CreatorID = CreatorIDold, OwnerID = OwnerIDold, GroupID = GroupIDold, LastOwnerID = LastOwnerIDold; +ALTER TABLE prims drop UUIDold; +ALTER TABLE prims drop RegionUUIDold; +ALTER TABLE prims drop CreatorIDold; +ALTER TABLE prims drop OwnerIDold; +ALTER TABLE prims drop GroupIDold; +ALTER TABLE prims drop LastOwnerIDold; +ALTER TABLE prims add constraint primary key(UUID); +ALTER TABLE prims add index prims_regionuuid(RegionUUID); + +COMMIT; + +:VERSION 8 #--------------------- + +BEGIN; + +ALTER TABLE primshapes change UUID UUIDold varchar(255); +ALTER TABLE primshapes add UUID char(36); +UPDATE primshapes set UUID = UUIDold; +ALTER TABLE primshapes drop UUIDold; +ALTER TABLE primshapes add constraint primary key(UUID); + +COMMIT; + +:VERSION 9 #--------------------- + +BEGIN; + +ALTER TABLE primitems change itemID itemIDold varchar(255); +ALTER TABLE primitems change primID primIDold varchar(255); +ALTER TABLE primitems change assetID assetIDold varchar(255); +ALTER TABLE primitems change parentFolderID parentFolderIDold varchar(255); +ALTER TABLE primitems change creatorID creatorIDold varchar(255); +ALTER TABLE primitems change ownerID ownerIDold varchar(255); +ALTER TABLE primitems change groupID groupIDold varchar(255); +ALTER TABLE primitems change lastOwnerID lastOwnerIDold varchar(255); +ALTER TABLE primitems add itemID char(36); +ALTER TABLE primitems add primID char(36); +ALTER TABLE primitems add assetID char(36); +ALTER TABLE primitems add parentFolderID char(36); +ALTER TABLE primitems add creatorID char(36); +ALTER TABLE primitems add ownerID char(36); +ALTER TABLE primitems add groupID char(36); +ALTER TABLE primitems add lastOwnerID char(36); +UPDATE primitems set itemID = itemIDold, primID = primIDold, assetID = assetIDold, parentFolderID = parentFolderIDold, creatorID = creatorIDold, ownerID = ownerIDold, groupID = groupIDold, lastOwnerID = lastOwnerIDold; +ALTER TABLE primitems drop itemIDold; +ALTER TABLE primitems drop primIDold; +ALTER TABLE primitems drop assetIDold; +ALTER TABLE primitems drop parentFolderIDold; +ALTER TABLE primitems drop creatorIDold; +ALTER TABLE primitems drop ownerIDold; +ALTER TABLE primitems drop groupIDold; +ALTER TABLE primitems drop lastOwnerIDold; +ALTER TABLE primitems add constraint primary key(itemID); +ALTER TABLE primitems add index primitems_primid(primID); + +COMMIT; + +:VERSION 10 #--------------------- + +# 1 "010_RegionStore.sql" +# 1 "" +# 1 "" +# 1 "010_RegionStore.sql" +BEGIN; + +DELETE FROM regionsettings; + +COMMIT; + + +:VERSION 11 #--------------------- + +BEGIN; + +ALTER TABLE prims change SceneGroupID SceneGroupIDold varchar(255); +ALTER TABLE prims add SceneGroupID char(36); +UPDATE prims set SceneGroupID = SceneGroupIDold; +ALTER TABLE prims drop SceneGroupIDold; +ALTER TABLE prims add index prims_scenegroupid(SceneGroupID); + +COMMIT; + +:VERSION 12 #--------------------- + +BEGIN; + +ALTER TABLE prims add index prims_parentid(ParentID); + +COMMIT; + +:VERSION 13 #--------------------- +begin; + +drop table regionsettings; + +CREATE TABLE `regionsettings` ( + `regionUUID` char(36) NOT NULL, + `block_terraform` int(11) NOT NULL, + `block_fly` int(11) NOT NULL, + `allow_damage` int(11) NOT NULL, + `restrict_pushing` int(11) NOT NULL, + `allow_land_resell` int(11) NOT NULL, + `allow_land_join_divide` int(11) NOT NULL, + `block_show_in_search` int(11) NOT NULL, + `agent_limit` int(11) NOT NULL, + `object_bonus` float NOT NULL, + `maturity` int(11) NOT NULL, + `disable_scripts` int(11) NOT NULL, + `disable_collisions` int(11) NOT NULL, + `disable_physics` int(11) NOT NULL, + `terrain_texture_1` char(36) NOT NULL, + `terrain_texture_2` char(36) NOT NULL, + `terrain_texture_3` char(36) NOT NULL, + `terrain_texture_4` char(36) NOT NULL, + `elevation_1_nw` float NOT NULL, + `elevation_2_nw` float NOT NULL, + `elevation_1_ne` float NOT NULL, + `elevation_2_ne` float NOT NULL, + `elevation_1_se` float NOT NULL, + `elevation_2_se` float NOT NULL, + `elevation_1_sw` float NOT NULL, + `elevation_2_sw` float NOT NULL, + `water_height` float NOT NULL, + `terrain_raise_limit` float NOT NULL, + `terrain_lower_limit` float NOT NULL, + `use_estate_sun` int(11) NOT NULL, + `fixed_sun` int(11) NOT NULL, + `sun_position` float NOT NULL, + `covenant` char(36) default NULL, + `Sandbox` tinyint(4) NOT NULL, + PRIMARY KEY (`regionUUID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_managers` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_groups` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_users` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estateban` ( + `EstateID` int(10) unsigned NOT NULL, + `bannedUUID` varchar(36) NOT NULL, + `bannedIp` varchar(16) NOT NULL, + `bannedIpHostMask` varchar(16) NOT NULL, + `bannedNameMask` varchar(64) default NULL, + KEY `estateban_EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_settings` ( + `EstateID` int(10) unsigned NOT NULL auto_increment, + `EstateName` varchar(64) default NULL, + `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, + `DenyAnonymous` tinyint(4) NOT NULL, + `ResetHomeOnTeleport` tinyint(4) NOT NULL, + `FixedSun` tinyint(4) NOT NULL, + `DenyTransacted` tinyint(4) NOT NULL, + `BlockDwell` tinyint(4) NOT NULL, + `DenyIdentified` tinyint(4) NOT NULL, + `AllowVoice` tinyint(4) NOT NULL, + `UseGlobalTime` tinyint(4) NOT NULL, + `PricePerMeter` int(11) NOT NULL, + `TaxFree` tinyint(4) NOT NULL, + `AllowDirectTeleport` tinyint(4) NOT NULL, + `RedirectGridX` int(11) NOT NULL, + `RedirectGridY` int(11) NOT NULL, + `ParentEstateID` int(10) unsigned NOT NULL, + `SunPosition` double NOT NULL, + `EstateSkipScripts` tinyint(4) NOT NULL, + `BillableFactor` float NOT NULL, + `PublicAccess` tinyint(4) NOT NULL, + PRIMARY KEY (`EstateID`) +) ENGINE=InnoDB AUTO_INCREMENT=100; + +CREATE TABLE `estate_map` ( + `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `EstateID` int(11) NOT NULL, + PRIMARY KEY (`RegionID`), + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +commit; + +:VERSION 14 #--------------------- + +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null; + +alter table estate_settings add column EstateOwner varchar(36) not null; + +commit; + + +:VERSION 15 #--------------------- + +begin; + +alter table estate_settings add column DenyMinors tinyint not null; + +commit; + +:VERSION 16 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton1 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton2 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton3 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton4 integer not null default 0; +ALTER TABLE prims ADD COLUMN LoopedSound char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float not null default 0.0; +ALTER TABLE prims ADD COLUMN TextureAnimation blob; +ALTER TABLE prims ADD COLUMN OmegaX float not null default 0.0; +ALTER TABLE prims ADD COLUMN OmegaY float not null default 0.0; +ALTER TABLE prims ADD COLUMN OmegaZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN ForceMouselook tinyint not null default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin integer not null default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop tinyint not null default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge tinyint not null default 0; +ALTER TABLE prims ADD COLUMN SalePrice integer not null default 10; +ALTER TABLE prims ADD COLUMN SaleType tinyint not null default 0; + +COMMIT; + + +:VERSION 17 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; +ALTER TABLE prims ADD COLUMN ParticleSystem blob; + +COMMIT; + + +:VERSION 18 #--------------------- + +begin; + +ALTER TABLE prims ADD COLUMN ClickAction tinyint NOT NULL default 0; + +commit; + +:VERSION 19 #--------------------- + +begin; + +ALTER TABLE prims ADD COLUMN Material tinyint NOT NULL default 3; + +commit; + + +:VERSION 20 #--------------------- + +begin; + +ALTER TABLE land ADD COLUMN OtherCleanTime integer NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell integer NOT NULL default 0; + +commit; + +:VERSION 21 #--------------------- + +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + + +:VERSION 22 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float not null default 0.0; + +COMMIT; + +:VERSION 23 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN LinkNumber integer not null default 0; + +COMMIT; + +:VERSION 24 #--------------------- + +BEGIN; + +alter table regionsettings change column `object_bonus` `object_bonus` double NOT NULL; +alter table regionsettings change column `elevation_1_nw` `elevation_1_nw` double NOT NULL; +alter table regionsettings change column `elevation_2_nw` `elevation_2_nw` double NOT NULL; +alter table regionsettings change column `elevation_1_ne` `elevation_1_ne` double NOT NULL; +alter table regionsettings change column `elevation_2_ne` `elevation_2_ne` double NOT NULL; +alter table regionsettings change column `elevation_1_se` `elevation_1_se` double NOT NULL; +alter table regionsettings change column `elevation_2_se` `elevation_2_se` double NOT NULL; +alter table regionsettings change column `elevation_1_sw` `elevation_1_sw` double NOT NULL; +alter table regionsettings change column `elevation_2_sw` `elevation_2_sw` double NOT NULL; +alter table regionsettings change column `water_height` `water_height` double NOT NULL; +alter table regionsettings change column `terrain_raise_limit` `terrain_raise_limit` double NOT NULL; +alter table regionsettings change column `terrain_lower_limit` `terrain_lower_limit` double NOT NULL; +alter table regionsettings change column `sun_position` `sun_position` double NOT NULL; + +COMMIT; + + +:VERSION 25 #--------------------- + +BEGIN; + +alter table prims change column `PositionX` `PositionX` double default NULL; +alter table prims change column `PositionY` `PositionY` double default NULL; +alter table prims change column `PositionZ` `PositionZ` double default NULL; +alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; +alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; +alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; +alter table prims change column `VelocityX` `VelocityX` double default NULL; +alter table prims change column `VelocityY` `VelocityY` double default NULL; +alter table prims change column `VelocityZ` `VelocityZ` double default NULL; +alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; +alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; +alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; +alter table prims change column `AccelerationX` `AccelerationX` double default NULL; +alter table prims change column `AccelerationY` `AccelerationY` double default NULL; +alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; +alter table prims change column `RotationX` `RotationX` double default NULL; +alter table prims change column `RotationY` `RotationY` double default NULL; +alter table prims change column `RotationZ` `RotationZ` double default NULL; +alter table prims change column `RotationW` `RotationW` double default NULL; +alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; +alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; +alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; +alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; +alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; +alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; +alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; +alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; +alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; +alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; +alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; +alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; + +alter table primshapes change column `ScaleX` `ScaleX` double NOT NULL default '0'; +alter table primshapes change column `ScaleY` `ScaleY` double NOT NULL default '0'; +alter table primshapes change column `ScaleZ` `ScaleZ` double NOT NULL default '0'; + +COMMIT; + +:VERSION 26 #--------------------- + +begin; + +alter table prims change column `PositionX` `PositionX` double default NULL; +alter table prims change column `PositionY` `PositionY` double default NULL; +alter table prims change column `PositionZ` `PositionZ` double default NULL; +alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; +alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; +alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; +alter table prims change column `VelocityX` `VelocityX` double default NULL; +alter table prims change column `VelocityY` `VelocityY` double default NULL; +alter table prims change column `VelocityZ` `VelocityZ` double default NULL; +alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; +alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; +alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; +alter table prims change column `AccelerationX` `AccelerationX` double default NULL; +alter table prims change column `AccelerationY` `AccelerationY` double default NULL; +alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; +alter table prims change column `RotationX` `RotationX` double default NULL; +alter table prims change column `RotationY` `RotationY` double default NULL; +alter table prims change column `RotationZ` `RotationZ` double default NULL; +alter table prims change column `RotationW` `RotationW` double default NULL; +alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; +alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; +alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; +alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; +alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; +alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; +alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; +alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; +alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; +alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; +alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; +alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; + +commit; + +:VERSION 27 #--------------------- + +BEGIN; + +ALTER TABLE prims DROP COLUMN ParentID; + +COMMIT; + +:VERSION 28 #--------------------- + +BEGIN; + +update terrain + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = concat(substr(LandUUID, 1, 8), "-", substr(LandUUID, 9, 4), "-", substr(LandUUID, 13, 4), "-", substr(LandUUID, 17, 4), "-", substr(LandUUID, 21, 12)) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = concat(substr(AccessUUID, 1, 8), "-", substr(AccessUUID, 9, 4), "-", substr(AccessUUID, 13, 4), "-", substr(AccessUUID, 17, 4), "-", substr(AccessUUID, 21, 12)) + where AccessUUID not like '%-%'; + + +update prims + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + +update prims + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = concat(substr(SceneGroupID, 1, 8), "-", substr(SceneGroupID, 9, 4), "-", substr(SceneGroupID, 13, 4), "-", substr(SceneGroupID, 17, 4), "-", substr(SceneGroupID, 21, 12)) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = concat(substr(CreatorID, 1, 8), "-", substr(CreatorID, 9, 4), "-", substr(CreatorID, 13, 4), "-", substr(CreatorID, 17, 4), "-", substr(CreatorID, 21, 12)) + where CreatorID not like '%-%'; + +update prims + set OwnerID = concat(substr(OwnerID, 1, 8), "-", substr(OwnerID, 9, 4), "-", substr(OwnerID, 13, 4), "-", substr(OwnerID, 17, 4), "-", substr(OwnerID, 21, 12)) + where OwnerID not like '%-%'; + +update prims + set GroupID = concat(substr(GroupID, 1, 8), "-", substr(GroupID, 9, 4), "-", substr(GroupID, 13, 4), "-", substr(GroupID, 17, 4), "-", substr(GroupID, 21, 12)) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = concat(substr(LastOwnerID, 1, 8), "-", substr(LastOwnerID, 9, 4), "-", substr(LastOwnerID, 13, 4), "-", substr(LastOwnerID, 17, 4), "-", substr(LastOwnerID, 21, 12)) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + + +update land + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + +update land + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = concat(substr(OwnerUUID, 1, 8), "-", substr(OwnerUUID, 9, 4), "-", substr(OwnerUUID, 13, 4), "-", substr(OwnerUUID, 17, 4), "-", substr(OwnerUUID, 21, 12)) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = concat(substr(GroupUUID, 1, 8), "-", substr(GroupUUID, 9, 4), "-", substr(GroupUUID, 13, 4), "-", substr(GroupUUID, 17, 4), "-", substr(GroupUUID, 21, 12)) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = concat(substr(MediaTextureUUID, 1, 8), "-", substr(MediaTextureUUID, 9, 4), "-", substr(MediaTextureUUID, 13, 4), "-", substr(MediaTextureUUID, 17, 4), "-", substr(MediaTextureUUID, 21, 12)) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = concat(substr(SnapshotUUID, 1, 8), "-", substr(SnapshotUUID, 9, 4), "-", substr(SnapshotUUID, 13, 4), "-", substr(SnapshotUUID, 17, 4), "-", substr(SnapshotUUID, 21, 12)) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = concat(substr(AuthbuyerID, 1, 8), "-", substr(AuthbuyerID, 9, 4), "-", substr(AuthbuyerID, 13, 4), "-", substr(AuthbuyerID, 17, 4), "-", substr(AuthbuyerID, 21, 12)) + where AuthbuyerID not like '%-%'; + +COMMIT; + +:VERSION 29 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN PassTouches tinyint not null default 0; + +COMMIT; + +:VERSION 30 #--------------------- + +BEGIN; + +ALTER TABLE regionsettings ADD COLUMN loaded_creation_date varchar(20) default NULL; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_time varchar(20) default NULL; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_id varchar(64) default NULL; + +COMMIT; + +:VERSION 31 #--------------------- + +BEGIN; + +ALTER TABLE regionsettings DROP COLUMN loaded_creation_date; +ALTER TABLE regionsettings DROP COLUMN loaded_creation_time; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_datetime int unsigned NOT NULL default 0; + +COMMIT; + +:VERSION 32 #--------------------- + +BEGIN; +ALTER TABLE estate_settings AUTO_INCREMENT = 100; +COMMIT; + + + + diff --git a/OpenSim/Data/MySQL/Resources/UserAccount.migrations b/OpenSim/Data/MySQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..84011e6e49 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserAccount.migrations @@ -0,0 +1,47 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `UserAccounts` ( + `PrincipalID` CHAR(36) NOT NULL, + `ScopeID` CHAR(36) NOT NULL, + `FirstName` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(64) NOT NULL, + `Email` VARCHAR(64), + `ServiceURLs` TEXT, + `Created` INT(11) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 2 # ------------------------- + +BEGIN; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, lastname AS LastName, email as Email, CONCAT('AssetServerURI=', userAssetURI, ' InventoryServerURI=', userInventoryURI, ' GatewayURI= HomeURI=') AS ServiceURLs, created as Created FROM users; + +COMMIT; + +:VERSION 3 # ------------------------- + +BEGIN; + +CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); +CREATE INDEX Email ON UserAccounts(Email); +CREATE INDEX FirstName ON UserAccounts(FirstName); +CREATE INDEX LastName ON UserAccounts(LastName); +CREATE INDEX Name ON UserAccounts(FirstName,LastName); + +COMMIT; + +:VERSION 4 # ------------------------- + +BEGIN; + +ALTER TABLE UserAccounts ADD COLUMN UserLevel integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD COLUMN UserFlags integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD COLUMN UserTitle varchar(64) NOT NULL DEFAULT ''; + +COMMIT; + + diff --git a/OpenSim/Data/MySQL/Resources/001_UserStore.sql b/OpenSim/Data/MySQL/Resources/UserStore.migrations similarity index 72% rename from OpenSim/Data/MySQL/Resources/001_UserStore.sql rename to OpenSim/Data/MySQL/Resources/UserStore.migrations index 29ebc7d88b..f054611f04 100644 --- a/OpenSim/Data/MySQL/Resources/001_UserStore.sql +++ b/OpenSim/Data/MySQL/Resources/UserStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 # ----------------------------- + BEGIN; SET FOREIGN_KEY_CHECKS=0; @@ -104,4 +106,63 @@ CREATE TABLE `users` ( -- ---------------------------- -- Records -- ---------------------------- -COMMIT; \ No newline at end of file +COMMIT; + +:VERSION 2 # ----------------------------- + +BEGIN; + +ALTER TABLE users add homeRegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 3 # ----------------------------- + +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; + +:VERSION 4 # ----------------------------- + +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 # ----------------------------- + +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL, `attachpoint` int(11) NOT NULL, `item` char(36) NOT NULL, `asset` char(36) NOT NULL) ENGINE=InnoDB; + +COMMIT; + +:VERSION 6 # ----------------------------- + +BEGIN; + +ALTER TABLE agents add currentLookAt varchar(36) not null default ''; + +COMMIT; + +:VERSION 7 # ----------------------------- + +BEGIN; + +ALTER TABLE users add email varchar(250); + +COMMIT; + +:VERSION 8 # ----------------------------- + +BEGIN; + +ALTER TABLE users add scopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + From 1ad12851d076094d6fad3ebe604b4cae378eaef3 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 6 May 2010 22:44:57 +0300 Subject: [PATCH 133/260] Migrations for SQLite converted to new format --- .../Data/SQLite/Resources/001_AssetStore.sql | 12 - .../SQLite/Resources/001_InventoryStore.sql | 32 -- .../Data/SQLite/Resources/001_RegionStore.sql | 144 ----- .../Data/SQLite/Resources/001_UserStore.sql | 39 -- .../Data/SQLite/Resources/002_AssetStore.sql | 10 - .../Data/SQLite/Resources/002_AuthStore.sql | 5 - .../SQLite/Resources/002_FriendsStore.sql | 5 - .../SQLite/Resources/002_InventoryStore.sql | 8 - .../Data/SQLite/Resources/002_RegionStore.sql | 10 - .../Data/SQLite/Resources/002_UserAccount.sql | 5 - .../Data/SQLite/Resources/002_UserStore.sql | 5 - .../Data/SQLite/Resources/003_AssetStore.sql | 1 - .../SQLite/Resources/003_InventoryStore.sql | 5 - .../Data/SQLite/Resources/003_RegionStore.sql | 5 - .../Data/SQLite/Resources/003_UserStore.sql | 6 - .../Data/SQLite/Resources/004_AssetStore.sql | 7 - .../Data/SQLite/Resources/004_RegionStore.sql | 38 -- .../Data/SQLite/Resources/004_UserStore.sql | 6 - .../Data/SQLite/Resources/005_RegionStore.sql | 5 - .../Data/SQLite/Resources/005_UserStore.sql | 5 - .../Data/SQLite/Resources/006_RegionStore.sql | 102 ---- .../Data/SQLite/Resources/006_UserStore.sql | 20 - .../Data/SQLite/Resources/007_RegionStore.sql | 8 - .../Data/SQLite/Resources/007_UserStore.sql | 7 - .../Data/SQLite/Resources/008_RegionStore.sql | 6 - .../Data/SQLite/Resources/008_UserStore.sql | 5 - .../Data/SQLite/Resources/009_RegionStore.sql | 8 - .../Data/SQLite/Resources/009_UserStore.sql | 11 - .../Data/SQLite/Resources/010_RegionStore.sql | 5 - .../Data/SQLite/Resources/010_UserStore.sql | 37 -- .../Data/SQLite/Resources/011_RegionStore.sql | 28 - .../Data/SQLite/Resources/012_RegionStore.sql | 5 - .../Data/SQLite/Resources/013_RegionStore.sql | 6 - .../Data/SQLite/Resources/014_RegionStore.sql | 8 - .../Data/SQLite/Resources/015_RegionStore.sql | 6 - .../Data/SQLite/Resources/016_RegionStore.sql | 5 - .../Data/SQLite/Resources/017_RegionStore.sql | 8 - .../Data/SQLite/Resources/018_RegionStore.sql | 79 --- .../SQLite/Resources/AssetStore.migrations | 42 ++ ...001_AuthStore.sql => AuthStore.migrations} | 11 + .../{001_Avatar.sql => Avatar.migrations} | 2 + ...iendsStore.sql => FriendsStore.migrations} | 10 + ...oryStore.sql => InventoryStore.migrations} | 56 ++ .../SQLite/Resources/RegionStore.migrations | 526 ++++++++++++++++++ ...UserAccount.sql => UserAccount.migrations} | 14 +- .../SQLite/Resources/UserStore.migrations | 169 ++++++ 46 files changed, 828 insertions(+), 709 deletions(-) delete mode 100644 OpenSim/Data/SQLite/Resources/001_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/001_InventoryStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/001_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/001_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_AuthStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_FriendsStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_InventoryStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_UserAccount.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_InventoryStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/004_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/004_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/004_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/005_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/005_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/006_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/006_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/007_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/007_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/008_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/008_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/009_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/009_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/010_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/010_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/011_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/012_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/013_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/014_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/015_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/016_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/017_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/018_RegionStore.sql create mode 100644 OpenSim/Data/SQLite/Resources/AssetStore.migrations rename OpenSim/Data/SQLite/Resources/{001_AuthStore.sql => AuthStore.migrations} (62%) rename OpenSim/Data/SQLite/Resources/{001_Avatar.sql => Avatar.migrations} (92%) rename OpenSim/Data/SQLite/Resources/{001_FriendsStore.sql => FriendsStore.migrations} (63%) rename OpenSim/Data/SQLite/Resources/{004_InventoryStore.sql => InventoryStore.migrations} (55%) create mode 100644 OpenSim/Data/SQLite/Resources/RegionStore.migrations rename OpenSim/Data/SQLite/Resources/{001_UserAccount.sql => UserAccount.migrations} (51%) create mode 100644 OpenSim/Data/SQLite/Resources/UserStore.migrations diff --git a/OpenSim/Data/SQLite/Resources/001_AssetStore.sql b/OpenSim/Data/SQLite/Resources/001_AssetStore.sql deleted file mode 100644 index 2e026cad19..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_AssetStore.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN TRANSACTION; -CREATE TABLE assets( - UUID varchar(255) primary key, - Name varchar(255), - Description varchar(255), - Type integer, - InvType integer, - Local integer, - Temporary integer, - Data blob); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/001_InventoryStore.sql deleted file mode 100644 index 554d5c2ec8..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_InventoryStore.sql +++ /dev/null @@ -1,32 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE inventoryfolders( - UUID varchar(255) primary key, - name varchar(255), - agentID varchar(255), - parentID varchar(255), - type integer, - version integer); - -CREATE TABLE inventoryitems( - UUID varchar(255) primary key, - assetID varchar(255), - assetType integer, - invType integer, - parentFolderID varchar(255), - avatarID varchar(255), - creatorsID varchar(255), - inventoryName varchar(255), - inventoryDescription varchar(255), - inventoryNextPermissions integer, - inventoryCurrentPermissions integer, - inventoryBasePermissions integer, - inventoryEveryOnePermissions integer, - salePrice integer default 99, - saleType integer default 0, - creationDate integer default 2000, - groupID varchar(255) default '00000000-0000-0000-0000-000000000000', - groupOwned integer default 0, - flags integer default 0); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_RegionStore.sql b/OpenSim/Data/SQLite/Resources/001_RegionStore.sql deleted file mode 100644 index 39e8180cdc..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_RegionStore.sql +++ /dev/null @@ -1,144 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE prims( - UUID varchar(255) primary key, - RegionUUID varchar(255), - ParentID integer, - CreationDate integer, - Name varchar(255), - SceneGroupID varchar(255), - Text varchar(255), - Description varchar(255), - SitName varchar(255), - TouchName varchar(255), - CreatorID varchar(255), - OwnerID varchar(255), - GroupID varchar(255), - LastOwnerID varchar(255), - OwnerMask integer, - NextOwnerMask integer, - GroupMask integer, - EveryoneMask integer, - BaseMask integer, - PositionX float, - PositionY float, - PositionZ float, - GroupPositionX float, - GroupPositionY float, - GroupPositionZ float, - VelocityX float, - VelocityY float, - VelocityZ float, - AngularVelocityX float, - AngularVelocityY float, - AngularVelocityZ float, - AccelerationX float, - AccelerationY float, - AccelerationZ float, - RotationX float, - RotationY float, - RotationZ float, - RotationW float, - ObjectFlags integer, - SitTargetOffsetX float NOT NULL default 0, - SitTargetOffsetY float NOT NULL default 0, - SitTargetOffsetZ float NOT NULL default 0, - SitTargetOrientW float NOT NULL default 0, - SitTargetOrientX float NOT NULL default 0, - SitTargetOrientY float NOT NULL default 0, - SitTargetOrientZ float NOT NULL default 0); - -CREATE TABLE primshapes( - UUID varchar(255) primary key, - Shape integer, - ScaleX float, - ScaleY float, - ScaleZ float, - PCode integer, - PathBegin integer, - PathEnd integer, - PathScaleX integer, - PathScaleY integer, - PathShearX integer, - PathShearY integer, - PathSkew integer, - PathCurve integer, - PathRadiusOffset integer, - PathRevolutions integer, - PathTaperX integer, - PathTaperY integer, - PathTwist integer, - PathTwistBegin integer, - ProfileBegin integer, - ProfileEnd integer, - ProfileCurve integer, - ProfileHollow integer, - Texture blob, - ExtraParams blob, - State Integer NOT NULL default 0); - -CREATE TABLE primitems( - itemID varchar(255) primary key, - primID varchar(255), - assetID varchar(255), - parentFolderID varchar(255), - invType integer, - assetType integer, - name varchar(255), - description varchar(255), - creationDate integer, - creatorID varchar(255), - ownerID varchar(255), - lastOwnerID varchar(255), - groupID varchar(255), - nextPermissions string, - currentPermissions string, - basePermissions string, - everyonePermissions string, - groupPermissions string); - -CREATE TABLE terrain( - RegionUUID varchar(255), - Revision integer, - Heightfield blob); - -CREATE TABLE land( - UUID varchar(255) primary key, - RegionUUID varchar(255), - LocalLandID string, - Bitmap blob, - Name varchar(255), - Desc varchar(255), - OwnerUUID varchar(255), - IsGroupOwned string, - Area integer, - AuctionID integer, - Category integer, - ClaimDate integer, - ClaimPrice integer, - GroupUUID varchar(255), - SalePrice integer, - LandStatus integer, - LandFlags string, - LandingType string, - MediaAutoScale string, - MediaTextureUUID varchar(255), - MediaURL varchar(255), - MusicURL varchar(255), - PassHours float, - PassPrice string, - SnapshotUUID varchar(255), - UserLocationX float, - UserLocationY float, - UserLocationZ float, - UserLookAtX float, - UserLookAtY float, - UserLookAtZ float, - AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'); - -CREATE TABLE landaccesslist( - LandUUID varchar(255), - AccessUUID varchar(255), - Flags string); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_UserStore.sql b/OpenSim/Data/SQLite/Resources/001_UserStore.sql deleted file mode 100644 index b584594ced..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_UserStore.sql +++ /dev/null @@ -1,39 +0,0 @@ -BEGIN TRANSACTION; - --- users table -CREATE TABLE users( - UUID varchar(255) primary key, - username varchar(255), - surname varchar(255), - passwordHash varchar(255), - passwordSalt varchar(255), - homeRegionX integer, - homeRegionY integer, - homeLocationX float, - homeLocationY float, - homeLocationZ float, - homeLookAtX float, - homeLookAtY float, - homeLookAtZ float, - created integer, - lastLogin integer, - rootInventoryFolderID varchar(255), - userInventoryURI varchar(255), - userAssetURI varchar(255), - profileCanDoMask integer, - profileWantDoMask integer, - profileAboutText varchar(255), - profileFirstText varchar(255), - profileImage varchar(255), - profileFirstImage varchar(255), - webLoginKey text default '00000000-0000-0000-0000-000000000000'); --- friends table -CREATE TABLE userfriends( - ownerID varchar(255), - friendID varchar(255), - friendPerms integer, - ownerPerms integer, - datetimestamp integer); - -COMMIT; - diff --git a/OpenSim/Data/SQLite/Resources/002_AssetStore.sql b/OpenSim/Data/SQLite/Resources/002_AssetStore.sql deleted file mode 100644 index 5339b84dfd..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_AssetStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); -INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; -DROP TABLE assets; -CREATE TABLE assets(UUID,Name,Description,Type,Local,Temporary,Data); -INSERT INTO assets SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; -DROP TABLE assets_backup; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_AuthStore.sql b/OpenSim/Data/SQLite/Resources/002_AuthStore.sql deleted file mode 100644 index 3237b68fd6..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_AuthStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_FriendsStore.sql b/OpenSim/Data/SQLite/Resources/002_FriendsStore.sql deleted file mode 100644 index 6733502224..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_FriendsStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/002_InventoryStore.sql deleted file mode 100644 index 01951d6582..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_InventoryStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN TRANSACTION; - -create index inventoryfolders_agentid on inventoryfolders(agentid); -create index inventoryfolders_parentid on inventoryfolders(parentid); -create index inventoryitems_parentfolderid on inventoryitems(parentfolderid); -create index inventoryitems_avatarid on inventoryitems(avatarid); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/002_RegionStore.sql b/OpenSim/Data/SQLite/Resources/002_RegionStore.sql deleted file mode 100644 index c5c7c99455..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_RegionStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE regionban( - regionUUID varchar (255), - bannedUUID varchar (255), - bannedIp varchar (255), - bannedIpHostMask varchar (255) - ); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/002_UserAccount.sql b/OpenSim/Data/SQLite/Resources/002_UserAccount.sql deleted file mode 100644 index c7a62932ac..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_UserAccount.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, surname AS LastName, '' as Email, '' AS ServiceURLs, created as Created FROM users; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_UserStore.sql b/OpenSim/Data/SQLite/Resources/002_UserStore.sql deleted file mode 100644 index 48fc680b33..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/003_AssetStore.sql b/OpenSim/Data/SQLite/Resources/003_AssetStore.sql deleted file mode 100644 index f54f8d98a2..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_AssetStore.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM assets WHERE UUID = 'dc4b9f0bd00845c696a401dd947ac621' diff --git a/OpenSim/Data/SQLite/Resources/003_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/003_InventoryStore.sql deleted file mode 100644 index 4c6da91aab..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_InventoryStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/003_RegionStore.sql b/OpenSim/Data/SQLite/Resources/003_RegionStore.sql deleted file mode 100644 index 4db2f7587d..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE primitems add flags integer not null default 0; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/003_UserStore.sql b/OpenSim/Data/SQLite/Resources/003_UserStore.sql deleted file mode 100644 index 6f890eeec1..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add userFlags integer NOT NULL default 0; -ALTER TABLE users add godLevel integer NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_AssetStore.sql b/OpenSim/Data/SQLite/Resources/004_AssetStore.sql deleted file mode 100644 index 39421c4434..0000000000 --- a/OpenSim/Data/SQLite/Resources/004_AssetStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -update assets - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_RegionStore.sql b/OpenSim/Data/SQLite/Resources/004_RegionStore.sql deleted file mode 100644 index de328cb47a..0000000000 --- a/OpenSim/Data/SQLite/Resources/004_RegionStore.sql +++ /dev/null @@ -1,38 +0,0 @@ -BEGIN; - -create table regionsettings ( - regionUUID char(36) not null, - block_terraform integer not null, - block_fly integer not null, - allow_damage integer not null, - restrict_pushing integer not null, - allow_land_resell integer not null, - allow_land_join_divide integer not null, - block_show_in_search integer not null, - agent_limit integer not null, - object_bonus float not null, - maturity integer not null, - disable_scripts integer not null, - disable_collisions integer not null, - disable_physics integer not null, - terrain_texture_1 char(36) not null, - terrain_texture_2 char(36) not null, - terrain_texture_3 char(36) not null, - terrain_texture_4 char(36) not null, - elevation_1_nw float not null, - elevation_2_nw float not null, - elevation_1_ne float not null, - elevation_2_ne float not null, - elevation_1_se float not null, - elevation_2_se float not null, - elevation_1_sw float not null, - elevation_2_sw float not null, - water_height float not null, - terrain_raise_limit float not null, - terrain_lower_limit float not null, - use_estate_sun integer not null, - fixed_sun integer not null, - sun_position float not null, - covenant char(36)); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_UserStore.sql b/OpenSim/Data/SQLite/Resources/004_UserStore.sql deleted file mode 100644 index 03142afa37..0000000000 --- a/OpenSim/Data/SQLite/Resources/004_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add customType varchar(32) not null default ''; -ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/005_RegionStore.sql b/OpenSim/Data/SQLite/Resources/005_RegionStore.sql deleted file mode 100644 index 1f6d1bd271..0000000000 --- a/OpenSim/Data/SQLite/Resources/005_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -delete from regionsettings; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/005_UserStore.sql b/OpenSim/Data/SQLite/Resources/005_UserStore.sql deleted file mode 100644 index e45c09a493..0000000000 --- a/OpenSim/Data/SQLite/Resources/005_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `attachpoint` int(11) NOT NULL DEFAULT 0, `item` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `asset` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/006_RegionStore.sql b/OpenSim/Data/SQLite/Resources/006_RegionStore.sql deleted file mode 100644 index 94ed8181cd..0000000000 --- a/OpenSim/Data/SQLite/Resources/006_RegionStore.sql +++ /dev/null @@ -1,102 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE estate_groups ( - EstateID int(10) NOT NULL, - uuid char(36) NOT NULL -); - -CREATE TABLE estate_managers ( - EstateID int(10) NOT NULL, - uuid char(36) NOT NULL -); - -CREATE TABLE estate_map ( - RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - EstateID int(11) NOT NULL -); - -CREATE TABLE estate_settings ( - EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - EstateName varchar(64) default NULL, - AbuseEmailToEstateOwner tinyint(4) NOT NULL, - DenyAnonymous tinyint(4) NOT NULL, - ResetHomeOnTeleport tinyint(4) NOT NULL, - FixedSun tinyint(4) NOT NULL, - DenyTransacted tinyint(4) NOT NULL, - BlockDwell tinyint(4) NOT NULL, - DenyIdentified tinyint(4) NOT NULL, - AllowVoice tinyint(4) NOT NULL, - UseGlobalTime tinyint(4) NOT NULL, - PricePerMeter int(11) NOT NULL, - TaxFree tinyint(4) NOT NULL, - AllowDirectTeleport tinyint(4) NOT NULL, - RedirectGridX int(11) NOT NULL, - RedirectGridY int(11) NOT NULL, - ParentEstateID int(10) NOT NULL, - SunPosition double NOT NULL, - EstateSkipScripts tinyint(4) NOT NULL, - BillableFactor float NOT NULL, - PublicAccess tinyint(4) NOT NULL -); -insert into estate_settings (EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); -delete from estate_settings; -CREATE TABLE estate_users ( - EstateID int(10) NOT NULL, - uuid char(36) NOT NULL -); - -CREATE TABLE estateban ( - EstateID int(10) NOT NULL, - bannedUUID varchar(36) NOT NULL, - bannedIp varchar(16) NOT NULL, - bannedIpHostMask varchar(16) NOT NULL, - bannedNameMask varchar(64) default NULL -); - -drop table regionsettings; -CREATE TABLE regionsettings ( - regionUUID char(36) NOT NULL, - block_terraform int(11) NOT NULL, - block_fly int(11) NOT NULL, - allow_damage int(11) NOT NULL, - restrict_pushing int(11) NOT NULL, - allow_land_resell int(11) NOT NULL, - allow_land_join_divide int(11) NOT NULL, - block_show_in_search int(11) NOT NULL, - agent_limit int(11) NOT NULL, - object_bonus float NOT NULL, - maturity int(11) NOT NULL, - disable_scripts int(11) NOT NULL, - disable_collisions int(11) NOT NULL, - disable_physics int(11) NOT NULL, - terrain_texture_1 char(36) NOT NULL, - terrain_texture_2 char(36) NOT NULL, - terrain_texture_3 char(36) NOT NULL, - terrain_texture_4 char(36) NOT NULL, - elevation_1_nw float NOT NULL, - elevation_2_nw float NOT NULL, - elevation_1_ne float NOT NULL, - elevation_2_ne float NOT NULL, - elevation_1_se float NOT NULL, - elevation_2_se float NOT NULL, - elevation_1_sw float NOT NULL, - elevation_2_sw float NOT NULL, - water_height float NOT NULL, - terrain_raise_limit float NOT NULL, - terrain_lower_limit float NOT NULL, - use_estate_sun int(11) NOT NULL, - fixed_sun int(11) NOT NULL, - sun_position float NOT NULL, - covenant char(36) default NULL, - Sandbox tinyint(4) NOT NULL, - PRIMARY KEY (regionUUID) -); - -CREATE INDEX estate_ban_estate_id on estateban(EstateID); -CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); -CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); -CREATE INDEX estate_map_estate_id on estate_map(EstateID); -CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); -CREATE INDEX estate_users_estate_id on estate_users(EstateID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/006_UserStore.sql b/OpenSim/Data/SQLite/Resources/006_UserStore.sql deleted file mode 100644 index f9454c55cf..0000000000 --- a/OpenSim/Data/SQLite/Resources/006_UserStore.sql +++ /dev/null @@ -1,20 +0,0 @@ -BEGIN TRANSACTION; - --- usersagents table -CREATE TABLE IF NOT EXISTS useragents( - UUID varchar(255) primary key, - agentIP varchar(255), - agentPort integer, - agentOnline boolean, - sessionID varchar(255), - secureSessionID varchar(255), - regionID varchar(255), - loginTime integer, - logoutTime integer, - currentRegion varchar(255), - currentHandle varchar(255), - currentPosX float, - currentPosY float, - currentPosZ float); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/007_RegionStore.sql b/OpenSim/Data/SQLite/Resources/007_RegionStore.sql deleted file mode 100644 index 1c813a0d40..0000000000 --- a/OpenSim/Data/SQLite/Resources/007_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -alter table estate_settings add column AbuseEmail varchar(255) not null default ''; - -alter table estate_settings add column EstateOwner varchar(36) not null default ''; - -commit; - diff --git a/OpenSim/Data/SQLite/Resources/007_UserStore.sql b/OpenSim/Data/SQLite/Resources/007_UserStore.sql deleted file mode 100644 index 8b0cd285c7..0000000000 --- a/OpenSim/Data/SQLite/Resources/007_UserStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION; - -ALTER TABLE useragents add currentLookAtX float not null default 128; -ALTER TABLE useragents add currentLookAtY float not null default 128; -ALTER TABLE useragents add currentLookAtZ float not null default 70; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/008_RegionStore.sql b/OpenSim/Data/SQLite/Resources/008_RegionStore.sql deleted file mode 100644 index 28bfbf59c3..0000000000 --- a/OpenSim/Data/SQLite/Resources/008_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -alter table estate_settings add column DenyMinors tinyint not null default 0; - -commit; - diff --git a/OpenSim/Data/SQLite/Resources/008_UserStore.sql b/OpenSim/Data/SQLite/Resources/008_UserStore.sql deleted file mode 100644 index 97da81848c..0000000000 --- a/OpenSim/Data/SQLite/Resources/008_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -ALTER TABLE users add email varchar(250); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/009_RegionStore.sql b/OpenSim/Data/SQLite/Resources/009_RegionStore.sql deleted file mode 100644 index 1f40548f36..0000000000 --- a/OpenSim/Data/SQLite/Resources/009_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/009_UserStore.sql b/OpenSim/Data/SQLite/Resources/009_UserStore.sql deleted file mode 100644 index 8ab03ef897..0000000000 --- a/OpenSim/Data/SQLite/Resources/009_UserStore.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - -update users - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -update useragents - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/010_RegionStore.sql b/OpenSim/Data/SQLite/Resources/010_RegionStore.sql deleted file mode 100644 index b91ccf0a8d..0000000000 --- a/OpenSim/Data/SQLite/Resources/010_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN ClickAction INTEGER NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/010_UserStore.sql b/OpenSim/Data/SQLite/Resources/010_UserStore.sql deleted file mode 100644 index 5f956dadfd..0000000000 --- a/OpenSim/Data/SQLite/Resources/010_UserStore.sql +++ /dev/null @@ -1,37 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE IF NOT EXISTS avatarappearance( - Owner varchar(36) NOT NULL primary key, - BodyItem varchar(36) DEFAULT NULL, - BodyAsset varchar(36) DEFAULT NULL, - SkinItem varchar(36) DEFAULT NULL, - SkinAsset varchar(36) DEFAULT NULL, - HairItem varchar(36) DEFAULT NULL, - HairAsset varchar(36) DEFAULT NULL, - EyesItem varchar(36) DEFAULT NULL, - EyesAsset varchar(36) DEFAULT NULL, - ShirtItem varchar(36) DEFAULT NULL, - ShirtAsset varchar(36) DEFAULT NULL, - PantsItem varchar(36) DEFAULT NULL, - PantsAsset varchar(36) DEFAULT NULL, - ShoesItem varchar(36) DEFAULT NULL, - ShoesAsset varchar(36) DEFAULT NULL, - SocksItem varchar(36) DEFAULT NULL, - SocksAsset varchar(36) DEFAULT NULL, - JacketItem varchar(36) DEFAULT NULL, - JacketAsset varchar(36) DEFAULT NULL, - GlovesItem varchar(36) DEFAULT NULL, - GlovesAsset varchar(36) DEFAULT NULL, - UnderShirtItem varchar(36) DEFAULT NULL, - UnderShirtAsset varchar(36) DEFAULT NULL, - UnderPantsItem varchar(36) DEFAULT NULL, - UnderPantsAsset varchar(36) DEFAULT NULL, - SkirtItem varchar(36) DEFAULT NULL, - SkirtAsset varchar(36) DEFAULT NULL, - Texture blob, - VisualParams blob, - Serial int DEFAULT NULL, - AvatarHeight float DEFAULT NULL -); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/011_RegionStore.sql b/OpenSim/Data/SQLite/Resources/011_RegionStore.sql deleted file mode 100644 index 42bef89616..0000000000 --- a/OpenSim/Data/SQLite/Resources/011_RegionStore.sql +++ /dev/null @@ -1,28 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN PayPrice INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton1 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton2 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton3 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton4 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN LoopedSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN LoopedSoundGain float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN TextureAnimation string; -ALTER TABLE prims ADD COLUMN ParticleSystem string; -ALTER TABLE prims ADD COLUMN OmegaX float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN OmegaY float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN OmegaZ float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetX float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetY float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN ForceMouselook string NOT NULL default 0; -ALTER TABLE prims ADD COLUMN ScriptAccessPin INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN AllowedDrop INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN DieAtEdge string NOT NULL default 0; -ALTER TABLE prims ADD COLUMN SalePrice INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN SaleType string NOT NULL default 0; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/012_RegionStore.sql b/OpenSim/Data/SQLite/Resources/012_RegionStore.sql deleted file mode 100644 index d952b78bd2..0000000000 --- a/OpenSim/Data/SQLite/Resources/012_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN Material INTEGER NOT NULL default 3; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/013_RegionStore.sql b/OpenSim/Data/SQLite/Resources/013_RegionStore.sql deleted file mode 100644 index 11529cd3f1..0000000000 --- a/OpenSim/Data/SQLite/Resources/013_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE land ADD COLUMN OtherCleanTime INTEGER NOT NULL default 0; -ALTER TABLE land ADD COLUMN Dwell INTEGER NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/014_RegionStore.sql b/OpenSim/Data/SQLite/Resources/014_RegionStore.sql deleted file mode 100644 index c59b27e745..0000000000 --- a/OpenSim/Data/SQLite/Resources/014_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/SQLite/Resources/015_RegionStore.sql b/OpenSim/Data/SQLite/Resources/015_RegionStore.sql deleted file mode 100644 index c43f356be3..0000000000 --- a/OpenSim/Data/SQLite/Resources/015_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN CollisionSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN CollisionSoundVolume float NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/016_RegionStore.sql b/OpenSim/Data/SQLite/Resources/016_RegionStore.sql deleted file mode 100644 index 52f160cdbc..0000000000 --- a/OpenSim/Data/SQLite/Resources/016_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN VolumeDetect INTEGER NOT NULL DEFAULT 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/017_RegionStore.sql b/OpenSim/Data/SQLite/Resources/017_RegionStore.sql deleted file mode 100644 index 6c6b7b5d40..0000000000 --- a/OpenSim/Data/SQLite/Resources/017_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; -CREATE TEMPORARY TABLE prims_backup(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); -INSERT INTO prims_backup SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims; -DROP TABLE prims; -CREATE TABLE prims(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); -INSERT INTO prims SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims_backup; -DROP TABLE prims_backup; -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/018_RegionStore.sql b/OpenSim/Data/SQLite/Resources/018_RegionStore.sql deleted file mode 100644 index 6a390c2161..0000000000 --- a/OpenSim/Data/SQLite/Resources/018_RegionStore.sql +++ /dev/null @@ -1,79 +0,0 @@ -BEGIN; - -update terrain - set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) - where RegionUUID not like '%-%'; - - -update landaccesslist - set LandUUID = substr(LandUUID, 1, 8) || "-" || substr(LandUUID, 9, 4) || "-" || substr(LandUUID, 13, 4) || "-" || substr(LandUUID, 17, 4) || "-" || substr(LandUUID, 21, 12) - where LandUUID not like '%-%'; - -update landaccesslist - set AccessUUID = substr(AccessUUID, 1, 8) || "-" || substr(AccessUUID, 9, 4) || "-" || substr(AccessUUID, 13, 4) || "-" || substr(AccessUUID, 17, 4) || "-" || substr(AccessUUID, 21, 12) - where AccessUUID not like '%-%'; - - -update prims - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -update prims - set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) - where RegionUUID not like '%-%'; - -update prims - set SceneGroupID = substr(SceneGroupID, 1, 8) || "-" || substr(SceneGroupID, 9, 4) || "-" || substr(SceneGroupID, 13, 4) || "-" || substr(SceneGroupID, 17, 4) || "-" || substr(SceneGroupID, 21, 12) - where SceneGroupID not like '%-%'; - -update prims - set CreatorID = substr(CreatorID, 1, 8) || "-" || substr(CreatorID, 9, 4) || "-" || substr(CreatorID, 13, 4) || "-" || substr(CreatorID, 17, 4) || "-" || substr(CreatorID, 21, 12) - where CreatorID not like '%-%'; - -update prims - set OwnerID = substr(OwnerID, 1, 8) || "-" || substr(OwnerID, 9, 4) || "-" || substr(OwnerID, 13, 4) || "-" || substr(OwnerID, 17, 4) || "-" || substr(OwnerID, 21, 12) - where OwnerID not like '%-%'; - -update prims - set GroupID = substr(GroupID, 1, 8) || "-" || substr(GroupID, 9, 4) || "-" || substr(GroupID, 13, 4) || "-" || substr(GroupID, 17, 4) || "-" || substr(GroupID, 21, 12) - where GroupID not like '%-%'; - -update prims - set LastOwnerID = substr(LastOwnerID, 1, 8) || "-" || substr(LastOwnerID, 9, 4) || "-" || substr(LastOwnerID, 13, 4) || "-" || substr(LastOwnerID, 17, 4) || "-" || substr(LastOwnerID, 21, 12) - where LastOwnerID not like '%-%'; - - -update primshapes - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - - -update land - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -update land - set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) - where RegionUUID not like '%-%'; - -update land - set OwnerUUID = substr(OwnerUUID, 1, 8) || "-" || substr(OwnerUUID, 9, 4) || "-" || substr(OwnerUUID, 13, 4) || "-" || substr(OwnerUUID, 17, 4) || "-" || substr(OwnerUUID, 21, 12) - where OwnerUUID not like '%-%'; - -update land - set GroupUUID = substr(GroupUUID, 1, 8) || "-" || substr(GroupUUID, 9, 4) || "-" || substr(GroupUUID, 13, 4) || "-" || substr(GroupUUID, 17, 4) || "-" || substr(GroupUUID, 21, 12) - where GroupUUID not like '%-%'; - -update land - set MediaTextureUUID = substr(MediaTextureUUID, 1, 8) || "-" || substr(MediaTextureUUID, 9, 4) || "-" || substr(MediaTextureUUID, 13, 4) || "-" || substr(MediaTextureUUID, 17, 4) || "-" || substr(MediaTextureUUID, 21, 12) - where MediaTextureUUID not like '%-%'; - -update land - set SnapshotUUID = substr(SnapshotUUID, 1, 8) || "-" || substr(SnapshotUUID, 9, 4) || "-" || substr(SnapshotUUID, 13, 4) || "-" || substr(SnapshotUUID, 17, 4) || "-" || substr(SnapshotUUID, 21, 12) - where SnapshotUUID not like '%-%'; - -update land - set AuthbuyerID = substr(AuthbuyerID, 1, 8) || "-" || substr(AuthbuyerID, 9, 4) || "-" || substr(AuthbuyerID, 13, 4) || "-" || substr(AuthbuyerID, 17, 4) || "-" || substr(AuthbuyerID, 21, 12) - where AuthbuyerID not like '%-%'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/AssetStore.migrations b/OpenSim/Data/SQLite/Resources/AssetStore.migrations new file mode 100644 index 0000000000..fb72b91af9 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/AssetStore.migrations @@ -0,0 +1,42 @@ +:VERSION 1 + +BEGIN TRANSACTION; +CREATE TABLE assets( + UUID varchar(255) primary key, + Name varchar(255), + Description varchar(255), + Type integer, + InvType integer, + Local integer, + Temporary integer, + Data blob); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; +DROP TABLE assets; +CREATE TABLE assets(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; +DROP TABLE assets_backup; + +COMMIT; + +:VERSION 3 + +DELETE FROM assets WHERE UUID = 'dc4b9f0bd00845c696a401dd947ac621' + +:VERSION 4 + +BEGIN; + +update assets + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/001_AuthStore.sql b/OpenSim/Data/SQLite/Resources/AuthStore.migrations similarity index 62% rename from OpenSim/Data/SQLite/Resources/001_AuthStore.sql rename to OpenSim/Data/SQLite/Resources/AuthStore.migrations index 468567dcc2..ca6bdf55a5 100644 --- a/OpenSim/Data/SQLite/Resources/001_AuthStore.sql +++ b/OpenSim/Data/SQLite/Resources/AuthStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION; CREATE TABLE auth ( @@ -16,3 +18,12 @@ CREATE TABLE tokens ( ); COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/001_Avatar.sql b/OpenSim/Data/SQLite/Resources/Avatar.migrations similarity index 92% rename from OpenSim/Data/SQLite/Resources/001_Avatar.sql rename to OpenSim/Data/SQLite/Resources/Avatar.migrations index 7ec906b48a..13b41969ec 100644 --- a/OpenSim/Data/SQLite/Resources/001_Avatar.sql +++ b/OpenSim/Data/SQLite/Resources/Avatar.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION; CREATE TABLE Avatars ( diff --git a/OpenSim/Data/SQLite/Resources/001_FriendsStore.sql b/OpenSim/Data/SQLite/Resources/FriendsStore.migrations similarity index 63% rename from OpenSim/Data/SQLite/Resources/001_FriendsStore.sql rename to OpenSim/Data/SQLite/Resources/FriendsStore.migrations index f1b9ab9902..3eb4352a0c 100644 --- a/OpenSim/Data/SQLite/Resources/001_FriendsStore.sql +++ b/OpenSim/Data/SQLite/Resources/FriendsStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION; CREATE TABLE `Friends` ( @@ -8,3 +10,11 @@ CREATE TABLE `Friends` ( PRIMARY KEY(`PrincipalID`, `Friend`)); COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations similarity index 55% rename from OpenSim/Data/SQLite/Resources/004_InventoryStore.sql rename to OpenSim/Data/SQLite/Resources/InventoryStore.migrations index e8f4d46333..585ac498e6 100644 --- a/OpenSim/Data/SQLite/Resources/004_InventoryStore.sql +++ b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations @@ -1,3 +1,59 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders( + UUID varchar(255) primary key, + name varchar(255), + agentID varchar(255), + parentID varchar(255), + type integer, + version integer); + +CREATE TABLE inventoryitems( + UUID varchar(255) primary key, + assetID varchar(255), + assetType integer, + invType integer, + parentFolderID varchar(255), + avatarID varchar(255), + creatorsID varchar(255), + inventoryName varchar(255), + inventoryDescription varchar(255), + inventoryNextPermissions integer, + inventoryCurrentPermissions integer, + inventoryBasePermissions integer, + inventoryEveryOnePermissions integer, + salePrice integer default 99, + saleType integer default 0, + creationDate integer default 2000, + groupID varchar(255) default '00000000-0000-0000-0000-000000000000', + groupOwned integer default 0, + flags integer default 0); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +create index inventoryfolders_agentid on inventoryfolders(agentid); +create index inventoryfolders_parentid on inventoryfolders(parentid); +create index inventoryitems_parentfolderid on inventoryitems(parentfolderid); +create index inventoryitems_avatarid on inventoryitems(avatarid); + +COMMIT; + +:VERSION 3 + +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; + +:VERSION 4 + BEGIN; update inventoryitems diff --git a/OpenSim/Data/SQLite/Resources/RegionStore.migrations b/OpenSim/Data/SQLite/Resources/RegionStore.migrations new file mode 100644 index 0000000000..7b27378139 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/RegionStore.migrations @@ -0,0 +1,526 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE prims( + UUID varchar(255) primary key, + RegionUUID varchar(255), + ParentID integer, + CreationDate integer, + Name varchar(255), + SceneGroupID varchar(255), + Text varchar(255), + Description varchar(255), + SitName varchar(255), + TouchName varchar(255), + CreatorID varchar(255), + OwnerID varchar(255), + GroupID varchar(255), + LastOwnerID varchar(255), + OwnerMask integer, + NextOwnerMask integer, + GroupMask integer, + EveryoneMask integer, + BaseMask integer, + PositionX float, + PositionY float, + PositionZ float, + GroupPositionX float, + GroupPositionY float, + GroupPositionZ float, + VelocityX float, + VelocityY float, + VelocityZ float, + AngularVelocityX float, + AngularVelocityY float, + AngularVelocityZ float, + AccelerationX float, + AccelerationY float, + AccelerationZ float, + RotationX float, + RotationY float, + RotationZ float, + RotationW float, + ObjectFlags integer, + SitTargetOffsetX float NOT NULL default 0, + SitTargetOffsetY float NOT NULL default 0, + SitTargetOffsetZ float NOT NULL default 0, + SitTargetOrientW float NOT NULL default 0, + SitTargetOrientX float NOT NULL default 0, + SitTargetOrientY float NOT NULL default 0, + SitTargetOrientZ float NOT NULL default 0); + +CREATE TABLE primshapes( + UUID varchar(255) primary key, + Shape integer, + ScaleX float, + ScaleY float, + ScaleZ float, + PCode integer, + PathBegin integer, + PathEnd integer, + PathScaleX integer, + PathScaleY integer, + PathShearX integer, + PathShearY integer, + PathSkew integer, + PathCurve integer, + PathRadiusOffset integer, + PathRevolutions integer, + PathTaperX integer, + PathTaperY integer, + PathTwist integer, + PathTwistBegin integer, + ProfileBegin integer, + ProfileEnd integer, + ProfileCurve integer, + ProfileHollow integer, + Texture blob, + ExtraParams blob, + State Integer NOT NULL default 0); + +CREATE TABLE primitems( + itemID varchar(255) primary key, + primID varchar(255), + assetID varchar(255), + parentFolderID varchar(255), + invType integer, + assetType integer, + name varchar(255), + description varchar(255), + creationDate integer, + creatorID varchar(255), + ownerID varchar(255), + lastOwnerID varchar(255), + groupID varchar(255), + nextPermissions string, + currentPermissions string, + basePermissions string, + everyonePermissions string, + groupPermissions string); + +CREATE TABLE terrain( + RegionUUID varchar(255), + Revision integer, + Heightfield blob); + +CREATE TABLE land( + UUID varchar(255) primary key, + RegionUUID varchar(255), + LocalLandID string, + Bitmap blob, + Name varchar(255), + Desc varchar(255), + OwnerUUID varchar(255), + IsGroupOwned string, + Area integer, + AuctionID integer, + Category integer, + ClaimDate integer, + ClaimPrice integer, + GroupUUID varchar(255), + SalePrice integer, + LandStatus integer, + LandFlags string, + LandingType string, + MediaAutoScale string, + MediaTextureUUID varchar(255), + MediaURL varchar(255), + MusicURL varchar(255), + PassHours float, + PassPrice string, + SnapshotUUID varchar(255), + UserLocationX float, + UserLocationY float, + UserLocationZ float, + UserLookAtX float, + UserLookAtY float, + UserLookAtZ float, + AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'); + +CREATE TABLE landaccesslist( + LandUUID varchar(255), + AccessUUID varchar(255), + Flags string); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE regionban( + regionUUID varchar (255), + bannedUUID varchar (255), + bannedIp varchar (255), + bannedIpHostMask varchar (255) + ); + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36)); + +COMMIT; + +:VERSION 5 + +BEGIN; + +delete from regionsettings; + +COMMIT; + +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE estate_groups ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_managers ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_map ( + RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + EstateID int(11) NOT NULL +); + +CREATE TABLE estate_settings ( + EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + EstateName varchar(64) default NULL, + AbuseEmailToEstateOwner tinyint(4) NOT NULL, + DenyAnonymous tinyint(4) NOT NULL, + ResetHomeOnTeleport tinyint(4) NOT NULL, + FixedSun tinyint(4) NOT NULL, + DenyTransacted tinyint(4) NOT NULL, + BlockDwell tinyint(4) NOT NULL, + DenyIdentified tinyint(4) NOT NULL, + AllowVoice tinyint(4) NOT NULL, + UseGlobalTime tinyint(4) NOT NULL, + PricePerMeter int(11) NOT NULL, + TaxFree tinyint(4) NOT NULL, + AllowDirectTeleport tinyint(4) NOT NULL, + RedirectGridX int(11) NOT NULL, + RedirectGridY int(11) NOT NULL, + ParentEstateID int(10) NOT NULL, + SunPosition double NOT NULL, + EstateSkipScripts tinyint(4) NOT NULL, + BillableFactor float NOT NULL, + PublicAccess tinyint(4) NOT NULL +); +insert into estate_settings (EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); +delete from estate_settings; +CREATE TABLE estate_users ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estateban ( + EstateID int(10) NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL, + bannedNameMask varchar(64) default NULL +); + +drop table regionsettings; +CREATE TABLE regionsettings ( + regionUUID char(36) NOT NULL, + block_terraform int(11) NOT NULL, + block_fly int(11) NOT NULL, + allow_damage int(11) NOT NULL, + restrict_pushing int(11) NOT NULL, + allow_land_resell int(11) NOT NULL, + allow_land_join_divide int(11) NOT NULL, + block_show_in_search int(11) NOT NULL, + agent_limit int(11) NOT NULL, + object_bonus float NOT NULL, + maturity int(11) NOT NULL, + disable_scripts int(11) NOT NULL, + disable_collisions int(11) NOT NULL, + disable_physics int(11) NOT NULL, + terrain_texture_1 char(36) NOT NULL, + terrain_texture_2 char(36) NOT NULL, + terrain_texture_3 char(36) NOT NULL, + terrain_texture_4 char(36) NOT NULL, + elevation_1_nw float NOT NULL, + elevation_2_nw float NOT NULL, + elevation_1_ne float NOT NULL, + elevation_2_ne float NOT NULL, + elevation_1_se float NOT NULL, + elevation_2_se float NOT NULL, + elevation_1_sw float NOT NULL, + elevation_2_sw float NOT NULL, + water_height float NOT NULL, + terrain_raise_limit float NOT NULL, + terrain_lower_limit float NOT NULL, + use_estate_sun int(11) NOT NULL, + fixed_sun int(11) NOT NULL, + sun_position float NOT NULL, + covenant char(36) default NULL, + Sandbox tinyint(4) NOT NULL, + PRIMARY KEY (regionUUID) +); + +CREATE INDEX estate_ban_estate_id on estateban(EstateID); +CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); +CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); +CREATE INDEX estate_map_estate_id on estate_map(EstateID); +CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); +CREATE INDEX estate_users_estate_id on estate_users(EstateID); + +COMMIT; + +:VERSION 7 + +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null default ''; + +alter table estate_settings add column EstateOwner varchar(36) not null default ''; + +commit; + +:VERSION 8 + +begin; + +alter table estate_settings add column DenyMinors tinyint not null default 0; + +commit; + +:VERSION 9 + +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; + +COMMIT; + +:VERSION 10 + +BEGIN; + +ALTER TABLE prims ADD COLUMN ClickAction INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 11 + +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton1 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton2 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton3 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton4 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN LoopedSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN TextureAnimation string; +ALTER TABLE prims ADD COLUMN ParticleSystem string; +ALTER TABLE prims ADD COLUMN OmegaX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ForceMouselook string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SalePrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SaleType string NOT NULL default 0; + +COMMIT; + +:VERSION 12 + +BEGIN; + +ALTER TABLE prims ADD COLUMN Material INTEGER NOT NULL default 3; + +COMMIT; + +:VERSION 13 + +BEGIN; + +ALTER TABLE land ADD COLUMN OtherCleanTime INTEGER NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 14 + +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + +:VERSION 15 + +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float NOT NULL default 0; + +COMMIT; + +:VERSION 16 + +BEGIN; + +ALTER TABLE prims ADD COLUMN VolumeDetect INTEGER NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 17 + +BEGIN; +CREATE TEMPORARY TABLE prims_backup(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims_backup SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims; +DROP TABLE prims; +CREATE TABLE prims(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims_backup; +DROP TABLE prims_backup; +COMMIT; + +:VERSION 18 + +BEGIN; + +update terrain + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = substr(LandUUID, 1, 8) || "-" || substr(LandUUID, 9, 4) || "-" || substr(LandUUID, 13, 4) || "-" || substr(LandUUID, 17, 4) || "-" || substr(LandUUID, 21, 12) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = substr(AccessUUID, 1, 8) || "-" || substr(AccessUUID, 9, 4) || "-" || substr(AccessUUID, 13, 4) || "-" || substr(AccessUUID, 17, 4) || "-" || substr(AccessUUID, 21, 12) + where AccessUUID not like '%-%'; + + +update prims + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update prims + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = substr(SceneGroupID, 1, 8) || "-" || substr(SceneGroupID, 9, 4) || "-" || substr(SceneGroupID, 13, 4) || "-" || substr(SceneGroupID, 17, 4) || "-" || substr(SceneGroupID, 21, 12) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = substr(CreatorID, 1, 8) || "-" || substr(CreatorID, 9, 4) || "-" || substr(CreatorID, 13, 4) || "-" || substr(CreatorID, 17, 4) || "-" || substr(CreatorID, 21, 12) + where CreatorID not like '%-%'; + +update prims + set OwnerID = substr(OwnerID, 1, 8) || "-" || substr(OwnerID, 9, 4) || "-" || substr(OwnerID, 13, 4) || "-" || substr(OwnerID, 17, 4) || "-" || substr(OwnerID, 21, 12) + where OwnerID not like '%-%'; + +update prims + set GroupID = substr(GroupID, 1, 8) || "-" || substr(GroupID, 9, 4) || "-" || substr(GroupID, 13, 4) || "-" || substr(GroupID, 17, 4) || "-" || substr(GroupID, 21, 12) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = substr(LastOwnerID, 1, 8) || "-" || substr(LastOwnerID, 9, 4) || "-" || substr(LastOwnerID, 13, 4) || "-" || substr(LastOwnerID, 17, 4) || "-" || substr(LastOwnerID, 21, 12) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + + +update land + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update land + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = substr(OwnerUUID, 1, 8) || "-" || substr(OwnerUUID, 9, 4) || "-" || substr(OwnerUUID, 13, 4) || "-" || substr(OwnerUUID, 17, 4) || "-" || substr(OwnerUUID, 21, 12) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = substr(GroupUUID, 1, 8) || "-" || substr(GroupUUID, 9, 4) || "-" || substr(GroupUUID, 13, 4) || "-" || substr(GroupUUID, 17, 4) || "-" || substr(GroupUUID, 21, 12) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = substr(MediaTextureUUID, 1, 8) || "-" || substr(MediaTextureUUID, 9, 4) || "-" || substr(MediaTextureUUID, 13, 4) || "-" || substr(MediaTextureUUID, 17, 4) || "-" || substr(MediaTextureUUID, 21, 12) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = substr(SnapshotUUID, 1, 8) || "-" || substr(SnapshotUUID, 9, 4) || "-" || substr(SnapshotUUID, 13, 4) || "-" || substr(SnapshotUUID, 17, 4) || "-" || substr(SnapshotUUID, 21, 12) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = substr(AuthbuyerID, 1, 8) || "-" || substr(AuthbuyerID, 9, 4) || "-" || substr(AuthbuyerID, 13, 4) || "-" || substr(AuthbuyerID, 17, 4) || "-" || substr(AuthbuyerID, 21, 12) + where AuthbuyerID not like '%-%'; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_UserAccount.sql b/OpenSim/Data/SQLite/Resources/UserAccount.migrations similarity index 51% rename from OpenSim/Data/SQLite/Resources/001_UserAccount.sql rename to OpenSim/Data/SQLite/Resources/UserAccount.migrations index c38d9a762f..854fe694c2 100644 --- a/OpenSim/Data/SQLite/Resources/001_UserAccount.sql +++ b/OpenSim/Data/SQLite/Resources/UserAccount.migrations @@ -1,4 +1,6 @@ -BEGIN TRANSACTION; +:VERSION 1 + +BEGIN TRANSACTION; -- useraccounts table CREATE TABLE UserAccounts ( @@ -14,4 +16,12 @@ CREATE TABLE UserAccounts ( UserTitle varchar(64) NOT NULL DEFAULT '' ); -COMMIT; \ No newline at end of file +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, surname AS LastName, '' as Email, '' AS ServiceURLs, created as Created FROM users; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/UserStore.migrations b/OpenSim/Data/SQLite/Resources/UserStore.migrations new file mode 100644 index 0000000000..73d35e83c3 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/UserStore.migrations @@ -0,0 +1,169 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +-- users table +CREATE TABLE users( + UUID varchar(255) primary key, + username varchar(255), + surname varchar(255), + passwordHash varchar(255), + passwordSalt varchar(255), + homeRegionX integer, + homeRegionY integer, + homeLocationX float, + homeLocationY float, + homeLocationZ float, + homeLookAtX float, + homeLookAtY float, + homeLookAtZ float, + created integer, + lastLogin integer, + rootInventoryFolderID varchar(255), + userInventoryURI varchar(255), + userAssetURI varchar(255), + profileCanDoMask integer, + profileWantDoMask integer, + profileAboutText varchar(255), + profileFirstText varchar(255), + profileImage varchar(255), + profileFirstImage varchar(255), + webLoginKey text default '00000000-0000-0000-0000-000000000000'); +-- friends table +CREATE TABLE userfriends( + ownerID varchar(255), + friendID varchar(255), + friendPerms integer, + ownerPerms integer, + datetimestamp integer); + +COMMIT; + +:VERSION 2 + +BEGIN; + +ALTER TABLE users add homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 + +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `attachpoint` int(11) NOT NULL DEFAULT 0, `item` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `asset` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'); + +COMMIT; + +:VERSION 6 + +BEGIN TRANSACTION; + +-- usersagents table +CREATE TABLE IF NOT EXISTS useragents( + UUID varchar(255) primary key, + agentIP varchar(255), + agentPort integer, + agentOnline boolean, + sessionID varchar(255), + secureSessionID varchar(255), + regionID varchar(255), + loginTime integer, + logoutTime integer, + currentRegion varchar(255), + currentHandle varchar(255), + currentPosX float, + currentPosY float, + currentPosZ float); + +COMMIT; + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE useragents add currentLookAtX float not null default 128; +ALTER TABLE useragents add currentLookAtY float not null default 128; +ALTER TABLE useragents add currentLookAtZ float not null default 70; + +COMMIT; + +:VERSION 8 + +BEGIN TRANSACTION; + +ALTER TABLE users add email varchar(250); + +COMMIT; + +:VERSION 9 + +BEGIN; + +update users + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update useragents + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; + +:VERSION 10 + +BEGIN TRANSACTION; + +CREATE TABLE IF NOT EXISTS avatarappearance( + Owner varchar(36) NOT NULL primary key, + BodyItem varchar(36) DEFAULT NULL, + BodyAsset varchar(36) DEFAULT NULL, + SkinItem varchar(36) DEFAULT NULL, + SkinAsset varchar(36) DEFAULT NULL, + HairItem varchar(36) DEFAULT NULL, + HairAsset varchar(36) DEFAULT NULL, + EyesItem varchar(36) DEFAULT NULL, + EyesAsset varchar(36) DEFAULT NULL, + ShirtItem varchar(36) DEFAULT NULL, + ShirtAsset varchar(36) DEFAULT NULL, + PantsItem varchar(36) DEFAULT NULL, + PantsAsset varchar(36) DEFAULT NULL, + ShoesItem varchar(36) DEFAULT NULL, + ShoesAsset varchar(36) DEFAULT NULL, + SocksItem varchar(36) DEFAULT NULL, + SocksAsset varchar(36) DEFAULT NULL, + JacketItem varchar(36) DEFAULT NULL, + JacketAsset varchar(36) DEFAULT NULL, + GlovesItem varchar(36) DEFAULT NULL, + GlovesAsset varchar(36) DEFAULT NULL, + UnderShirtItem varchar(36) DEFAULT NULL, + UnderShirtAsset varchar(36) DEFAULT NULL, + UnderPantsItem varchar(36) DEFAULT NULL, + UnderPantsAsset varchar(36) DEFAULT NULL, + SkirtItem varchar(36) DEFAULT NULL, + SkirtAsset varchar(36) DEFAULT NULL, + Texture blob, + VisualParams blob, + Serial int DEFAULT NULL, + AvatarHeight float DEFAULT NULL +); + +COMMIT; From dfeb9a0b5c07a85ec8aed591206468cee83ce637 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 6 May 2010 23:16:36 +0300 Subject: [PATCH 134/260] MS SQL migrations converted to the new format --- .../Data/MSSQL/Resources/001_AssetStore.sql | 13 - .../Data/MSSQL/Resources/001_EstateStore.sql | 85 -- .../Data/MSSQL/Resources/001_GridStore.sql | 37 - .../MSSQL/Resources/001_InventoryStore.sql | 64 -- .../Data/MSSQL/Resources/001_RegionStore.sql | 161 --- .../Data/MSSQL/Resources/001_UserAccount.sql | 14 - .../Data/MSSQL/Resources/001_UserStore.sql | 112 --- .../Data/MSSQL/Resources/002_AssetStore.sql | 29 - .../Data/MSSQL/Resources/002_AuthStore.sql | 6 - .../Data/MSSQL/Resources/002_EstateStore.sql | 25 - .../Data/MSSQL/Resources/002_FriendsStore.sql | 6 - .../Data/MSSQL/Resources/002_GridStore.sql | 49 - .../MSSQL/Resources/002_InventoryStore.sql | 5 - .../Data/MSSQL/Resources/002_RegionStore.sql | 50 - .../Data/MSSQL/Resources/002_UserAccount.sql | 12 - .../Data/MSSQL/Resources/002_UserStore.sql | 9 - .../Data/MSSQL/Resources/003_AssetStore.sql | 6 - .../Data/MSSQL/Resources/003_EstateStore.sql | 25 - .../Data/MSSQL/Resources/003_GridStore.sql | 22 - .../MSSQL/Resources/003_InventoryStore.sql | 38 - .../Data/MSSQL/Resources/003_RegionStore.sql | 67 -- .../Data/MSSQL/Resources/003_UserAccount.sql | 9 - .../Data/MSSQL/Resources/003_UserStore.sql | 15 - .../Data/MSSQL/Resources/004_AssetStore.sql | 31 - .../Data/MSSQL/Resources/004_EstateStore.sql | 22 - .../Data/MSSQL/Resources/004_GridStore.sql | 68 -- .../MSSQL/Resources/004_InventoryStore.sql | 52 - .../Data/MSSQL/Resources/004_RegionStore.sql | 40 - .../Data/MSSQL/Resources/004_UserAccount.sql | 7 - .../Data/MSSQL/Resources/004_UserStore.sql | 29 - .../Data/MSSQL/Resources/005_AssetStore.sql | 1 - .../Data/MSSQL/Resources/005_EstateStore.sql | 22 - .../Data/MSSQL/Resources/005_GridStore.sql | 5 - .../Data/MSSQL/Resources/005_RegionStore.sql | 49 - .../Data/MSSQL/Resources/005_UserStore.sql | 5 - .../Data/MSSQL/Resources/006_EstateStore.sql | 22 - .../Data/MSSQL/Resources/006_GridStore.sql | 8 - .../Data/MSSQL/Resources/006_RegionStore.sql | 36 - .../Data/MSSQL/Resources/006_UserStore.sql | 57 -- .../Data/MSSQL/Resources/007_EstateStore.sql | 25 - .../Data/MSSQL/Resources/007_GridStore.sql | 9 - .../Data/MSSQL/Resources/007_RegionStore.sql | 10 - .../Data/MSSQL/Resources/007_UserStore.sql | 42 - .../Data/MSSQL/Resources/008_EstateStore.sql | 49 - .../Data/MSSQL/Resources/008_RegionStore.sql | 7 - .../Data/MSSQL/Resources/008_UserStore.sql | 29 - .../Data/MSSQL/Resources/009_EstateStore.sql | 24 - .../Data/MSSQL/Resources/009_RegionStore.sql | 5 - .../Data/MSSQL/Resources/009_UserStore.sql | 53 - .../Data/MSSQL/Resources/010_RegionStore.sql | 7 - .../Data/MSSQL/Resources/010_UserStore.sql | 24 - .../Data/MSSQL/Resources/011_RegionStore.sql | 6 - .../Data/MSSQL/Resources/011_UserStore.sql | 5 - .../Data/MSSQL/Resources/012_RegionStore.sql | 5 - .../Data/MSSQL/Resources/013_RegionStore.sql | 112 --- .../Data/MSSQL/Resources/014_RegionStore.sql | 49 - .../Data/MSSQL/Resources/015_RegionStore.sql | 45 - .../Data/MSSQL/Resources/016_RegionStore.sql | 19 - .../Data/MSSQL/Resources/017_RegionStore.sql | 56 -- .../Data/MSSQL/Resources/018_RegionStore.sql | 18 - .../Data/MSSQL/Resources/019_RegionStore.sql | 19 - .../Data/MSSQL/Resources/020_RegionStore.sql | 58 -- .../Data/MSSQL/Resources/021_RegionStore.sql | 5 - .../Data/MSSQL/Resources/022_RegionStore.sql | 7 - .../Data/MSSQL/Resources/023_RegionStore.sql | 7 - .../MSSQL/Resources/AssetStore.migrations | 100 ++ ...001_AuthStore.sql => AuthStore.migrations} | 11 + .../{001_Avatar.sql => Avatar.migrations} | 2 + .../MSSQL/Resources/EstateStore.migrations | 334 +++++++ ...iendsStore.sql => FriendsStore.migrations} | 9 + .../Data/MSSQL/Resources/GridStore.migrations | 225 +++++ .../MSSQL/Resources/InventoryStore.migrations | 174 ++++ .../{001_LogStore.sql => LogStore.migrations} | 2 + .../{001_Presence.sql => Presence.migrations} | 11 + .../MSSQL/Resources/RegionStore.migrations | 929 ++++++++++++++++++ .../MSSQL/Resources/UserAccount.migrations | 55 ++ .../Data/MSSQL/Resources/UserStore.migrations | 421 ++++++++ 77 files changed, 2273 insertions(+), 2008 deletions(-) delete mode 100644 OpenSim/Data/MSSQL/Resources/001_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_AuthStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/008_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/008_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/008_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/009_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/009_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/009_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/010_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/010_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/011_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/011_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/012_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/013_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/014_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/015_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/016_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/017_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/018_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/019_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/020_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/021_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/022_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/023_RegionStore.sql create mode 100644 OpenSim/Data/MSSQL/Resources/AssetStore.migrations rename OpenSim/Data/MSSQL/Resources/{001_AuthStore.sql => AuthStore.migrations} (63%) rename OpenSim/Data/MSSQL/Resources/{001_Avatar.sql => Avatar.migrations} (94%) create mode 100644 OpenSim/Data/MSSQL/Resources/EstateStore.migrations rename OpenSim/Data/MSSQL/Resources/{001_FriendsStore.sql => FriendsStore.migrations} (54%) create mode 100644 OpenSim/Data/MSSQL/Resources/GridStore.migrations create mode 100644 OpenSim/Data/MSSQL/Resources/InventoryStore.migrations rename OpenSim/Data/MSSQL/Resources/{001_LogStore.sql => LogStore.migrations} (96%) rename OpenSim/Data/MSSQL/Resources/{001_Presence.sql => Presence.migrations} (81%) create mode 100644 OpenSim/Data/MSSQL/Resources/RegionStore.migrations create mode 100644 OpenSim/Data/MSSQL/Resources/UserAccount.migrations create mode 100644 OpenSim/Data/MSSQL/Resources/UserStore.migrations diff --git a/OpenSim/Data/MSSQL/Resources/001_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/001_AssetStore.sql deleted file mode 100644 index 2b293c7636..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_AssetStore.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE [assets] ( - [id] [varchar](36) NOT NULL, - [name] [varchar](64) NOT NULL, - [description] [varchar](64) NOT NULL, - [assetType] [tinyint] NOT NULL, - [local] [tinyint] NOT NULL, - [temporary] [tinyint] NOT NULL, - [data] [image] NOT NULL, -PRIMARY KEY CLUSTERED -( - [id] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] diff --git a/OpenSim/Data/MSSQL/Resources/001_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/001_EstateStore.sql deleted file mode 100644 index 9bb2f75965..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_EstateStore.sql +++ /dev/null @@ -1,85 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [dbo].[estate_managers]( - [EstateID] [int] NOT NULL, - [uuid] [varchar](36) NOT NULL, - CONSTRAINT [PK_estate_managers] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - -CREATE TABLE [dbo].[estate_groups]( - [EstateID] [int] NOT NULL, - [uuid] [varchar](36) NOT NULL, - CONSTRAINT [PK_estate_groups] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - - -CREATE TABLE [dbo].[estate_users]( - [EstateID] [int] NOT NULL, - [uuid] [varchar](36) NOT NULL, - CONSTRAINT [PK_estate_users] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - - -CREATE TABLE [dbo].[estateban]( - [EstateID] [int] NOT NULL, - [bannedUUID] [varchar](36) NOT NULL, - [bannedIp] [varchar](16) NOT NULL, - [bannedIpHostMask] [varchar](16) NOT NULL, - [bannedNameMask] [varchar](64) NULL DEFAULT (NULL), - CONSTRAINT [PK_estateban] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - -CREATE TABLE [dbo].[estate_settings]( - [EstateID] [int] IDENTITY(1,100) NOT NULL, - [EstateName] [varchar](64) NULL DEFAULT (NULL), - [AbuseEmailToEstateOwner] [bit] NOT NULL, - [DenyAnonymous] [bit] NOT NULL, - [ResetHomeOnTeleport] [bit] NOT NULL, - [FixedSun] [bit] NOT NULL, - [DenyTransacted] [bit] NOT NULL, - [BlockDwell] [bit] NOT NULL, - [DenyIdentified] [bit] NOT NULL, - [AllowVoice] [bit] NOT NULL, - [UseGlobalTime] [bit] NOT NULL, - [PricePerMeter] [int] NOT NULL, - [TaxFree] [bit] NOT NULL, - [AllowDirectTeleport] [bit] NOT NULL, - [RedirectGridX] [int] NOT NULL, - [RedirectGridY] [int] NOT NULL, - [ParentEstateID] [int] NOT NULL, - [SunPosition] [float] NOT NULL, - [EstateSkipScripts] [bit] NOT NULL, - [BillableFactor] [float] NOT NULL, - [PublicAccess] [bit] NOT NULL, - [AbuseEmail] [varchar](255) NOT NULL, - [EstateOwner] [varchar](36) NOT NULL, - [DenyMinors] [bit] NOT NULL, - CONSTRAINT [PK_estate_settings] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - - -CREATE TABLE [dbo].[estate_map]( - [RegionID] [varchar](36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - [EstateID] [int] NOT NULL, - CONSTRAINT [PK_estate_map] PRIMARY KEY CLUSTERED -( - [RegionID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/001_GridStore.sql b/OpenSim/Data/MSSQL/Resources/001_GridStore.sql deleted file mode 100644 index ff15f54c07..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_GridStore.sql +++ /dev/null @@ -1,37 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [dbo].[regions]( - [regionHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionName] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [uuid] [varchar](255) COLLATE Latin1_General_CI_AS NOT NULL, - [regionRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionSecret] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionDataURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverIP] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [locX] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [locY] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [locZ] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [eastOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [westOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [southOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [northOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionAssetURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionAssetRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionAssetSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionUserURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionUserRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionUserSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionMapTexture] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverHttpPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverRemotingPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [owner_uuid] [varchar](36) COLLATE Latin1_General_CI_AS NULL, -PRIMARY KEY CLUSTERED -( - [uuid] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql deleted file mode 100644 index 836d2d1e2b..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql +++ /dev/null @@ -1,64 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [inventoryfolders] ( - [folderID] [varchar](36) NOT NULL default '', - [agentID] [varchar](36) default NULL, - [parentFolderID] [varchar](36) default NULL, - [folderName] [varchar](64) default NULL, - [type] [smallint] NOT NULL default 0, - [version] [int] NOT NULL default 0, - PRIMARY KEY CLUSTERED -( - [folderID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [owner] ON [inventoryfolders] -( - [agentID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [parent] ON [inventoryfolders] -( - [parentFolderID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE TABLE [inventoryitems] ( - [inventoryID] [varchar](36) NOT NULL default '', - [assetID] [varchar](36) default NULL, - [assetType] [int] default NULL, - [parentFolderID] [varchar](36) default NULL, - [avatarID] [varchar](36) default NULL, - [inventoryName] [varchar](64) default NULL, - [inventoryDescription] [varchar](128) default NULL, - [inventoryNextPermissions] [int] default NULL, - [inventoryCurrentPermissions] [int] default NULL, - [invType] [int] default NULL, - [creatorID] [varchar](36) default NULL, - [inventoryBasePermissions] [int] NOT NULL default 0, - [inventoryEveryOnePermissions] [int] NOT NULL default 0, - [salePrice] [int] default NULL, - [saleType] [tinyint] default NULL, - [creationDate] [int] default NULL, - [groupID] [varchar](36) default NULL, - [groupOwned] [bit] default NULL, - [flags] [int] default NULL, - PRIMARY KEY CLUSTERED -( - [inventoryID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX [owner] ON [inventoryitems] -( - [avatarID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [folder] ON [inventoryitems] -( - [parentFolderID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/001_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/001_RegionStore.sql deleted file mode 100644 index fe7c58f8ed..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_RegionStore.sql +++ /dev/null @@ -1,161 +0,0 @@ -CREATE TABLE [dbo].[prims]( - [UUID] [varchar](255) NOT NULL, - [RegionUUID] [varchar](255) NULL, - [ParentID] [int] NULL, - [CreationDate] [int] NULL, - [Name] [varchar](255) NULL, - [SceneGroupID] [varchar](255) NULL, - [Text] [varchar](255) NULL, - [Description] [varchar](255) NULL, - [SitName] [varchar](255) NULL, - [TouchName] [varchar](255) NULL, - [ObjectFlags] [int] NULL, - [CreatorID] [varchar](255) NULL, - [OwnerID] [varchar](255) NULL, - [GroupID] [varchar](255) NULL, - [LastOwnerID] [varchar](255) NULL, - [OwnerMask] [int] NULL, - [NextOwnerMask] [int] NULL, - [GroupMask] [int] NULL, - [EveryoneMask] [int] NULL, - [BaseMask] [int] NULL, - [PositionX] [float] NULL, - [PositionY] [float] NULL, - [PositionZ] [float] NULL, - [GroupPositionX] [float] NULL, - [GroupPositionY] [float] NULL, - [GroupPositionZ] [float] NULL, - [VelocityX] [float] NULL, - [VelocityY] [float] NULL, - [VelocityZ] [float] NULL, - [AngularVelocityX] [float] NULL, - [AngularVelocityY] [float] NULL, - [AngularVelocityZ] [float] NULL, - [AccelerationX] [float] NULL, - [AccelerationY] [float] NULL, - [AccelerationZ] [float] NULL, - [RotationX] [float] NULL, - [RotationY] [float] NULL, - [RotationZ] [float] NULL, - [RotationW] [float] NULL, - [SitTargetOffsetX] [float] NULL, - [SitTargetOffsetY] [float] NULL, - [SitTargetOffsetZ] [float] NULL, - [SitTargetOrientW] [float] NULL, - [SitTargetOrientX] [float] NULL, - [SitTargetOrientY] [float] NULL, - [SitTargetOrientZ] [float] NULL, -PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -CREATE TABLE [dbo].[primshapes]( - [UUID] [varchar](255) NOT NULL, - [Shape] [int] NULL, - [ScaleX] [float] NULL, - [ScaleY] [float] NULL, - [ScaleZ] [float] NULL, - [PCode] [int] NULL, - [PathBegin] [int] NULL, - [PathEnd] [int] NULL, - [PathScaleX] [int] NULL, - [PathScaleY] [int] NULL, - [PathShearX] [int] NULL, - [PathShearY] [int] NULL, - [PathSkew] [int] NULL, - [PathCurve] [int] NULL, - [PathRadiusOffset] [int] NULL, - [PathRevolutions] [int] NULL, - [PathTaperX] [int] NULL, - [PathTaperY] [int] NULL, - [PathTwist] [int] NULL, - [PathTwistBegin] [int] NULL, - [ProfileBegin] [int] NULL, - [ProfileEnd] [int] NULL, - [ProfileCurve] [int] NULL, - [ProfileHollow] [int] NULL, - [State] [int] NULL, - [Texture] [image] NULL, - [ExtraParams] [image] NULL, -PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -CREATE TABLE [dbo].[primitems]( - [itemID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [primID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [assetID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [parentFolderID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [invType] [int] NULL, - [assetType] [int] NULL, - [name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [creationDate] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [creatorID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [ownerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [lastOwnerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [groupID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [nextPermissions] [int] NULL, - [currentPermissions] [int] NULL, - [basePermissions] [int] NULL, - [everyonePermissions] [int] NULL, - [groupPermissions] [int] NULL, -PRIMARY KEY CLUSTERED -( - [itemID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -CREATE TABLE [dbo].[terrain]( - [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [Revision] [int] NULL, - [Heightfield] [image] NULL -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -CREATE TABLE [dbo].[land]( - [UUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [LocalLandID] [int] NULL, - [Bitmap] [image] NULL, - [Name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [Description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [OwnerUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [IsGroupOwned] [int] NULL, - [Area] [int] NULL, - [AuctionID] [int] NULL, - [Category] [int] NULL, - [ClaimDate] [int] NULL, - [ClaimPrice] [int] NULL, - [GroupUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [SalePrice] [int] NULL, - [LandStatus] [int] NULL, - [LandFlags] [int] NULL, - [LandingType] [int] NULL, - [MediaAutoScale] [int] NULL, - [MediaTextureUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [MediaURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [MusicURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [PassHours] [float] NULL, - [PassPrice] [int] NULL, - [SnapshotUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [UserLocationX] [float] NULL, - [UserLocationY] [float] NULL, - [UserLocationZ] [float] NULL, - [UserLookAtX] [float] NULL, - [UserLookAtY] [float] NULL, - [UserLookAtZ] [float] NULL, -PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -CREATE TABLE [dbo].[landaccesslist]( - [LandUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [AccessUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [Flags] [int] NULL -) ON [PRIMARY] \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/001_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/001_UserAccount.sql deleted file mode 100644 index 3dbf8a4925..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_UserAccount.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE [UserAccounts] ( - [PrincipalID] uniqueidentifier NOT NULL, - [ScopeID] uniqueidentifier NOT NULL, - [FirstName] [varchar](64) NOT NULL, - [LastName] [varchar](64) NOT NULL, - [Email] [varchar](64) NULL, - [ServiceURLs] [text] NULL, - [Created] [int] default NULL, - - PRIMARY KEY CLUSTERED -( - [PrincipalID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] diff --git a/OpenSim/Data/MSSQL/Resources/001_UserStore.sql b/OpenSim/Data/MSSQL/Resources/001_UserStore.sql deleted file mode 100644 index 160c457dad..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_UserStore.sql +++ /dev/null @@ -1,112 +0,0 @@ -CREATE TABLE [users] ( - [UUID] [varchar](36) NOT NULL default '', - [username] [varchar](32) NOT NULL, - [lastname] [varchar](32) NOT NULL, - [passwordHash] [varchar](32) NOT NULL, - [passwordSalt] [varchar](32) NOT NULL, - [homeRegion] [bigint] default NULL, - [homeLocationX] [float] default NULL, - [homeLocationY] [float] default NULL, - [homeLocationZ] [float] default NULL, - [homeLookAtX] [float] default NULL, - [homeLookAtY] [float] default NULL, - [homeLookAtZ] [float] default NULL, - [created] [int] NOT NULL, - [lastLogin] [int] NOT NULL, - [userInventoryURI] [varchar](255) default NULL, - [userAssetURI] [varchar](255) default NULL, - [profileCanDoMask] [int] default NULL, - [profileWantDoMask] [int] default NULL, - [profileAboutText] [ntext], - [profileFirstText] [ntext], - [profileImage] [varchar](36) default NULL, - [profileFirstImage] [varchar](36) default NULL, - [webLoginKey] [varchar](36) default NULL, - PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX [usernames] ON [users] -( - [username] ASC, - [lastname] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE TABLE [agents] ( - [UUID] [varchar](36) NOT NULL, - [sessionID] [varchar](36) NOT NULL, - [secureSessionID] [varchar](36) NOT NULL, - [agentIP] [varchar](16) NOT NULL, - [agentPort] [int] NOT NULL, - [agentOnline] [tinyint] NOT NULL, - [loginTime] [int] NOT NULL, - [logoutTime] [int] NOT NULL, - [currentRegion] [varchar](36) NOT NULL, - [currentHandle] [bigint] NOT NULL, - [currentPos] [varchar](64) NOT NULL, - PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX [session] ON [agents] -( - [sessionID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [ssession] ON [agents] -( - [secureSessionID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE TABLE [dbo].[userfriends]( - [ownerID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, - [friendID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, - [friendPerms] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [datetimestamp] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL -) ON [PRIMARY] - -CREATE TABLE [avatarappearance] ( - [Owner] [varchar](36) NOT NULL, - [Serial] int NOT NULL, - [Visual_Params] [image] NOT NULL, - [Texture] [image] NOT NULL, - [Avatar_Height] float NOT NULL, - [Body_Item] [varchar](36) NOT NULL, - [Body_Asset] [varchar](36) NOT NULL, - [Skin_Item] [varchar](36) NOT NULL, - [Skin_Asset] [varchar](36) NOT NULL, - [Hair_Item] [varchar](36) NOT NULL, - [Hair_Asset] [varchar](36) NOT NULL, - [Eyes_Item] [varchar](36) NOT NULL, - [Eyes_Asset] [varchar](36) NOT NULL, - [Shirt_Item] [varchar](36) NOT NULL, - [Shirt_Asset] [varchar](36) NOT NULL, - [Pants_Item] [varchar](36) NOT NULL, - [Pants_Asset] [varchar](36) NOT NULL, - [Shoes_Item] [varchar](36) NOT NULL, - [Shoes_Asset] [varchar](36) NOT NULL, - [Socks_Item] [varchar](36) NOT NULL, - [Socks_Asset] [varchar](36) NOT NULL, - [Jacket_Item] [varchar](36) NOT NULL, - [Jacket_Asset] [varchar](36) NOT NULL, - [Gloves_Item] [varchar](36) NOT NULL, - [Gloves_Asset] [varchar](36) NOT NULL, - [Undershirt_Item] [varchar](36) NOT NULL, - [Undershirt_Asset] [varchar](36) NOT NULL, - [Underpants_Item] [varchar](36) NOT NULL, - [Underpants_Asset] [varchar](36) NOT NULL, - [Skirt_Item] [varchar](36) NOT NULL, - [Skirt_Asset] [varchar](36) NOT NULL, - - PRIMARY KEY CLUSTERED ( - [Owner] - ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] diff --git a/OpenSim/Data/MSSQL/Resources/002_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/002_AssetStore.sql deleted file mode 100644 index 3e245432bf..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_AssetStore.sql +++ /dev/null @@ -1,29 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_assets - ( - id varchar(36) NOT NULL, - name varchar(64) NOT NULL, - description varchar(64) NOT NULL, - assetType tinyint NOT NULL, - local bit NOT NULL, - temporary bit NOT NULL, - data image NOT NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM assets) - EXEC('INSERT INTO Tmp_assets (id, name, description, assetType, local, temporary, data) - SELECT id, name, description, assetType, CONVERT(bit, local), CONVERT(bit, temporary), data FROM assets WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE assets - -EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' - -ALTER TABLE dbo.assets ADD CONSTRAINT - PK__assets__id PRIMARY KEY CLUSTERED - ( - id - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/002_AuthStore.sql b/OpenSim/Data/MSSQL/Resources/002_AuthStore.sql deleted file mode 100644 index daed955932..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_AuthStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey, accountType) SELECT [UUID] AS UUID, [passwordHash] AS passwordHash, [passwordSalt] AS passwordSalt, [webLoginKey] AS webLoginKey, 'UserAccount' as [accountType] FROM users; - - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/002_EstateStore.sql deleted file mode 100644 index 18c12c097c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_EstateStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE dbo.estate_managers DROP CONSTRAINT PK_estate_managers - -CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -ALTER TABLE dbo.estate_groups DROP CONSTRAINT PK_estate_groups - -CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -ALTER TABLE dbo.estate_users DROP CONSTRAINT PK_estate_users - -CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql b/OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql deleted file mode 100644 index e67d20e4b7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -INSERT INTO Friends (PrincipalID, Friend, Flags, Offered) SELECT [ownerID], [friendID], [friendPerms], 0 FROM userfriends; - - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_GridStore.sql b/OpenSim/Data/MSSQL/Resources/002_GridStore.sql deleted file mode 100644 index f5901cb527..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_GridStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_regions - ( - uuid varchar(36) COLLATE Latin1_General_CI_AS NOT NULL, - regionHandle bigint NULL, - regionName varchar(20) NULL, - regionRecvKey varchar(128) NULL, - regionSendKey varchar(128) NULL, - regionSecret varchar(128) NULL, - regionDataURI varchar(128) NULL, - serverIP varchar(64) NULL, - serverPort int NULL, - serverURI varchar(255) NULL, - locX int NULL, - locY int NULL, - locZ int NULL, - eastOverrideHandle bigint NULL, - westOverrideHandle bigint NULL, - southOverrideHandle bigint NULL, - northOverrideHandle bigint NULL, - regionAssetURI varchar(255) NULL, - regionAssetRecvKey varchar(128) NULL, - regionAssetSendKey varchar(128) NULL, - regionUserURI varchar(255) NULL, - regionUserRecvKey varchar(128) NULL, - regionUserSendKey varchar(128) NULL, - regionMapTexture varchar(36) NULL, - serverHttpPort int NULL, - serverRemotingPort int NULL, - owner_uuid varchar(36) NULL, - originUUID varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM regions) - EXEC('INSERT INTO Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid) - SELECT CONVERT(varchar(36), uuid), CONVERT(bigint, regionHandle), CONVERT(varchar(20), regionName), CONVERT(varchar(128), regionRecvKey), CONVERT(varchar(128), regionSendKey), CONVERT(varchar(128), regionSecret), CONVERT(varchar(128), regionDataURI), CONVERT(varchar(64), serverIP), CONVERT(int, serverPort), serverURI, CONVERT(int, locX), CONVERT(int, locY), CONVERT(int, locZ), CONVERT(bigint, eastOverrideHandle), CONVERT(bigint, westOverrideHandle), CONVERT(bigint, southOverrideHandle), CONVERT(bigint, northOverrideHandle), regionAssetURI, CONVERT(varchar(128), regionAssetRecvKey), CONVERT(varchar(128), regionAssetSendKey), regionUserURI, CONVERT(varchar(128), regionUserRecvKey), CONVERT(varchar(128), regionUserSendKey), CONVERT(varchar(36), regionMapTexture), CONVERT(int, serverHttpPort), CONVERT(int, serverRemotingPort), owner_uuid FROM regions') - -DROP TABLE regions - -EXECUTE sp_rename N'Tmp_regions', N'regions', 'OBJECT' - -ALTER TABLE regions ADD CONSTRAINT - PK__regions__uuid PRIMARY KEY CLUSTERED - ( - uuid - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql deleted file mode 100644 index bcc26b88c8..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE inventoryitems ADD inventoryGroupPermissions INTEGER NOT NULL default 0 - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/002_RegionStore.sql deleted file mode 100644 index 1801035206..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_RegionStore.sql +++ /dev/null @@ -1,50 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE regionban ( - [regionUUID] VARCHAR(36) NOT NULL, - [bannedUUID] VARCHAR(36) NOT NULL, - [bannedIp] VARCHAR(16) NOT NULL, - [bannedIpHostMask] VARCHAR(16) NOT NULL) - -create table [dbo].[regionsettings] ( - [regionUUID] [varchar](36) not null, - [block_terraform] [bit] not null, - [block_fly] [bit] not null, - [allow_damage] [bit] not null, - [restrict_pushing] [bit] not null, - [allow_land_resell] [bit] not null, - [allow_land_join_divide] [bit] not null, - [block_show_in_search] [bit] not null, - [agent_limit] [int] not null, - [object_bonus] [float] not null, - [maturity] [int] not null, - [disable_scripts] [bit] not null, - [disable_collisions] [bit] not null, - [disable_physics] [bit] not null, - [terrain_texture_1] [varchar](36) not null, - [terrain_texture_2] [varchar](36) not null, - [terrain_texture_3] [varchar](36) not null, - [terrain_texture_4] [varchar](36) not null, - [elevation_1_nw] [float] not null, - [elevation_2_nw] [float] not null, - [elevation_1_ne] [float] not null, - [elevation_2_ne] [float] not null, - [elevation_1_se] [float] not null, - [elevation_2_se] [float] not null, - [elevation_1_sw] [float] not null, - [elevation_2_sw] [float] not null, - [water_height] [float] not null, - [terrain_raise_limit] [float] not null, - [terrain_lower_limit] [float] not null, - [use_estate_sun] [bit] not null, - [fixed_sun] [bit] not null, - [sun_position] [float] not null, - [covenant] [varchar](36) default NULL, - [Sandbox] [bit] NOT NULL, -PRIMARY KEY CLUSTERED -( - [regionUUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/002_UserAccount.sql deleted file mode 100644 index 89d1f3495a..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_UserAccount.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN TRANSACTION - -INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT [UUID] AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, -username AS FirstName, -lastname AS LastName, -email as Email, ( -'AssetServerURI=' + -userAssetURI + ' InventoryServerURI=' + userInventoryURI + ' GatewayURI= HomeURI=') AS ServiceURLs, -created as Created FROM users; - - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_UserStore.sql b/OpenSim/Data/MSSQL/Resources/002_UserStore.sql deleted file mode 100644 index 402eddf15c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_UserStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE users ADD homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE users ADD userFlags int NOT NULL default 0; -ALTER TABLE users ADD godLevel int NOT NULL default 0; -ALTER TABLE users ADD customType varchar(32) not null default ''; -ALTER TABLE users ADD partner varchar(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/003_AssetStore.sql deleted file mode 100644 index 1434330739..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_AssetStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE assets add create_time integer default 0 -ALTER TABLE assets add access_time integer default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/003_EstateStore.sql deleted file mode 100644 index 120966ae1d..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_EstateStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estateban - ( - EstateID int NOT NULL, - bannedUUID varchar(36) NOT NULL, - bannedIp varchar(16) NULL, - bannedIpHostMask varchar(16) NULL, - bannedNameMask varchar(64) NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estateban) - EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) - SELECT EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban') - -DROP TABLE dbo.estateban - -EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_GridStore.sql b/OpenSim/Data/MSSQL/Resources/003_GridStore.sql deleted file mode 100644 index e080947665..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_GridStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions - ( - regionName - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions - ( - regionHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions - ( - eastOverrideHandle, - westOverrideHandle, - southOverrideHandle, - northOverrideHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql deleted file mode 100644 index 2f623ec911..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql +++ /dev/null @@ -1,38 +0,0 @@ -/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_inventoryfolders - ( - folderID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - agentID uniqueidentifier NULL DEFAULT (NULL), - parentFolderID uniqueidentifier NULL DEFAULT (NULL), - folderName varchar(64) NULL DEFAULT (NULL), - type smallint NOT NULL DEFAULT ((0)), - version int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.inventoryfolders) - EXEC('INSERT INTO dbo.Tmp_inventoryfolders (folderID, agentID, parentFolderID, folderName, type, version) - SELECT CONVERT(uniqueidentifier, folderID), CONVERT(uniqueidentifier, agentID), CONVERT(uniqueidentifier, parentFolderID), folderName, type, version FROM dbo.inventoryfolders WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.inventoryfolders - -EXECUTE sp_rename N'dbo.Tmp_inventoryfolders', N'inventoryfolders', 'OBJECT' - -ALTER TABLE dbo.inventoryfolders ADD CONSTRAINT - PK__inventor__C2FABFB3173876EA PRIMARY KEY CLUSTERED - ( - folderID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX owner ON dbo.inventoryfolders - ( - agentID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX parent ON dbo.inventoryfolders - ( - parentFolderID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/003_RegionStore.sql deleted file mode 100644 index a8f40c2a86..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_RegionStore.sql +++ /dev/null @@ -1,67 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_prims - ( - UUID varchar(36) NOT NULL, - RegionUUID varchar(36) NULL, - ParentID int NULL, - CreationDate int NULL, - Name varchar(255) NULL, - SceneGroupID varchar(36) NULL, - Text varchar(255) NULL, - Description varchar(255) NULL, - SitName varchar(255) NULL, - TouchName varchar(255) NULL, - ObjectFlags int NULL, - CreatorID varchar(36) NULL, - OwnerID varchar(36) NULL, - GroupID varchar(36) NULL, - LastOwnerID varchar(36) NULL, - OwnerMask int NULL, - NextOwnerMask int NULL, - GroupMask int NULL, - EveryoneMask int NULL, - BaseMask int NULL, - PositionX float(53) NULL, - PositionY float(53) NULL, - PositionZ float(53) NULL, - GroupPositionX float(53) NULL, - GroupPositionY float(53) NULL, - GroupPositionZ float(53) NULL, - VelocityX float(53) NULL, - VelocityY float(53) NULL, - VelocityZ float(53) NULL, - AngularVelocityX float(53) NULL, - AngularVelocityY float(53) NULL, - AngularVelocityZ float(53) NULL, - AccelerationX float(53) NULL, - AccelerationY float(53) NULL, - AccelerationZ float(53) NULL, - RotationX float(53) NULL, - RotationY float(53) NULL, - RotationZ float(53) NULL, - RotationW float(53) NULL, - SitTargetOffsetX float(53) NULL, - SitTargetOffsetY float(53) NULL, - SitTargetOffsetZ float(53) NULL, - SitTargetOrientW float(53) NULL, - SitTargetOrientX float(53) NULL, - SitTargetOrientY float(53) NULL, - SitTargetOrientZ float(53) NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.prims) - EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ) - SELECT CONVERT(varchar(36), UUID), CONVERT(varchar(36), RegionUUID), ParentID, CreationDate, Name, CONVERT(varchar(36), SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(varchar(36), CreatorID), CONVERT(varchar(36), OwnerID), CONVERT(varchar(36), GroupID), CONVERT(varchar(36), LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.prims - -EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' - -ALTER TABLE dbo.prims ADD CONSTRAINT - PK__prims__10566F31 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/003_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/003_UserAccount.sql deleted file mode 100644 index da0395b49c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_UserAccount.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN TRANSACTION - -CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); -CREATE INDEX Email ON UserAccounts(Email); -CREATE INDEX FirstName ON UserAccounts(FirstName); -CREATE INDEX LastName ON UserAccounts(LastName); -CREATE INDEX Name ON UserAccounts(FirstName,LastName); - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/003_UserStore.sql b/OpenSim/Data/MSSQL/Resources/003_UserStore.sql deleted file mode 100644 index cb507c9630..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_UserStore.sql +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [avatarattachments] ( - [UUID] varchar(36) NOT NULL - , [attachpoint] int NOT NULL - , [item] varchar(36) NOT NULL - , [asset] varchar(36) NOT NULL) - -CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/004_AssetStore.sql deleted file mode 100644 index 215cf3a14e..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_AssetStore.sql +++ /dev/null @@ -1,31 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_assets - ( - id uniqueidentifier NOT NULL, - name varchar(64) NOT NULL, - description varchar(64) NOT NULL, - assetType tinyint NOT NULL, - local bit NOT NULL, - temporary bit NOT NULL, - data image NOT NULL, - create_time int NULL, - access_time int NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.assets) - EXEC('INSERT INTO dbo.Tmp_assets (id, name, description, assetType, local, temporary, data, create_time, access_time) - SELECT CONVERT(uniqueidentifier, id), name, description, assetType, local, temporary, data, create_time, access_time FROM dbo.assets WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE assets - -EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' - -ALTER TABLE dbo.assets ADD CONSTRAINT - PK__assets__id PRIMARY KEY CLUSTERED - ( - id - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/004_EstateStore.sql deleted file mode 100644 index 0a132c110e..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_EstateStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_managers - ( - EstateID int NOT NULL, - uuid uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_managers) - EXEC('INSERT INTO dbo.Tmp_estate_managers (EstateID, uuid) - SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_managers WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_managers - -EXECUTE sp_rename N'dbo.Tmp_estate_managers', N'estate_managers', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_GridStore.sql b/OpenSim/Data/MSSQL/Resources/004_GridStore.sql deleted file mode 100644 index 6456c95f83..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_GridStore.sql +++ /dev/null @@ -1,68 +0,0 @@ -/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_regions - ( - uuid uniqueidentifier NOT NULL, - regionHandle bigint NULL, - regionName varchar(20) NULL, - regionRecvKey varchar(128) NULL, - regionSendKey varchar(128) NULL, - regionSecret varchar(128) NULL, - regionDataURI varchar(128) NULL, - serverIP varchar(64) NULL, - serverPort int NULL, - serverURI varchar(255) NULL, - locX int NULL, - locY int NULL, - locZ int NULL, - eastOverrideHandle bigint NULL, - westOverrideHandle bigint NULL, - southOverrideHandle bigint NULL, - northOverrideHandle bigint NULL, - regionAssetURI varchar(255) NULL, - regionAssetRecvKey varchar(128) NULL, - regionAssetSendKey varchar(128) NULL, - regionUserURI varchar(255) NULL, - regionUserRecvKey varchar(128) NULL, - regionUserSendKey varchar(128) NULL, - regionMapTexture uniqueidentifier NULL, - serverHttpPort int NULL, - serverRemotingPort int NULL, - owner_uuid uniqueidentifier NOT NULL, - originUUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.regions) - EXEC('INSERT INTO dbo.Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid, originUUID) - SELECT CONVERT(uniqueidentifier, uuid), regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, CONVERT(uniqueidentifier, regionMapTexture), serverHttpPort, serverRemotingPort, CONVERT(uniqueidentifier, owner_uuid), CONVERT(uniqueidentifier, originUUID) FROM dbo.regions WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.regions - -EXECUTE sp_rename N'dbo.Tmp_regions', N'regions', 'OBJECT' - -ALTER TABLE dbo.regions ADD CONSTRAINT - PK__regions__uuid PRIMARY KEY CLUSTERED - ( - uuid - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions - ( - regionName - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions - ( - regionHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions - ( - eastOverrideHandle, - westOverrideHandle, - southOverrideHandle, - northOverrideHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql deleted file mode 100644 index 96ef1c0c90..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql +++ /dev/null @@ -1,52 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_inventoryitems - ( - inventoryID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - assetID uniqueidentifier NULL DEFAULT (NULL), - assetType int NULL DEFAULT (NULL), - parentFolderID uniqueidentifier NULL DEFAULT (NULL), - avatarID uniqueidentifier NULL DEFAULT (NULL), - inventoryName varchar(64) NULL DEFAULT (NULL), - inventoryDescription varchar(128) NULL DEFAULT (NULL), - inventoryNextPermissions int NULL DEFAULT (NULL), - inventoryCurrentPermissions int NULL DEFAULT (NULL), - invType int NULL DEFAULT (NULL), - creatorID uniqueidentifier NULL DEFAULT (NULL), - inventoryBasePermissions int NOT NULL DEFAULT ((0)), - inventoryEveryOnePermissions int NOT NULL DEFAULT ((0)), - salePrice int NULL DEFAULT (NULL), - saleType tinyint NULL DEFAULT (NULL), - creationDate int NULL DEFAULT (NULL), - groupID uniqueidentifier NULL DEFAULT (NULL), - groupOwned bit NULL DEFAULT (NULL), - flags int NULL DEFAULT (NULL), - inventoryGroupPermissions int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.inventoryitems) - EXEC('INSERT INTO dbo.Tmp_inventoryitems (inventoryID, assetID, assetType, parentFolderID, avatarID, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryGroupPermissions) - SELECT CONVERT(uniqueidentifier, inventoryID), CONVERT(uniqueidentifier, assetID), assetType, CONVERT(uniqueidentifier, parentFolderID), CONVERT(uniqueidentifier, avatarID), inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, CONVERT(uniqueidentifier, creatorID), inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, CONVERT(uniqueidentifier, groupID), groupOwned, flags, inventoryGroupPermissions FROM dbo.inventoryitems WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.inventoryitems - -EXECUTE sp_rename N'dbo.Tmp_inventoryitems', N'inventoryitems', 'OBJECT' - -ALTER TABLE dbo.inventoryitems ADD CONSTRAINT - PK__inventor__C4B7BC2220C1E124 PRIMARY KEY CLUSTERED - ( - inventoryID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX owner ON dbo.inventoryitems - ( - avatarID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX folder ON dbo.inventoryitems - ( - parentFolderID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/004_RegionStore.sql deleted file mode 100644 index 15b39a7fcf..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_RegionStore.sql +++ /dev/null @@ -1,40 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_primitems - ( - itemID varchar(36) NOT NULL, - primID varchar(36) NULL, - assetID varchar(36) NULL, - parentFolderID varchar(36) NULL, - invType int NULL, - assetType int NULL, - name varchar(255) NULL, - description varchar(255) NULL, - creationDate varchar(255) NULL, - creatorID varchar(36) NULL, - ownerID varchar(36) NULL, - lastOwnerID varchar(36) NULL, - groupID varchar(36) NULL, - nextPermissions int NULL, - currentPermissions int NULL, - basePermissions int NULL, - everyonePermissions int NULL, - groupPermissions int NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM primitems) - EXEC('INSERT INTO Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions) - SELECT CONVERT(varchar(36), itemID), CONVERT(varchar(36), primID), CONVERT(varchar(36), assetID), CONVERT(varchar(36), parentFolderID), invType, assetType, name, description, creationDate, CONVERT(varchar(36), creatorID), CONVERT(varchar(36), ownerID), CONVERT(varchar(36), lastOwnerID), CONVERT(varchar(36), groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions') - -DROP TABLE primitems - -EXECUTE sp_rename N'Tmp_primitems', N'primitems', 'OBJECT' - -ALTER TABLE primitems ADD CONSTRAINT - PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED - ( - itemID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/004_UserAccount.sql deleted file mode 100644 index a9a9021cc7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_UserAccount.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE UserAccounts ADD UserLevel integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD UserFlags integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD UserTitle varchar(64) NOT NULL DEFAULT ''; - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/004_UserStore.sql b/OpenSim/Data/MSSQL/Resources/004_UserStore.sql deleted file mode 100644 index 08f1a1d182..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_UserStore.sql +++ /dev/null @@ -1,29 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_userfriends - ( - ownerID varchar(36) NOT NULL, - friendID varchar(36) NOT NULL, - friendPerms int NOT NULL, - datetimestamp int NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM userfriends) - EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) - SELECT CONVERT(varchar(36), ownerID), CONVERT(varchar(36), friendID), CONVERT(int, friendPerms), CONVERT(int, datetimestamp) FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.userfriends - -EXECUTE sp_rename N'Tmp_userfriends', N'userfriends', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON userfriends - ( - ownerID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON userfriends - ( - friendID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/005_AssetStore.sql deleted file mode 100644 index 4e95b2b693..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_AssetStore.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; diff --git a/OpenSim/Data/MSSQL/Resources/005_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/005_EstateStore.sql deleted file mode 100644 index ba93b39ff4..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_EstateStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_groups - ( - EstateID int NOT NULL, - uuid uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_groups) - EXEC('INSERT INTO dbo.Tmp_estate_groups (EstateID, uuid) - SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_groups WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_groups - -EXECUTE sp_rename N'dbo.Tmp_estate_groups', N'estate_groups', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_GridStore.sql b/OpenSim/Data/MSSQL/Resources/005_GridStore.sql deleted file mode 100644 index aa04a33019..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_GridStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regions ADD access int default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/005_RegionStore.sql deleted file mode 100644 index eb0862c9bd..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_RegionStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_primshapes - ( - UUID varchar(36) NOT NULL, - Shape int NULL, - ScaleX float(53) NULL, - ScaleY float(53) NULL, - ScaleZ float(53) NULL, - PCode int NULL, - PathBegin int NULL, - PathEnd int NULL, - PathScaleX int NULL, - PathScaleY int NULL, - PathShearX int NULL, - PathShearY int NULL, - PathSkew int NULL, - PathCurve int NULL, - PathRadiusOffset int NULL, - PathRevolutions int NULL, - PathTaperX int NULL, - PathTaperY int NULL, - PathTwist int NULL, - PathTwistBegin int NULL, - ProfileBegin int NULL, - ProfileEnd int NULL, - ProfileCurve int NULL, - ProfileHollow int NULL, - State int NULL, - Texture image NULL, - ExtraParams image NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM primshapes) - EXEC('INSERT INTO Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) - SELECT CONVERT(varchar(36), UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM primshapes WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE primshapes - -EXECUTE sp_rename N'Tmp_primshapes', N'primshapes', 'OBJECT' - -ALTER TABLE primshapes ADD CONSTRAINT - PK__primshapes__0880433F PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_UserStore.sql b/OpenSim/Data/MSSQL/Resources/005_UserStore.sql deleted file mode 100644 index 1b6ab8f7da..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - - ALTER TABLE users add email varchar(250); - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/006_EstateStore.sql deleted file mode 100644 index f7df8fda18..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_EstateStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_users - ( - EstateID int NOT NULL, - uuid uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_users) - EXEC('INSERT INTO dbo.Tmp_estate_users (EstateID, uuid) - SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_users WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_users - -EXECUTE sp_rename N'dbo.Tmp_estate_users', N'estate_users', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_GridStore.sql b/OpenSim/Data/MSSQL/Resources/006_GridStore.sql deleted file mode 100644 index 42010ce657..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_GridStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regions ADD scopeid uniqueidentifier default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE regions ADD DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [owner_uuid]; -ALTER TABLE regions ADD sizeX integer not null default 0; -ALTER TABLE regions ADD sizeY integer not null default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/006_RegionStore.sql deleted file mode 100644 index 0419c0c8a6..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_RegionStore.sql +++ /dev/null @@ -1,36 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD PayPrice int not null default 0 -ALTER TABLE prims ADD PayButton1 int not null default 0 -ALTER TABLE prims ADD PayButton2 int not null default 0 -ALTER TABLE prims ADD PayButton3 int not null default 0 -ALTER TABLE prims ADD PayButton4 int not null default 0 -ALTER TABLE prims ADD LoopedSound varchar(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD LoopedSoundGain float not null default 0.0; -ALTER TABLE prims ADD TextureAnimation image -ALTER TABLE prims ADD OmegaX float not null default 0.0 -ALTER TABLE prims ADD OmegaY float not null default 0.0 -ALTER TABLE prims ADD OmegaZ float not null default 0.0 -ALTER TABLE prims ADD CameraEyeOffsetX float not null default 0.0 -ALTER TABLE prims ADD CameraEyeOffsetY float not null default 0.0 -ALTER TABLE prims ADD CameraEyeOffsetZ float not null default 0.0 -ALTER TABLE prims ADD CameraAtOffsetX float not null default 0.0 -ALTER TABLE prims ADD CameraAtOffsetY float not null default 0.0 -ALTER TABLE prims ADD CameraAtOffsetZ float not null default 0.0 -ALTER TABLE prims ADD ForceMouselook tinyint not null default 0 -ALTER TABLE prims ADD ScriptAccessPin int not null default 0 -ALTER TABLE prims ADD AllowedDrop tinyint not null default 0 -ALTER TABLE prims ADD DieAtEdge tinyint not null default 0 -ALTER TABLE prims ADD SalePrice int not null default 10 -ALTER TABLE prims ADD SaleType tinyint not null default 0 - -ALTER TABLE primitems add flags integer not null default 0 - -ALTER TABLE land ADD AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000' - -CREATE index prims_regionuuid on prims(RegionUUID) -CREATE index prims_parentid on prims(ParentID) - -CREATE index primitems_primid on primitems(primID) - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_UserStore.sql b/OpenSim/Data/MSSQL/Resources/006_UserStore.sql deleted file mode 100644 index 67fe5818a8..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_UserStore.sql +++ /dev/null @@ -1,57 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_users - ( - UUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - username varchar(32) NOT NULL, - lastname varchar(32) NOT NULL, - passwordHash varchar(32) NOT NULL, - passwordSalt varchar(32) NOT NULL, - homeRegion bigint NULL DEFAULT (NULL), - homeLocationX float(53) NULL DEFAULT (NULL), - homeLocationY float(53) NULL DEFAULT (NULL), - homeLocationZ float(53) NULL DEFAULT (NULL), - homeLookAtX float(53) NULL DEFAULT (NULL), - homeLookAtY float(53) NULL DEFAULT (NULL), - homeLookAtZ float(53) NULL DEFAULT (NULL), - created int NOT NULL, - lastLogin int NOT NULL, - userInventoryURI varchar(255) NULL DEFAULT (NULL), - userAssetURI varchar(255) NULL DEFAULT (NULL), - profileCanDoMask int NULL DEFAULT (NULL), - profileWantDoMask int NULL DEFAULT (NULL), - profileAboutText ntext NULL, - profileFirstText ntext NULL, - profileImage uniqueidentifier NULL DEFAULT (NULL), - profileFirstImage uniqueidentifier NULL DEFAULT (NULL), - webLoginKey uniqueidentifier NULL DEFAULT (NULL), - homeRegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - userFlags int NOT NULL DEFAULT ((0)), - godLevel int NOT NULL DEFAULT ((0)), - customType varchar(32) NOT NULL DEFAULT (''), - partner uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - email varchar(250) NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.users) - EXEC('INSERT INTO dbo.Tmp_users (UUID, username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, profileImage, profileFirstImage, webLoginKey, homeRegionID, userFlags, godLevel, customType, partner, email) - SELECT CONVERT(uniqueidentifier, UUID), username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, CONVERT(uniqueidentifier, profileImage), CONVERT(uniqueidentifier, profileFirstImage), CONVERT(uniqueidentifier, webLoginKey), CONVERT(uniqueidentifier, homeRegionID), userFlags, godLevel, customType, CONVERT(uniqueidentifier, partner), email FROM dbo.users WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.users - -EXECUTE sp_rename N'dbo.Tmp_users', N'users', 'OBJECT' - -ALTER TABLE dbo.users ADD CONSTRAINT - PK__users__65A475E737A5467C PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX usernames ON dbo.users - ( - username, - lastname - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/007_EstateStore.sql deleted file mode 100644 index c9165b0c6d..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_EstateStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estateban - ( - EstateID int NOT NULL, - bannedUUID uniqueidentifier NOT NULL, - bannedIp varchar(16) NULL, - bannedIpHostMask varchar(16) NULL, - bannedNameMask varchar(64) NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estateban) - EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) - SELECT EstateID, CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estateban - -EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_GridStore.sql b/OpenSim/Data/MSSQL/Resources/007_GridStore.sql deleted file mode 100644 index 0b66d40b47..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_GridStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regions ADD [flags] integer NOT NULL DEFAULT 0; -CREATE INDEX [flags] ON regions(flags); -ALTER TABLE [regions] ADD [last_seen] integer NOT NULL DEFAULT 0; -ALTER TABLE [regions] ADD [PrincipalID] uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; -ALTER TABLE [regions] ADD [Token] varchar(255) NOT NULL DEFAULT 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/007_RegionStore.sql deleted file mode 100644 index 684f937334..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_RegionStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD ColorR int not null default 0; -ALTER TABLE prims ADD ColorG int not null default 0; -ALTER TABLE prims ADD ColorB int not null default 0; -ALTER TABLE prims ADD ColorA int not null default 0; -ALTER TABLE prims ADD ParticleSystem IMAGE; -ALTER TABLE prims ADD ClickAction tinyint NOT NULL default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_UserStore.sql b/OpenSim/Data/MSSQL/Resources/007_UserStore.sql deleted file mode 100644 index 92a8fc572a..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_UserStore.sql +++ /dev/null @@ -1,42 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_agents - ( - UUID uniqueidentifier NOT NULL, - sessionID uniqueidentifier NOT NULL, - secureSessionID uniqueidentifier NOT NULL, - agentIP varchar(16) NOT NULL, - agentPort int NOT NULL, - agentOnline tinyint NOT NULL, - loginTime int NOT NULL, - logoutTime int NOT NULL, - currentRegion uniqueidentifier NOT NULL, - currentHandle bigint NOT NULL, - currentPos varchar(64) NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.agents) - EXEC('INSERT INTO dbo.Tmp_agents (UUID, sessionID, secureSessionID, agentIP, agentPort, agentOnline, loginTime, logoutTime, currentRegion, currentHandle, currentPos) - SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, sessionID), CONVERT(uniqueidentifier, secureSessionID), agentIP, agentPort, agentOnline, loginTime, logoutTime, CONVERT(uniqueidentifier, currentRegion), currentHandle, currentPos FROM dbo.agents WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.agents - -EXECUTE sp_rename N'dbo.Tmp_agents', N'agents', 'OBJECT' - -ALTER TABLE dbo.agents ADD CONSTRAINT - PK__agents__65A475E749C3F6B7 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX session ON dbo.agents - ( - sessionID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX ssession ON dbo.agents - ( - secureSessionID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/008_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/008_EstateStore.sql deleted file mode 100644 index 9c5355eac7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/008_EstateStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_settings - ( - EstateID int NOT NULL IDENTITY (1, 100), - EstateName varchar(64) NULL DEFAULT (NULL), - AbuseEmailToEstateOwner bit NOT NULL, - DenyAnonymous bit NOT NULL, - ResetHomeOnTeleport bit NOT NULL, - FixedSun bit NOT NULL, - DenyTransacted bit NOT NULL, - BlockDwell bit NOT NULL, - DenyIdentified bit NOT NULL, - AllowVoice bit NOT NULL, - UseGlobalTime bit NOT NULL, - PricePerMeter int NOT NULL, - TaxFree bit NOT NULL, - AllowDirectTeleport bit NOT NULL, - RedirectGridX int NOT NULL, - RedirectGridY int NOT NULL, - ParentEstateID int NOT NULL, - SunPosition float(53) NOT NULL, - EstateSkipScripts bit NOT NULL, - BillableFactor float(53) NOT NULL, - PublicAccess bit NOT NULL, - AbuseEmail varchar(255) NOT NULL, - EstateOwner uniqueidentifier NOT NULL, - DenyMinors bit NOT NULL - ) ON [PRIMARY] - -SET IDENTITY_INSERT dbo.Tmp_estate_settings ON - -IF EXISTS(SELECT * FROM dbo.estate_settings) - EXEC('INSERT INTO dbo.Tmp_estate_settings (EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, EstateOwner, DenyMinors) - SELECT EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, CONVERT(uniqueidentifier, EstateOwner), DenyMinors FROM dbo.estate_settings WITH (HOLDLOCK TABLOCKX)') - -SET IDENTITY_INSERT dbo.Tmp_estate_settings OFF - -DROP TABLE dbo.estate_settings - -EXECUTE sp_rename N'dbo.Tmp_estate_settings', N'estate_settings', 'OBJECT' - -ALTER TABLE dbo.estate_settings ADD CONSTRAINT - PK_estate_settings PRIMARY KEY CLUSTERED - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/008_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/008_RegionStore.sql deleted file mode 100644 index 87d6d80005..0000000000 --- a/OpenSim/Data/MSSQL/Resources/008_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE land ADD OtherCleanTime integer NOT NULL default 0; -ALTER TABLE land ADD Dwell integer NOT NULL default 0; - -COMMIT - diff --git a/OpenSim/Data/MSSQL/Resources/008_UserStore.sql b/OpenSim/Data/MSSQL/Resources/008_UserStore.sql deleted file mode 100644 index 505252ba42..0000000000 --- a/OpenSim/Data/MSSQL/Resources/008_UserStore.sql +++ /dev/null @@ -1,29 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_userfriends - ( - ownerID uniqueidentifier NOT NULL, - friendID uniqueidentifier NOT NULL, - friendPerms int NOT NULL, - datetimestamp int NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.userfriends) - EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) - SELECT CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, friendID), friendPerms, datetimestamp FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.userfriends - -EXECUTE sp_rename N'dbo.Tmp_userfriends', N'userfriends', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON dbo.userfriends - ( - ownerID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON dbo.userfriends - ( - friendID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/009_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/009_EstateStore.sql deleted file mode 100644 index f91557c805..0000000000 --- a/OpenSim/Data/MSSQL/Resources/009_EstateStore.sql +++ /dev/null @@ -1,24 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_map - ( - RegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - EstateID int NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_map) - EXEC('INSERT INTO dbo.Tmp_estate_map (RegionID, EstateID) - SELECT CONVERT(uniqueidentifier, RegionID), EstateID FROM dbo.estate_map WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_map - -EXECUTE sp_rename N'dbo.Tmp_estate_map', N'estate_map', 'OBJECT' - -ALTER TABLE dbo.estate_map ADD CONSTRAINT - PK_estate_map PRIMARY KEY CLUSTERED - ( - RegionID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/009_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/009_RegionStore.sql deleted file mode 100644 index 4ef3b3f125..0000000000 --- a/OpenSim/Data/MSSQL/Resources/009_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD Material tinyint NOT NULL default 3 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/009_UserStore.sql b/OpenSim/Data/MSSQL/Resources/009_UserStore.sql deleted file mode 100644 index b1ab8ba69b..0000000000 --- a/OpenSim/Data/MSSQL/Resources/009_UserStore.sql +++ /dev/null @@ -1,53 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_avatarappearance - ( - Owner uniqueidentifier NOT NULL, - Serial int NOT NULL, - Visual_Params image NOT NULL, - Texture image NOT NULL, - Avatar_Height float(53) NOT NULL, - Body_Item uniqueidentifier NOT NULL, - Body_Asset uniqueidentifier NOT NULL, - Skin_Item uniqueidentifier NOT NULL, - Skin_Asset uniqueidentifier NOT NULL, - Hair_Item uniqueidentifier NOT NULL, - Hair_Asset uniqueidentifier NOT NULL, - Eyes_Item uniqueidentifier NOT NULL, - Eyes_Asset uniqueidentifier NOT NULL, - Shirt_Item uniqueidentifier NOT NULL, - Shirt_Asset uniqueidentifier NOT NULL, - Pants_Item uniqueidentifier NOT NULL, - Pants_Asset uniqueidentifier NOT NULL, - Shoes_Item uniqueidentifier NOT NULL, - Shoes_Asset uniqueidentifier NOT NULL, - Socks_Item uniqueidentifier NOT NULL, - Socks_Asset uniqueidentifier NOT NULL, - Jacket_Item uniqueidentifier NOT NULL, - Jacket_Asset uniqueidentifier NOT NULL, - Gloves_Item uniqueidentifier NOT NULL, - Gloves_Asset uniqueidentifier NOT NULL, - Undershirt_Item uniqueidentifier NOT NULL, - Undershirt_Asset uniqueidentifier NOT NULL, - Underpants_Item uniqueidentifier NOT NULL, - Underpants_Asset uniqueidentifier NOT NULL, - Skirt_Item uniqueidentifier NOT NULL, - Skirt_Asset uniqueidentifier NOT NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.avatarappearance) - EXEC('INSERT INTO dbo.Tmp_avatarappearance (Owner, Serial, Visual_Params, Texture, Avatar_Height, Body_Item, Body_Asset, Skin_Item, Skin_Asset, Hair_Item, Hair_Asset, Eyes_Item, Eyes_Asset, Shirt_Item, Shirt_Asset, Pants_Item, Pants_Asset, Shoes_Item, Shoes_Asset, Socks_Item, Socks_Asset, Jacket_Item, Jacket_Asset, Gloves_Item, Gloves_Asset, Undershirt_Item, Undershirt_Asset, Underpants_Item, Underpants_Asset, Skirt_Item, Skirt_Asset) - SELECT CONVERT(uniqueidentifier, Owner), Serial, Visual_Params, Texture, Avatar_Height, CONVERT(uniqueidentifier, Body_Item), CONVERT(uniqueidentifier, Body_Asset), CONVERT(uniqueidentifier, Skin_Item), CONVERT(uniqueidentifier, Skin_Asset), CONVERT(uniqueidentifier, Hair_Item), CONVERT(uniqueidentifier, Hair_Asset), CONVERT(uniqueidentifier, Eyes_Item), CONVERT(uniqueidentifier, Eyes_Asset), CONVERT(uniqueidentifier, Shirt_Item), CONVERT(uniqueidentifier, Shirt_Asset), CONVERT(uniqueidentifier, Pants_Item), CONVERT(uniqueidentifier, Pants_Asset), CONVERT(uniqueidentifier, Shoes_Item), CONVERT(uniqueidentifier, Shoes_Asset), CONVERT(uniqueidentifier, Socks_Item), CONVERT(uniqueidentifier, Socks_Asset), CONVERT(uniqueidentifier, Jacket_Item), CONVERT(uniqueidentifier, Jacket_Asset), CONVERT(uniqueidentifier, Gloves_Item), CONVERT(uniqueidentifier, Gloves_Asset), CONVERT(uniqueidentifier, Undershirt_Item), CONVERT(uniqueidentifier, Undershirt_Asset), CONVERT(uniqueidentifier, Underpants_Item), CONVERT(uniqueidentifier, Underpants_Asset), CONVERT(uniqueidentifier, Skirt_Item), CONVERT(uniqueidentifier, Skirt_Asset) FROM dbo.avatarappearance WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.avatarappearance - -EXECUTE sp_rename N'dbo.Tmp_avatarappearance', N'avatarappearance', 'OBJECT' - -ALTER TABLE dbo.avatarappearance ADD CONSTRAINT - PK__avatarap__7DD115CC4E88ABD4 PRIMARY KEY CLUSTERED - ( - Owner - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/010_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/010_RegionStore.sql deleted file mode 100644 index 74ad9c2e28..0000000000 --- a/OpenSim/Data/MSSQL/Resources/010_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regionsettings ADD sunvectorx float NOT NULL default 0; -ALTER TABLE regionsettings ADD sunvectory float NOT NULL default 0; -ALTER TABLE regionsettings ADD sunvectorz float NOT NULL default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/010_UserStore.sql b/OpenSim/Data/MSSQL/Resources/010_UserStore.sql deleted file mode 100644 index 0af008aa13..0000000000 --- a/OpenSim/Data/MSSQL/Resources/010_UserStore.sql +++ /dev/null @@ -1,24 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_avatarattachments - ( - UUID uniqueidentifier NOT NULL, - attachpoint int NOT NULL, - item uniqueidentifier NOT NULL, - asset uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.avatarattachments) - EXEC('INSERT INTO dbo.Tmp_avatarattachments (UUID, attachpoint, item, asset) - SELECT CONVERT(uniqueidentifier, UUID), attachpoint, CONVERT(uniqueidentifier, item), CONVERT(uniqueidentifier, asset) FROM dbo.avatarattachments WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.avatarattachments - -EXECUTE sp_rename N'dbo.Tmp_avatarattachments', N'avatarattachments', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/011_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/011_RegionStore.sql deleted file mode 100644 index 14c71a3737..0000000000 --- a/OpenSim/Data/MSSQL/Resources/011_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000' -ALTER TABLE prims ADD CollisionSoundVolume float not null default 0.0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/011_UserStore.sql b/OpenSim/Data/MSSQL/Resources/011_UserStore.sql deleted file mode 100644 index 5aa064fac8..0000000000 --- a/OpenSim/Data/MSSQL/Resources/011_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE users ADD scopeID uniqueidentifier not null default '00000000-0000-0000-0000-000000000000' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/012_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/012_RegionStore.sql deleted file mode 100644 index eef8d9075c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/012_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD LinkNumber integer not null default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/013_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/013_RegionStore.sql deleted file mode 100644 index ef5d4c035c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/013_RegionStore.sql +++ /dev/null @@ -1,112 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_prims - ( - UUID uniqueidentifier NOT NULL, - RegionUUID uniqueidentifier NULL, - ParentID int NULL, - CreationDate int NULL, - Name varchar(255) NULL, - SceneGroupID uniqueidentifier NULL, - Text varchar(255) NULL, - Description varchar(255) NULL, - SitName varchar(255) NULL, - TouchName varchar(255) NULL, - ObjectFlags int NULL, - CreatorID uniqueidentifier NULL, - OwnerID uniqueidentifier NULL, - GroupID uniqueidentifier NULL, - LastOwnerID uniqueidentifier NULL, - OwnerMask int NULL, - NextOwnerMask int NULL, - GroupMask int NULL, - EveryoneMask int NULL, - BaseMask int NULL, - PositionX float(53) NULL, - PositionY float(53) NULL, - PositionZ float(53) NULL, - GroupPositionX float(53) NULL, - GroupPositionY float(53) NULL, - GroupPositionZ float(53) NULL, - VelocityX float(53) NULL, - VelocityY float(53) NULL, - VelocityZ float(53) NULL, - AngularVelocityX float(53) NULL, - AngularVelocityY float(53) NULL, - AngularVelocityZ float(53) NULL, - AccelerationX float(53) NULL, - AccelerationY float(53) NULL, - AccelerationZ float(53) NULL, - RotationX float(53) NULL, - RotationY float(53) NULL, - RotationZ float(53) NULL, - RotationW float(53) NULL, - SitTargetOffsetX float(53) NULL, - SitTargetOffsetY float(53) NULL, - SitTargetOffsetZ float(53) NULL, - SitTargetOrientW float(53) NULL, - SitTargetOrientX float(53) NULL, - SitTargetOrientY float(53) NULL, - SitTargetOrientZ float(53) NULL, - PayPrice int NOT NULL DEFAULT ((0)), - PayButton1 int NOT NULL DEFAULT ((0)), - PayButton2 int NOT NULL DEFAULT ((0)), - PayButton3 int NOT NULL DEFAULT ((0)), - PayButton4 int NOT NULL DEFAULT ((0)), - LoopedSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - LoopedSoundGain float(53) NOT NULL DEFAULT ((0.0)), - TextureAnimation image NULL, - OmegaX float(53) NOT NULL DEFAULT ((0.0)), - OmegaY float(53) NOT NULL DEFAULT ((0.0)), - OmegaZ float(53) NOT NULL DEFAULT ((0.0)), - CameraEyeOffsetX float(53) NOT NULL DEFAULT ((0.0)), - CameraEyeOffsetY float(53) NOT NULL DEFAULT ((0.0)), - CameraEyeOffsetZ float(53) NOT NULL DEFAULT ((0.0)), - CameraAtOffsetX float(53) NOT NULL DEFAULT ((0.0)), - CameraAtOffsetY float(53) NOT NULL DEFAULT ((0.0)), - CameraAtOffsetZ float(53) NOT NULL DEFAULT ((0.0)), - ForceMouselook tinyint NOT NULL DEFAULT ((0)), - ScriptAccessPin int NOT NULL DEFAULT ((0)), - AllowedDrop tinyint NOT NULL DEFAULT ((0)), - DieAtEdge tinyint NOT NULL DEFAULT ((0)), - SalePrice int NOT NULL DEFAULT ((10)), - SaleType tinyint NOT NULL DEFAULT ((0)), - ColorR int NOT NULL DEFAULT ((0)), - ColorG int NOT NULL DEFAULT ((0)), - ColorB int NOT NULL DEFAULT ((0)), - ColorA int NOT NULL DEFAULT ((0)), - ParticleSystem image NULL, - ClickAction tinyint NOT NULL DEFAULT ((0)), - Material tinyint NOT NULL DEFAULT ((3)), - CollisionSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - CollisionSoundVolume float(53) NOT NULL DEFAULT ((0.0)), - LinkNumber int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.prims) - EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, LoopedSound, LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, LinkNumber) - SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), ParentID, CreationDate, Name, CONVERT(uniqueidentifier, SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(uniqueidentifier, CreatorID), CONVERT(uniqueidentifier, OwnerID), CONVERT(uniqueidentifier, GroupID), CONVERT(uniqueidentifier, LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, CONVERT(uniqueidentifier, LoopedSound), LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CONVERT(uniqueidentifier, CollisionSound), CollisionSoundVolume, LinkNumber FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.prims - -EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' - -ALTER TABLE dbo.prims ADD CONSTRAINT - PK__prims__10566F31 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX prims_regionuuid ON dbo.prims - ( - RegionUUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX prims_parentid ON dbo.prims - ( - ParentID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/014_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/014_RegionStore.sql deleted file mode 100644 index 02f6f55b86..0000000000 --- a/OpenSim/Data/MSSQL/Resources/014_RegionStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_primshapes - ( - UUID uniqueidentifier NOT NULL, - Shape int NULL, - ScaleX float(53) NULL, - ScaleY float(53) NULL, - ScaleZ float(53) NULL, - PCode int NULL, - PathBegin int NULL, - PathEnd int NULL, - PathScaleX int NULL, - PathScaleY int NULL, - PathShearX int NULL, - PathShearY int NULL, - PathSkew int NULL, - PathCurve int NULL, - PathRadiusOffset int NULL, - PathRevolutions int NULL, - PathTaperX int NULL, - PathTaperY int NULL, - PathTwist int NULL, - PathTwistBegin int NULL, - ProfileBegin int NULL, - ProfileEnd int NULL, - ProfileCurve int NULL, - ProfileHollow int NULL, - State int NULL, - Texture image NULL, - ExtraParams image NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.primshapes) - EXEC('INSERT INTO dbo.Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) - SELECT CONVERT(uniqueidentifier, UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM dbo.primshapes WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.primshapes - -EXECUTE sp_rename N'dbo.Tmp_primshapes', N'primshapes', 'OBJECT' - -ALTER TABLE dbo.primshapes ADD CONSTRAINT - PK__primshapes__0880433F PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/015_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/015_RegionStore.sql deleted file mode 100644 index cbaaf88bb9..0000000000 --- a/OpenSim/Data/MSSQL/Resources/015_RegionStore.sql +++ /dev/null @@ -1,45 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_primitems - ( - itemID uniqueidentifier NOT NULL, - primID uniqueidentifier NULL, - assetID uniqueidentifier NULL, - parentFolderID uniqueidentifier NULL, - invType int NULL, - assetType int NULL, - name varchar(255) NULL, - description varchar(255) NULL, - creationDate varchar(255) NULL, - creatorID uniqueidentifier NULL, - ownerID uniqueidentifier NULL, - lastOwnerID uniqueidentifier NULL, - groupID uniqueidentifier NULL, - nextPermissions int NULL, - currentPermissions int NULL, - basePermissions int NULL, - everyonePermissions int NULL, - groupPermissions int NULL, - flags int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.primitems) - EXEC('INSERT INTO dbo.Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags) - SELECT CONVERT(uniqueidentifier, itemID), CONVERT(uniqueidentifier, primID), CONVERT(uniqueidentifier, assetID), CONVERT(uniqueidentifier, parentFolderID), invType, assetType, name, description, creationDate, CONVERT(uniqueidentifier, creatorID), CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, lastOwnerID), CONVERT(uniqueidentifier, groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags FROM dbo.primitems WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.primitems - -EXECUTE sp_rename N'dbo.Tmp_primitems', N'primitems', 'OBJECT' - -ALTER TABLE dbo.primitems ADD CONSTRAINT - PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED - ( - itemID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX primitems_primid ON dbo.primitems - ( - primID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/016_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/016_RegionStore.sql deleted file mode 100644 index e91da19b12..0000000000 --- a/OpenSim/Data/MSSQL/Resources/016_RegionStore.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_terrain - ( - RegionUUID uniqueidentifier NULL, - Revision int NULL, - Heightfield image NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.terrain) - EXEC('INSERT INTO dbo.Tmp_terrain (RegionUUID, Revision, Heightfield) - SELECT CONVERT(uniqueidentifier, RegionUUID), Revision, Heightfield FROM dbo.terrain WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.terrain - -EXECUTE sp_rename N'dbo.Tmp_terrain', N'terrain', 'OBJECT' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/017_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/017_RegionStore.sql deleted file mode 100644 index 3d3dbc0ee7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/017_RegionStore.sql +++ /dev/null @@ -1,56 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_land - ( - UUID uniqueidentifier NOT NULL, - RegionUUID uniqueidentifier NULL, - LocalLandID int NULL, - Bitmap image NULL, - Name varchar(255) NULL, - Description varchar(255) NULL, - OwnerUUID uniqueidentifier NULL, - IsGroupOwned int NULL, - Area int NULL, - AuctionID int NULL, - Category int NULL, - ClaimDate int NULL, - ClaimPrice int NULL, - GroupUUID uniqueidentifier NULL, - SalePrice int NULL, - LandStatus int NULL, - LandFlags int NULL, - LandingType int NULL, - MediaAutoScale int NULL, - MediaTextureUUID uniqueidentifier NULL, - MediaURL varchar(255) NULL, - MusicURL varchar(255) NULL, - PassHours float(53) NULL, - PassPrice int NULL, - SnapshotUUID uniqueidentifier NULL, - UserLocationX float(53) NULL, - UserLocationY float(53) NULL, - UserLocationZ float(53) NULL, - UserLookAtX float(53) NULL, - UserLookAtY float(53) NULL, - UserLookAtZ float(53) NULL, - AuthbuyerID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - OtherCleanTime int NOT NULL DEFAULT ((0)), - Dwell int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.land) - EXEC('INSERT INTO dbo.Tmp_land (UUID, RegionUUID, LocalLandID, Bitmap, Name, Description, OwnerUUID, IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, GroupUUID, SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, MediaTextureUUID, MediaURL, MusicURL, PassHours, PassPrice, SnapshotUUID, UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, AuthbuyerID, OtherCleanTime, Dwell) - SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), LocalLandID, Bitmap, Name, Description, CONVERT(uniqueidentifier, OwnerUUID), IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, CONVERT(uniqueidentifier, GroupUUID), SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, CONVERT(uniqueidentifier, MediaTextureUUID), MediaURL, MusicURL, PassHours, PassPrice, CONVERT(uniqueidentifier, SnapshotUUID), UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, CONVERT(uniqueidentifier, AuthbuyerID), OtherCleanTime, Dwell FROM dbo.land WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.land - -EXECUTE sp_rename N'dbo.Tmp_land', N'land', 'OBJECT' - -ALTER TABLE dbo.land ADD CONSTRAINT - PK__land__65A475E71BFD2C07 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/018_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/018_RegionStore.sql deleted file mode 100644 index 6157e3536e..0000000000 --- a/OpenSim/Data/MSSQL/Resources/018_RegionStore.sql +++ /dev/null @@ -1,18 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_landaccesslist - ( - LandUUID uniqueidentifier NULL, - AccessUUID uniqueidentifier NULL, - Flags int NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.landaccesslist) - EXEC('INSERT INTO dbo.Tmp_landaccesslist (LandUUID, AccessUUID, Flags) - SELECT CONVERT(uniqueidentifier, LandUUID), CONVERT(uniqueidentifier, AccessUUID), Flags FROM dbo.landaccesslist WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.landaccesslist - -EXECUTE sp_rename N'dbo.Tmp_landaccesslist', N'landaccesslist', 'OBJECT' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/019_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/019_RegionStore.sql deleted file mode 100644 index 8e613b9d09..0000000000 --- a/OpenSim/Data/MSSQL/Resources/019_RegionStore.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_regionban - ( - regionUUID uniqueidentifier NOT NULL, - bannedUUID uniqueidentifier NOT NULL, - bannedIp varchar(16) NOT NULL, - bannedIpHostMask varchar(16) NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.regionban) - EXEC('INSERT INTO dbo.Tmp_regionban (regionUUID, bannedUUID, bannedIp, bannedIpHostMask) - SELECT CONVERT(uniqueidentifier, regionUUID), CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask FROM dbo.regionban WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.regionban - -EXECUTE sp_rename N'dbo.Tmp_regionban', N'regionban', 'OBJECT' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/020_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/020_RegionStore.sql deleted file mode 100644 index 2ce91f6dca..0000000000 --- a/OpenSim/Data/MSSQL/Resources/020_RegionStore.sql +++ /dev/null @@ -1,58 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_regionsettings - ( - regionUUID uniqueidentifier NOT NULL, - block_terraform bit NOT NULL, - block_fly bit NOT NULL, - allow_damage bit NOT NULL, - restrict_pushing bit NOT NULL, - allow_land_resell bit NOT NULL, - allow_land_join_divide bit NOT NULL, - block_show_in_search bit NOT NULL, - agent_limit int NOT NULL, - object_bonus float(53) NOT NULL, - maturity int NOT NULL, - disable_scripts bit NOT NULL, - disable_collisions bit NOT NULL, - disable_physics bit NOT NULL, - terrain_texture_1 uniqueidentifier NOT NULL, - terrain_texture_2 uniqueidentifier NOT NULL, - terrain_texture_3 uniqueidentifier NOT NULL, - terrain_texture_4 uniqueidentifier NOT NULL, - elevation_1_nw float(53) NOT NULL, - elevation_2_nw float(53) NOT NULL, - elevation_1_ne float(53) NOT NULL, - elevation_2_ne float(53) NOT NULL, - elevation_1_se float(53) NOT NULL, - elevation_2_se float(53) NOT NULL, - elevation_1_sw float(53) NOT NULL, - elevation_2_sw float(53) NOT NULL, - water_height float(53) NOT NULL, - terrain_raise_limit float(53) NOT NULL, - terrain_lower_limit float(53) NOT NULL, - use_estate_sun bit NOT NULL, - fixed_sun bit NOT NULL, - sun_position float(53) NOT NULL, - covenant uniqueidentifier NULL DEFAULT (NULL), - Sandbox bit NOT NULL, - sunvectorx float(53) NOT NULL DEFAULT ((0)), - sunvectory float(53) NOT NULL DEFAULT ((0)), - sunvectorz float(53) NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.regionsettings) - EXEC('INSERT INTO dbo.Tmp_regionsettings (regionUUID, block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, terrain_texture_1, terrain_texture_2, terrain_texture_3, terrain_texture_4, elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, covenant, Sandbox, sunvectorx, sunvectory, sunvectorz) - SELECT CONVERT(uniqueidentifier, regionUUID), block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, CONVERT(uniqueidentifier, terrain_texture_1), CONVERT(uniqueidentifier, terrain_texture_2), CONVERT(uniqueidentifier, terrain_texture_3), CONVERT(uniqueidentifier, terrain_texture_4), elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, CONVERT(uniqueidentifier, covenant), Sandbox, sunvectorx, sunvectory, sunvectorz FROM dbo.regionsettings WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.regionsettings - -EXECUTE sp_rename N'dbo.Tmp_regionsettings', N'regionsettings', 'OBJECT' - -ALTER TABLE dbo.regionsettings ADD CONSTRAINT - PK__regionse__5B35159D21B6055D PRIMARY KEY CLUSTERED - ( - regionUUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/021_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/021_RegionStore.sql deleted file mode 100644 index ac59182abc..0000000000 --- a/OpenSim/Data/MSSQL/Resources/021_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD PassTouches bit not null default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/022_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/022_RegionStore.sql deleted file mode 100644 index 421e8d3dc7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/022_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regionsettings ADD loaded_creation_date varchar(20) -ALTER TABLE regionsettings ADD loaded_creation_time varchar(20) -ALTER TABLE regionsettings ADD loaded_creation_id varchar(64) - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/023_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/023_RegionStore.sql deleted file mode 100644 index 75a16f3fb0..0000000000 --- a/OpenSim/Data/MSSQL/Resources/023_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regionsettings DROP COLUMN loaded_creation_date -ALTER TABLE regionsettings DROP COLUMN loaded_creation_time -ALTER TABLE regionsettings ADD loaded_creation_datetime int NOT NULL default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/AssetStore.migrations b/OpenSim/Data/MSSQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..beb82b9fc2 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/AssetStore.migrations @@ -0,0 +1,100 @@ +:VERSION 1 + +CREATE TABLE [assets] ( + [id] [varchar](36) NOT NULL, + [name] [varchar](64) NOT NULL, + [description] [varchar](64) NOT NULL, + [assetType] [tinyint] NOT NULL, + [local] [tinyint] NOT NULL, + [temporary] [tinyint] NOT NULL, + [data] [image] NOT NULL, +PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_assets + ( + id varchar(36) NOT NULL, + name varchar(64) NOT NULL, + description varchar(64) NOT NULL, + assetType tinyint NOT NULL, + local bit NOT NULL, + temporary bit NOT NULL, + data image NOT NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM assets) + EXEC('INSERT INTO Tmp_assets (id, name, description, assetType, local, temporary, data) + SELECT id, name, description, assetType, CONVERT(bit, local), CONVERT(bit, temporary), data FROM assets WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE assets + +EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' + +ALTER TABLE dbo.assets ADD CONSTRAINT + PK__assets__id PRIMARY KEY CLUSTERED + ( + id + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 3 + +BEGIN TRANSACTION + +ALTER TABLE assets add create_time integer default 0 +ALTER TABLE assets add access_time integer default 0 + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_assets + ( + id uniqueidentifier NOT NULL, + name varchar(64) NOT NULL, + description varchar(64) NOT NULL, + assetType tinyint NOT NULL, + local bit NOT NULL, + temporary bit NOT NULL, + data image NOT NULL, + create_time int NULL, + access_time int NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.assets) + EXEC('INSERT INTO dbo.Tmp_assets (id, name, description, assetType, local, temporary, data, create_time, access_time) + SELECT CONVERT(uniqueidentifier, id), name, description, assetType, local, temporary, data, create_time, access_time FROM dbo.assets WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE assets + +EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' + +ALTER TABLE dbo.assets ADD CONSTRAINT + PK__assets__id PRIMARY KEY CLUSTERED + ( + id + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; + + diff --git a/OpenSim/Data/MSSQL/Resources/001_AuthStore.sql b/OpenSim/Data/MSSQL/Resources/AuthStore.migrations similarity index 63% rename from OpenSim/Data/MSSQL/Resources/001_AuthStore.sql rename to OpenSim/Data/MSSQL/Resources/AuthStore.migrations index c70a19319b..5b90ca3d36 100644 --- a/OpenSim/Data/MSSQL/Resources/001_AuthStore.sql +++ b/OpenSim/Data/MSSQL/Resources/AuthStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [auth] ( @@ -14,4 +16,13 @@ CREATE TABLE [tokens] ( [validity] [datetime] NOT NULL ) ON [PRIMARY] +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey, accountType) SELECT [UUID] AS UUID, [passwordHash] AS passwordHash, [passwordSalt] AS passwordSalt, [webLoginKey] AS webLoginKey, 'UserAccount' as [accountType] FROM users; + + COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/001_Avatar.sql b/OpenSim/Data/MSSQL/Resources/Avatar.migrations similarity index 94% rename from OpenSim/Data/MSSQL/Resources/001_Avatar.sql rename to OpenSim/Data/MSSQL/Resources/Avatar.migrations index 48f4c00669..759e939caf 100644 --- a/OpenSim/Data/MSSQL/Resources/001_Avatar.sql +++ b/OpenSim/Data/MSSQL/Resources/Avatar.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [Avatars] ( diff --git a/OpenSim/Data/MSSQL/Resources/EstateStore.migrations b/OpenSim/Data/MSSQL/Resources/EstateStore.migrations new file mode 100644 index 0000000000..64b2d2bdb2 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/EstateStore.migrations @@ -0,0 +1,334 @@ +:VERSION 1 + +BEGIN TRANSACTION + +CREATE TABLE [dbo].[estate_managers]( + [EstateID] [int] NOT NULL, + [uuid] [varchar](36) NOT NULL, + CONSTRAINT [PK_estate_managers] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + +CREATE TABLE [dbo].[estate_groups]( + [EstateID] [int] NOT NULL, + [uuid] [varchar](36) NOT NULL, + CONSTRAINT [PK_estate_groups] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + + +CREATE TABLE [dbo].[estate_users]( + [EstateID] [int] NOT NULL, + [uuid] [varchar](36) NOT NULL, + CONSTRAINT [PK_estate_users] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + + +CREATE TABLE [dbo].[estateban]( + [EstateID] [int] NOT NULL, + [bannedUUID] [varchar](36) NOT NULL, + [bannedIp] [varchar](16) NOT NULL, + [bannedIpHostMask] [varchar](16) NOT NULL, + [bannedNameMask] [varchar](64) NULL DEFAULT (NULL), + CONSTRAINT [PK_estateban] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + +CREATE TABLE [dbo].[estate_settings]( + [EstateID] [int] IDENTITY(1,100) NOT NULL, + [EstateName] [varchar](64) NULL DEFAULT (NULL), + [AbuseEmailToEstateOwner] [bit] NOT NULL, + [DenyAnonymous] [bit] NOT NULL, + [ResetHomeOnTeleport] [bit] NOT NULL, + [FixedSun] [bit] NOT NULL, + [DenyTransacted] [bit] NOT NULL, + [BlockDwell] [bit] NOT NULL, + [DenyIdentified] [bit] NOT NULL, + [AllowVoice] [bit] NOT NULL, + [UseGlobalTime] [bit] NOT NULL, + [PricePerMeter] [int] NOT NULL, + [TaxFree] [bit] NOT NULL, + [AllowDirectTeleport] [bit] NOT NULL, + [RedirectGridX] [int] NOT NULL, + [RedirectGridY] [int] NOT NULL, + [ParentEstateID] [int] NOT NULL, + [SunPosition] [float] NOT NULL, + [EstateSkipScripts] [bit] NOT NULL, + [BillableFactor] [float] NOT NULL, + [PublicAccess] [bit] NOT NULL, + [AbuseEmail] [varchar](255) NOT NULL, + [EstateOwner] [varchar](36) NOT NULL, + [DenyMinors] [bit] NOT NULL, + CONSTRAINT [PK_estate_settings] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + + +CREATE TABLE [dbo].[estate_map]( + [RegionID] [varchar](36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + [EstateID] [int] NOT NULL, + CONSTRAINT [PK_estate_map] PRIMARY KEY CLUSTERED +( + [RegionID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +ALTER TABLE dbo.estate_managers DROP CONSTRAINT PK_estate_managers + +CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +ALTER TABLE dbo.estate_groups DROP CONSTRAINT PK_estate_groups + +CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +ALTER TABLE dbo.estate_users DROP CONSTRAINT PK_estate_users + +CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estateban + ( + EstateID int NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NULL, + bannedIpHostMask varchar(16) NULL, + bannedNameMask varchar(64) NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estateban) + EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) + SELECT EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban') + +DROP TABLE dbo.estateban + +EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_managers + ( + EstateID int NOT NULL, + uuid uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_managers) + EXEC('INSERT INTO dbo.Tmp_estate_managers (EstateID, uuid) + SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_managers WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_managers + +EXECUTE sp_rename N'dbo.Tmp_estate_managers', N'estate_managers', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_groups + ( + EstateID int NOT NULL, + uuid uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_groups) + EXEC('INSERT INTO dbo.Tmp_estate_groups (EstateID, uuid) + SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_groups WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_groups + +EXECUTE sp_rename N'dbo.Tmp_estate_groups', N'estate_groups', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_users + ( + EstateID int NOT NULL, + uuid uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_users) + EXEC('INSERT INTO dbo.Tmp_estate_users (EstateID, uuid) + SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_users WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_users + +EXECUTE sp_rename N'dbo.Tmp_estate_users', N'estate_users', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estateban + ( + EstateID int NOT NULL, + bannedUUID uniqueidentifier NOT NULL, + bannedIp varchar(16) NULL, + bannedIpHostMask varchar(16) NULL, + bannedNameMask varchar(64) NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estateban) + EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) + SELECT EstateID, CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estateban + +EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 8 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_settings + ( + EstateID int NOT NULL IDENTITY (1, 100), + EstateName varchar(64) NULL DEFAULT (NULL), + AbuseEmailToEstateOwner bit NOT NULL, + DenyAnonymous bit NOT NULL, + ResetHomeOnTeleport bit NOT NULL, + FixedSun bit NOT NULL, + DenyTransacted bit NOT NULL, + BlockDwell bit NOT NULL, + DenyIdentified bit NOT NULL, + AllowVoice bit NOT NULL, + UseGlobalTime bit NOT NULL, + PricePerMeter int NOT NULL, + TaxFree bit NOT NULL, + AllowDirectTeleport bit NOT NULL, + RedirectGridX int NOT NULL, + RedirectGridY int NOT NULL, + ParentEstateID int NOT NULL, + SunPosition float(53) NOT NULL, + EstateSkipScripts bit NOT NULL, + BillableFactor float(53) NOT NULL, + PublicAccess bit NOT NULL, + AbuseEmail varchar(255) NOT NULL, + EstateOwner uniqueidentifier NOT NULL, + DenyMinors bit NOT NULL + ) ON [PRIMARY] + +SET IDENTITY_INSERT dbo.Tmp_estate_settings ON + +IF EXISTS(SELECT * FROM dbo.estate_settings) + EXEC('INSERT INTO dbo.Tmp_estate_settings (EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, EstateOwner, DenyMinors) + SELECT EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, CONVERT(uniqueidentifier, EstateOwner), DenyMinors FROM dbo.estate_settings WITH (HOLDLOCK TABLOCKX)') + +SET IDENTITY_INSERT dbo.Tmp_estate_settings OFF + +DROP TABLE dbo.estate_settings + +EXECUTE sp_rename N'dbo.Tmp_estate_settings', N'estate_settings', 'OBJECT' + +ALTER TABLE dbo.estate_settings ADD CONSTRAINT + PK_estate_settings PRIMARY KEY CLUSTERED + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 9 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_map + ( + RegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + EstateID int NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_map) + EXEC('INSERT INTO dbo.Tmp_estate_map (RegionID, EstateID) + SELECT CONVERT(uniqueidentifier, RegionID), EstateID FROM dbo.estate_map WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_map + +EXECUTE sp_rename N'dbo.Tmp_estate_map', N'estate_map', 'OBJECT' + +ALTER TABLE dbo.estate_map ADD CONSTRAINT + PK_estate_map PRIMARY KEY CLUSTERED + ( + RegionID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +COMMIT + + diff --git a/OpenSim/Data/MSSQL/Resources/001_FriendsStore.sql b/OpenSim/Data/MSSQL/Resources/FriendsStore.migrations similarity index 54% rename from OpenSim/Data/MSSQL/Resources/001_FriendsStore.sql rename to OpenSim/Data/MSSQL/Resources/FriendsStore.migrations index 94d240b512..f981a91999 100644 --- a/OpenSim/Data/MSSQL/Resources/001_FriendsStore.sql +++ b/OpenSim/Data/MSSQL/Resources/FriendsStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [Friends] ( @@ -7,5 +9,12 @@ CREATE TABLE [Friends] ( [Offered] varchar(32) NOT NULL DEFAULT 0) ON [PRIMARY] +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +INSERT INTO Friends (PrincipalID, Friend, Flags, Offered) SELECT [ownerID], [friendID], [friendPerms], 0 FROM userfriends; COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/GridStore.migrations b/OpenSim/Data/MSSQL/Resources/GridStore.migrations new file mode 100644 index 0000000000..d2ca27a071 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/GridStore.migrations @@ -0,0 +1,225 @@ +:VERSION 1 + +BEGIN TRANSACTION + +CREATE TABLE [dbo].[regions]( + [regionHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionName] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [uuid] [varchar](255) COLLATE Latin1_General_CI_AS NOT NULL, + [regionRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionSecret] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionDataURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverIP] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [locX] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [locY] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [locZ] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [eastOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [westOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [southOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [northOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionAssetURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionAssetRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionAssetSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionUserURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionUserRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionUserSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionMapTexture] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverHttpPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverRemotingPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [owner_uuid] [varchar](36) COLLATE Latin1_General_CI_AS NULL, +PRIMARY KEY CLUSTERED +( + [uuid] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +COMMIT + + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_regions + ( + uuid varchar(36) COLLATE Latin1_General_CI_AS NOT NULL, + regionHandle bigint NULL, + regionName varchar(20) NULL, + regionRecvKey varchar(128) NULL, + regionSendKey varchar(128) NULL, + regionSecret varchar(128) NULL, + regionDataURI varchar(128) NULL, + serverIP varchar(64) NULL, + serverPort int NULL, + serverURI varchar(255) NULL, + locX int NULL, + locY int NULL, + locZ int NULL, + eastOverrideHandle bigint NULL, + westOverrideHandle bigint NULL, + southOverrideHandle bigint NULL, + northOverrideHandle bigint NULL, + regionAssetURI varchar(255) NULL, + regionAssetRecvKey varchar(128) NULL, + regionAssetSendKey varchar(128) NULL, + regionUserURI varchar(255) NULL, + regionUserRecvKey varchar(128) NULL, + regionUserSendKey varchar(128) NULL, + regionMapTexture varchar(36) NULL, + serverHttpPort int NULL, + serverRemotingPort int NULL, + owner_uuid varchar(36) NULL, + originUUID varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM regions) + EXEC('INSERT INTO Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid) + SELECT CONVERT(varchar(36), uuid), CONVERT(bigint, regionHandle), CONVERT(varchar(20), regionName), CONVERT(varchar(128), regionRecvKey), CONVERT(varchar(128), regionSendKey), CONVERT(varchar(128), regionSecret), CONVERT(varchar(128), regionDataURI), CONVERT(varchar(64), serverIP), CONVERT(int, serverPort), serverURI, CONVERT(int, locX), CONVERT(int, locY), CONVERT(int, locZ), CONVERT(bigint, eastOverrideHandle), CONVERT(bigint, westOverrideHandle), CONVERT(bigint, southOverrideHandle), CONVERT(bigint, northOverrideHandle), regionAssetURI, CONVERT(varchar(128), regionAssetRecvKey), CONVERT(varchar(128), regionAssetSendKey), regionUserURI, CONVERT(varchar(128), regionUserRecvKey), CONVERT(varchar(128), regionUserSendKey), CONVERT(varchar(36), regionMapTexture), CONVERT(int, serverHttpPort), CONVERT(int, serverRemotingPort), owner_uuid FROM regions') + +DROP TABLE regions + +EXECUTE sp_rename N'Tmp_regions', N'regions', 'OBJECT' + +ALTER TABLE regions ADD CONSTRAINT + PK__regions__uuid PRIMARY KEY CLUSTERED + ( + uuid + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions + ( + regionName + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions + ( + regionHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions + ( + eastOverrideHandle, + westOverrideHandle, + southOverrideHandle, + northOverrideHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 4 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_regions + ( + uuid uniqueidentifier NOT NULL, + regionHandle bigint NULL, + regionName varchar(20) NULL, + regionRecvKey varchar(128) NULL, + regionSendKey varchar(128) NULL, + regionSecret varchar(128) NULL, + regionDataURI varchar(128) NULL, + serverIP varchar(64) NULL, + serverPort int NULL, + serverURI varchar(255) NULL, + locX int NULL, + locY int NULL, + locZ int NULL, + eastOverrideHandle bigint NULL, + westOverrideHandle bigint NULL, + southOverrideHandle bigint NULL, + northOverrideHandle bigint NULL, + regionAssetURI varchar(255) NULL, + regionAssetRecvKey varchar(128) NULL, + regionAssetSendKey varchar(128) NULL, + regionUserURI varchar(255) NULL, + regionUserRecvKey varchar(128) NULL, + regionUserSendKey varchar(128) NULL, + regionMapTexture uniqueidentifier NULL, + serverHttpPort int NULL, + serverRemotingPort int NULL, + owner_uuid uniqueidentifier NOT NULL, + originUUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.regions) + EXEC('INSERT INTO dbo.Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid, originUUID) + SELECT CONVERT(uniqueidentifier, uuid), regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, CONVERT(uniqueidentifier, regionMapTexture), serverHttpPort, serverRemotingPort, CONVERT(uniqueidentifier, owner_uuid), CONVERT(uniqueidentifier, originUUID) FROM dbo.regions WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.regions + +EXECUTE sp_rename N'dbo.Tmp_regions', N'regions', 'OBJECT' + +ALTER TABLE dbo.regions ADD CONSTRAINT + PK__regions__uuid PRIMARY KEY CLUSTERED + ( + uuid + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions + ( + regionName + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions + ( + regionHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions + ( + eastOverrideHandle, + westOverrideHandle, + southOverrideHandle, + northOverrideHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + +ALTER TABLE regions ADD access int default 0; + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +ALTER TABLE regions ADD scopeid uniqueidentifier default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE regions ADD DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [owner_uuid]; +ALTER TABLE regions ADD sizeX integer not null default 0; +ALTER TABLE regions ADD sizeY integer not null default 0; + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +ALTER TABLE regions ADD [flags] integer NOT NULL DEFAULT 0; +CREATE INDEX [flags] ON regions(flags); +ALTER TABLE [regions] ADD [last_seen] integer NOT NULL DEFAULT 0; +ALTER TABLE [regions] ADD [PrincipalID] uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE [regions] ADD [Token] varchar(255) NOT NULL DEFAULT 0; + +COMMIT + + diff --git a/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations b/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..cd5dfdc1ca --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations @@ -0,0 +1,174 @@ +:VERSION 1 + +BEGIN TRANSACTION + +CREATE TABLE [inventoryfolders] ( + [folderID] [varchar](36) NOT NULL default '', + [agentID] [varchar](36) default NULL, + [parentFolderID] [varchar](36) default NULL, + [folderName] [varchar](64) default NULL, + [type] [smallint] NOT NULL default 0, + [version] [int] NOT NULL default 0, + PRIMARY KEY CLUSTERED +( + [folderID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [owner] ON [inventoryfolders] +( + [agentID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [parent] ON [inventoryfolders] +( + [parentFolderID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE TABLE [inventoryitems] ( + [inventoryID] [varchar](36) NOT NULL default '', + [assetID] [varchar](36) default NULL, + [assetType] [int] default NULL, + [parentFolderID] [varchar](36) default NULL, + [avatarID] [varchar](36) default NULL, + [inventoryName] [varchar](64) default NULL, + [inventoryDescription] [varchar](128) default NULL, + [inventoryNextPermissions] [int] default NULL, + [inventoryCurrentPermissions] [int] default NULL, + [invType] [int] default NULL, + [creatorID] [varchar](36) default NULL, + [inventoryBasePermissions] [int] NOT NULL default 0, + [inventoryEveryOnePermissions] [int] NOT NULL default 0, + [salePrice] [int] default NULL, + [saleType] [tinyint] default NULL, + [creationDate] [int] default NULL, + [groupID] [varchar](36) default NULL, + [groupOwned] [bit] default NULL, + [flags] [int] default NULL, + PRIMARY KEY CLUSTERED +( + [inventoryID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX [owner] ON [inventoryitems] +( + [avatarID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [folder] ON [inventoryitems] +( + [parentFolderID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 2 + +BEGIN TRANSACTION + +ALTER TABLE inventoryitems ADD inventoryGroupPermissions INTEGER NOT NULL default 0 + +COMMIT + +:VERSION 3 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_inventoryfolders + ( + folderID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + agentID uniqueidentifier NULL DEFAULT (NULL), + parentFolderID uniqueidentifier NULL DEFAULT (NULL), + folderName varchar(64) NULL DEFAULT (NULL), + type smallint NOT NULL DEFAULT ((0)), + version int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.inventoryfolders) + EXEC('INSERT INTO dbo.Tmp_inventoryfolders (folderID, agentID, parentFolderID, folderName, type, version) + SELECT CONVERT(uniqueidentifier, folderID), CONVERT(uniqueidentifier, agentID), CONVERT(uniqueidentifier, parentFolderID), folderName, type, version FROM dbo.inventoryfolders WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.inventoryfolders + +EXECUTE sp_rename N'dbo.Tmp_inventoryfolders', N'inventoryfolders', 'OBJECT' + +ALTER TABLE dbo.inventoryfolders ADD CONSTRAINT + PK__inventor__C2FABFB3173876EA PRIMARY KEY CLUSTERED + ( + folderID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX owner ON dbo.inventoryfolders + ( + agentID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX parent ON dbo.inventoryfolders + ( + parentFolderID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_inventoryitems + ( + inventoryID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + assetID uniqueidentifier NULL DEFAULT (NULL), + assetType int NULL DEFAULT (NULL), + parentFolderID uniqueidentifier NULL DEFAULT (NULL), + avatarID uniqueidentifier NULL DEFAULT (NULL), + inventoryName varchar(64) NULL DEFAULT (NULL), + inventoryDescription varchar(128) NULL DEFAULT (NULL), + inventoryNextPermissions int NULL DEFAULT (NULL), + inventoryCurrentPermissions int NULL DEFAULT (NULL), + invType int NULL DEFAULT (NULL), + creatorID uniqueidentifier NULL DEFAULT (NULL), + inventoryBasePermissions int NOT NULL DEFAULT ((0)), + inventoryEveryOnePermissions int NOT NULL DEFAULT ((0)), + salePrice int NULL DEFAULT (NULL), + saleType tinyint NULL DEFAULT (NULL), + creationDate int NULL DEFAULT (NULL), + groupID uniqueidentifier NULL DEFAULT (NULL), + groupOwned bit NULL DEFAULT (NULL), + flags int NULL DEFAULT (NULL), + inventoryGroupPermissions int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.inventoryitems) + EXEC('INSERT INTO dbo.Tmp_inventoryitems (inventoryID, assetID, assetType, parentFolderID, avatarID, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryGroupPermissions) + SELECT CONVERT(uniqueidentifier, inventoryID), CONVERT(uniqueidentifier, assetID), assetType, CONVERT(uniqueidentifier, parentFolderID), CONVERT(uniqueidentifier, avatarID), inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, CONVERT(uniqueidentifier, creatorID), inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, CONVERT(uniqueidentifier, groupID), groupOwned, flags, inventoryGroupPermissions FROM dbo.inventoryitems WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.inventoryitems + +EXECUTE sp_rename N'dbo.Tmp_inventoryitems', N'inventoryitems', 'OBJECT' + +ALTER TABLE dbo.inventoryitems ADD CONSTRAINT + PK__inventor__C4B7BC2220C1E124 PRIMARY KEY CLUSTERED + ( + inventoryID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX owner ON dbo.inventoryitems + ( + avatarID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX folder ON dbo.inventoryitems + ( + parentFolderID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + diff --git a/OpenSim/Data/MSSQL/Resources/001_LogStore.sql b/OpenSim/Data/MSSQL/Resources/LogStore.migrations similarity index 96% rename from OpenSim/Data/MSSQL/Resources/001_LogStore.sql rename to OpenSim/Data/MSSQL/Resources/LogStore.migrations index 9ece6274dd..1430d8d0ee 100644 --- a/OpenSim/Data/MSSQL/Resources/001_LogStore.sql +++ b/OpenSim/Data/MSSQL/Resources/LogStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [logs] ( diff --git a/OpenSim/Data/MSSQL/Resources/001_Presence.sql b/OpenSim/Data/MSSQL/Resources/Presence.migrations similarity index 81% rename from OpenSim/Data/MSSQL/Resources/001_Presence.sql rename to OpenSim/Data/MSSQL/Resources/Presence.migrations index 877881c859..35f78e1f52 100644 --- a/OpenSim/Data/MSSQL/Resources/001_Presence.sql +++ b/OpenSim/Data/MSSQL/Resources/Presence.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [Presence] ( @@ -16,4 +18,13 @@ CREATE TABLE [Presence] ( ) ON [PRIMARY] +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE UNIQUE INDEX SessionID ON Presence(SessionID); +CREATE INDEX UserID ON Presence(UserID); + COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..e912d646a0 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations @@ -0,0 +1,929 @@ + +:VERSION 1 + +CREATE TABLE [dbo].[prims]( + [UUID] [varchar](255) NOT NULL, + [RegionUUID] [varchar](255) NULL, + [ParentID] [int] NULL, + [CreationDate] [int] NULL, + [Name] [varchar](255) NULL, + [SceneGroupID] [varchar](255) NULL, + [Text] [varchar](255) NULL, + [Description] [varchar](255) NULL, + [SitName] [varchar](255) NULL, + [TouchName] [varchar](255) NULL, + [ObjectFlags] [int] NULL, + [CreatorID] [varchar](255) NULL, + [OwnerID] [varchar](255) NULL, + [GroupID] [varchar](255) NULL, + [LastOwnerID] [varchar](255) NULL, + [OwnerMask] [int] NULL, + [NextOwnerMask] [int] NULL, + [GroupMask] [int] NULL, + [EveryoneMask] [int] NULL, + [BaseMask] [int] NULL, + [PositionX] [float] NULL, + [PositionY] [float] NULL, + [PositionZ] [float] NULL, + [GroupPositionX] [float] NULL, + [GroupPositionY] [float] NULL, + [GroupPositionZ] [float] NULL, + [VelocityX] [float] NULL, + [VelocityY] [float] NULL, + [VelocityZ] [float] NULL, + [AngularVelocityX] [float] NULL, + [AngularVelocityY] [float] NULL, + [AngularVelocityZ] [float] NULL, + [AccelerationX] [float] NULL, + [AccelerationY] [float] NULL, + [AccelerationZ] [float] NULL, + [RotationX] [float] NULL, + [RotationY] [float] NULL, + [RotationZ] [float] NULL, + [RotationW] [float] NULL, + [SitTargetOffsetX] [float] NULL, + [SitTargetOffsetY] [float] NULL, + [SitTargetOffsetZ] [float] NULL, + [SitTargetOrientW] [float] NULL, + [SitTargetOrientX] [float] NULL, + [SitTargetOrientY] [float] NULL, + [SitTargetOrientZ] [float] NULL, +PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +CREATE TABLE [dbo].[primshapes]( + [UUID] [varchar](255) NOT NULL, + [Shape] [int] NULL, + [ScaleX] [float] NULL, + [ScaleY] [float] NULL, + [ScaleZ] [float] NULL, + [PCode] [int] NULL, + [PathBegin] [int] NULL, + [PathEnd] [int] NULL, + [PathScaleX] [int] NULL, + [PathScaleY] [int] NULL, + [PathShearX] [int] NULL, + [PathShearY] [int] NULL, + [PathSkew] [int] NULL, + [PathCurve] [int] NULL, + [PathRadiusOffset] [int] NULL, + [PathRevolutions] [int] NULL, + [PathTaperX] [int] NULL, + [PathTaperY] [int] NULL, + [PathTwist] [int] NULL, + [PathTwistBegin] [int] NULL, + [ProfileBegin] [int] NULL, + [ProfileEnd] [int] NULL, + [ProfileCurve] [int] NULL, + [ProfileHollow] [int] NULL, + [State] [int] NULL, + [Texture] [image] NULL, + [ExtraParams] [image] NULL, +PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +CREATE TABLE [dbo].[primitems]( + [itemID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [primID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [assetID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [parentFolderID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [invType] [int] NULL, + [assetType] [int] NULL, + [name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [creationDate] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [creatorID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [ownerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [lastOwnerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [groupID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [nextPermissions] [int] NULL, + [currentPermissions] [int] NULL, + [basePermissions] [int] NULL, + [everyonePermissions] [int] NULL, + [groupPermissions] [int] NULL, +PRIMARY KEY CLUSTERED +( + [itemID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +CREATE TABLE [dbo].[terrain]( + [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [Revision] [int] NULL, + [Heightfield] [image] NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +CREATE TABLE [dbo].[land]( + [UUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [LocalLandID] [int] NULL, + [Bitmap] [image] NULL, + [Name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [Description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [OwnerUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [IsGroupOwned] [int] NULL, + [Area] [int] NULL, + [AuctionID] [int] NULL, + [Category] [int] NULL, + [ClaimDate] [int] NULL, + [ClaimPrice] [int] NULL, + [GroupUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [SalePrice] [int] NULL, + [LandStatus] [int] NULL, + [LandFlags] [int] NULL, + [LandingType] [int] NULL, + [MediaAutoScale] [int] NULL, + [MediaTextureUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [MediaURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [MusicURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [PassHours] [float] NULL, + [PassPrice] [int] NULL, + [SnapshotUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [UserLocationX] [float] NULL, + [UserLocationY] [float] NULL, + [UserLocationZ] [float] NULL, + [UserLookAtX] [float] NULL, + [UserLookAtY] [float] NULL, + [UserLookAtZ] [float] NULL, +PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +CREATE TABLE [dbo].[landaccesslist]( + [LandUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [AccessUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [Flags] [int] NULL +) ON [PRIMARY] + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE TABLE regionban ( + [regionUUID] VARCHAR(36) NOT NULL, + [bannedUUID] VARCHAR(36) NOT NULL, + [bannedIp] VARCHAR(16) NOT NULL, + [bannedIpHostMask] VARCHAR(16) NOT NULL) + +create table [dbo].[regionsettings] ( + [regionUUID] [varchar](36) not null, + [block_terraform] [bit] not null, + [block_fly] [bit] not null, + [allow_damage] [bit] not null, + [restrict_pushing] [bit] not null, + [allow_land_resell] [bit] not null, + [allow_land_join_divide] [bit] not null, + [block_show_in_search] [bit] not null, + [agent_limit] [int] not null, + [object_bonus] [float] not null, + [maturity] [int] not null, + [disable_scripts] [bit] not null, + [disable_collisions] [bit] not null, + [disable_physics] [bit] not null, + [terrain_texture_1] [varchar](36) not null, + [terrain_texture_2] [varchar](36) not null, + [terrain_texture_3] [varchar](36) not null, + [terrain_texture_4] [varchar](36) not null, + [elevation_1_nw] [float] not null, + [elevation_2_nw] [float] not null, + [elevation_1_ne] [float] not null, + [elevation_2_ne] [float] not null, + [elevation_1_se] [float] not null, + [elevation_2_se] [float] not null, + [elevation_1_sw] [float] not null, + [elevation_2_sw] [float] not null, + [water_height] [float] not null, + [terrain_raise_limit] [float] not null, + [terrain_lower_limit] [float] not null, + [use_estate_sun] [bit] not null, + [fixed_sun] [bit] not null, + [sun_position] [float] not null, + [covenant] [varchar](36) default NULL, + [Sandbox] [bit] NOT NULL, +PRIMARY KEY CLUSTERED +( + [regionUUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +COMMIT + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_prims + ( + UUID varchar(36) NOT NULL, + RegionUUID varchar(36) NULL, + ParentID int NULL, + CreationDate int NULL, + Name varchar(255) NULL, + SceneGroupID varchar(36) NULL, + Text varchar(255) NULL, + Description varchar(255) NULL, + SitName varchar(255) NULL, + TouchName varchar(255) NULL, + ObjectFlags int NULL, + CreatorID varchar(36) NULL, + OwnerID varchar(36) NULL, + GroupID varchar(36) NULL, + LastOwnerID varchar(36) NULL, + OwnerMask int NULL, + NextOwnerMask int NULL, + GroupMask int NULL, + EveryoneMask int NULL, + BaseMask int NULL, + PositionX float(53) NULL, + PositionY float(53) NULL, + PositionZ float(53) NULL, + GroupPositionX float(53) NULL, + GroupPositionY float(53) NULL, + GroupPositionZ float(53) NULL, + VelocityX float(53) NULL, + VelocityY float(53) NULL, + VelocityZ float(53) NULL, + AngularVelocityX float(53) NULL, + AngularVelocityY float(53) NULL, + AngularVelocityZ float(53) NULL, + AccelerationX float(53) NULL, + AccelerationY float(53) NULL, + AccelerationZ float(53) NULL, + RotationX float(53) NULL, + RotationY float(53) NULL, + RotationZ float(53) NULL, + RotationW float(53) NULL, + SitTargetOffsetX float(53) NULL, + SitTargetOffsetY float(53) NULL, + SitTargetOffsetZ float(53) NULL, + SitTargetOrientW float(53) NULL, + SitTargetOrientX float(53) NULL, + SitTargetOrientY float(53) NULL, + SitTargetOrientZ float(53) NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.prims) + EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ) + SELECT CONVERT(varchar(36), UUID), CONVERT(varchar(36), RegionUUID), ParentID, CreationDate, Name, CONVERT(varchar(36), SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(varchar(36), CreatorID), CONVERT(varchar(36), OwnerID), CONVERT(varchar(36), GroupID), CONVERT(varchar(36), LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.prims + +EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' + +ALTER TABLE dbo.prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_primitems + ( + itemID varchar(36) NOT NULL, + primID varchar(36) NULL, + assetID varchar(36) NULL, + parentFolderID varchar(36) NULL, + invType int NULL, + assetType int NULL, + name varchar(255) NULL, + description varchar(255) NULL, + creationDate varchar(255) NULL, + creatorID varchar(36) NULL, + ownerID varchar(36) NULL, + lastOwnerID varchar(36) NULL, + groupID varchar(36) NULL, + nextPermissions int NULL, + currentPermissions int NULL, + basePermissions int NULL, + everyonePermissions int NULL, + groupPermissions int NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM primitems) + EXEC('INSERT INTO Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions) + SELECT CONVERT(varchar(36), itemID), CONVERT(varchar(36), primID), CONVERT(varchar(36), assetID), CONVERT(varchar(36), parentFolderID), invType, assetType, name, description, creationDate, CONVERT(varchar(36), creatorID), CONVERT(varchar(36), ownerID), CONVERT(varchar(36), lastOwnerID), CONVERT(varchar(36), groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions') + +DROP TABLE primitems + +EXECUTE sp_rename N'Tmp_primitems', N'primitems', 'OBJECT' + +ALTER TABLE primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED + ( + itemID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_primshapes + ( + UUID varchar(36) NOT NULL, + Shape int NULL, + ScaleX float(53) NULL, + ScaleY float(53) NULL, + ScaleZ float(53) NULL, + PCode int NULL, + PathBegin int NULL, + PathEnd int NULL, + PathScaleX int NULL, + PathScaleY int NULL, + PathShearX int NULL, + PathShearY int NULL, + PathSkew int NULL, + PathCurve int NULL, + PathRadiusOffset int NULL, + PathRevolutions int NULL, + PathTaperX int NULL, + PathTaperY int NULL, + PathTwist int NULL, + PathTwistBegin int NULL, + ProfileBegin int NULL, + ProfileEnd int NULL, + ProfileCurve int NULL, + ProfileHollow int NULL, + State int NULL, + Texture image NULL, + ExtraParams image NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM primshapes) + EXEC('INSERT INTO Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) + SELECT CONVERT(varchar(36), UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM primshapes WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE primshapes + +EXECUTE sp_rename N'Tmp_primshapes', N'primshapes', 'OBJECT' + +ALTER TABLE primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD PayPrice int not null default 0 +ALTER TABLE prims ADD PayButton1 int not null default 0 +ALTER TABLE prims ADD PayButton2 int not null default 0 +ALTER TABLE prims ADD PayButton3 int not null default 0 +ALTER TABLE prims ADD PayButton4 int not null default 0 +ALTER TABLE prims ADD LoopedSound varchar(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD LoopedSoundGain float not null default 0.0; +ALTER TABLE prims ADD TextureAnimation image +ALTER TABLE prims ADD OmegaX float not null default 0.0 +ALTER TABLE prims ADD OmegaY float not null default 0.0 +ALTER TABLE prims ADD OmegaZ float not null default 0.0 +ALTER TABLE prims ADD CameraEyeOffsetX float not null default 0.0 +ALTER TABLE prims ADD CameraEyeOffsetY float not null default 0.0 +ALTER TABLE prims ADD CameraEyeOffsetZ float not null default 0.0 +ALTER TABLE prims ADD CameraAtOffsetX float not null default 0.0 +ALTER TABLE prims ADD CameraAtOffsetY float not null default 0.0 +ALTER TABLE prims ADD CameraAtOffsetZ float not null default 0.0 +ALTER TABLE prims ADD ForceMouselook tinyint not null default 0 +ALTER TABLE prims ADD ScriptAccessPin int not null default 0 +ALTER TABLE prims ADD AllowedDrop tinyint not null default 0 +ALTER TABLE prims ADD DieAtEdge tinyint not null default 0 +ALTER TABLE prims ADD SalePrice int not null default 10 +ALTER TABLE prims ADD SaleType tinyint not null default 0 + +ALTER TABLE primitems add flags integer not null default 0 + +ALTER TABLE land ADD AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000' + +CREATE index prims_regionuuid on prims(RegionUUID) +CREATE index prims_parentid on prims(ParentID) + +CREATE index primitems_primid on primitems(primID) + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD ColorR int not null default 0; +ALTER TABLE prims ADD ColorG int not null default 0; +ALTER TABLE prims ADD ColorB int not null default 0; +ALTER TABLE prims ADD ColorA int not null default 0; +ALTER TABLE prims ADD ParticleSystem IMAGE; +ALTER TABLE prims ADD ClickAction tinyint NOT NULL default 0; + +COMMIT + + +:VERSION 8 + +BEGIN TRANSACTION + +ALTER TABLE land ADD OtherCleanTime integer NOT NULL default 0; +ALTER TABLE land ADD Dwell integer NOT NULL default 0; + +COMMIT + +:VERSION 9 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD Material tinyint NOT NULL default 3 + +COMMIT + + +:VERSION 10 + +BEGIN TRANSACTION + +ALTER TABLE regionsettings ADD sunvectorx float NOT NULL default 0; +ALTER TABLE regionsettings ADD sunvectory float NOT NULL default 0; +ALTER TABLE regionsettings ADD sunvectorz float NOT NULL default 0; + +COMMIT + + +:VERSION 11 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000' +ALTER TABLE prims ADD CollisionSoundVolume float not null default 0.0 + +COMMIT + + +:VERSION 12 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD LinkNumber integer not null default 0 + +COMMIT + + +:VERSION 13 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_prims + ( + UUID uniqueidentifier NOT NULL, + RegionUUID uniqueidentifier NULL, + ParentID int NULL, + CreationDate int NULL, + Name varchar(255) NULL, + SceneGroupID uniqueidentifier NULL, + Text varchar(255) NULL, + Description varchar(255) NULL, + SitName varchar(255) NULL, + TouchName varchar(255) NULL, + ObjectFlags int NULL, + CreatorID uniqueidentifier NULL, + OwnerID uniqueidentifier NULL, + GroupID uniqueidentifier NULL, + LastOwnerID uniqueidentifier NULL, + OwnerMask int NULL, + NextOwnerMask int NULL, + GroupMask int NULL, + EveryoneMask int NULL, + BaseMask int NULL, + PositionX float(53) NULL, + PositionY float(53) NULL, + PositionZ float(53) NULL, + GroupPositionX float(53) NULL, + GroupPositionY float(53) NULL, + GroupPositionZ float(53) NULL, + VelocityX float(53) NULL, + VelocityY float(53) NULL, + VelocityZ float(53) NULL, + AngularVelocityX float(53) NULL, + AngularVelocityY float(53) NULL, + AngularVelocityZ float(53) NULL, + AccelerationX float(53) NULL, + AccelerationY float(53) NULL, + AccelerationZ float(53) NULL, + RotationX float(53) NULL, + RotationY float(53) NULL, + RotationZ float(53) NULL, + RotationW float(53) NULL, + SitTargetOffsetX float(53) NULL, + SitTargetOffsetY float(53) NULL, + SitTargetOffsetZ float(53) NULL, + SitTargetOrientW float(53) NULL, + SitTargetOrientX float(53) NULL, + SitTargetOrientY float(53) NULL, + SitTargetOrientZ float(53) NULL, + PayPrice int NOT NULL DEFAULT ((0)), + PayButton1 int NOT NULL DEFAULT ((0)), + PayButton2 int NOT NULL DEFAULT ((0)), + PayButton3 int NOT NULL DEFAULT ((0)), + PayButton4 int NOT NULL DEFAULT ((0)), + LoopedSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + LoopedSoundGain float(53) NOT NULL DEFAULT ((0.0)), + TextureAnimation image NULL, + OmegaX float(53) NOT NULL DEFAULT ((0.0)), + OmegaY float(53) NOT NULL DEFAULT ((0.0)), + OmegaZ float(53) NOT NULL DEFAULT ((0.0)), + CameraEyeOffsetX float(53) NOT NULL DEFAULT ((0.0)), + CameraEyeOffsetY float(53) NOT NULL DEFAULT ((0.0)), + CameraEyeOffsetZ float(53) NOT NULL DEFAULT ((0.0)), + CameraAtOffsetX float(53) NOT NULL DEFAULT ((0.0)), + CameraAtOffsetY float(53) NOT NULL DEFAULT ((0.0)), + CameraAtOffsetZ float(53) NOT NULL DEFAULT ((0.0)), + ForceMouselook tinyint NOT NULL DEFAULT ((0)), + ScriptAccessPin int NOT NULL DEFAULT ((0)), + AllowedDrop tinyint NOT NULL DEFAULT ((0)), + DieAtEdge tinyint NOT NULL DEFAULT ((0)), + SalePrice int NOT NULL DEFAULT ((10)), + SaleType tinyint NOT NULL DEFAULT ((0)), + ColorR int NOT NULL DEFAULT ((0)), + ColorG int NOT NULL DEFAULT ((0)), + ColorB int NOT NULL DEFAULT ((0)), + ColorA int NOT NULL DEFAULT ((0)), + ParticleSystem image NULL, + ClickAction tinyint NOT NULL DEFAULT ((0)), + Material tinyint NOT NULL DEFAULT ((3)), + CollisionSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + CollisionSoundVolume float(53) NOT NULL DEFAULT ((0.0)), + LinkNumber int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.prims) + EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, LoopedSound, LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, LinkNumber) + SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), ParentID, CreationDate, Name, CONVERT(uniqueidentifier, SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(uniqueidentifier, CreatorID), CONVERT(uniqueidentifier, OwnerID), CONVERT(uniqueidentifier, GroupID), CONVERT(uniqueidentifier, LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, CONVERT(uniqueidentifier, LoopedSound), LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CONVERT(uniqueidentifier, CollisionSound), CollisionSoundVolume, LinkNumber FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.prims + +EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' + +ALTER TABLE dbo.prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX prims_regionuuid ON dbo.prims + ( + RegionUUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX prims_parentid ON dbo.prims + ( + ParentID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 14 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_primshapes + ( + UUID uniqueidentifier NOT NULL, + Shape int NULL, + ScaleX float(53) NULL, + ScaleY float(53) NULL, + ScaleZ float(53) NULL, + PCode int NULL, + PathBegin int NULL, + PathEnd int NULL, + PathScaleX int NULL, + PathScaleY int NULL, + PathShearX int NULL, + PathShearY int NULL, + PathSkew int NULL, + PathCurve int NULL, + PathRadiusOffset int NULL, + PathRevolutions int NULL, + PathTaperX int NULL, + PathTaperY int NULL, + PathTwist int NULL, + PathTwistBegin int NULL, + ProfileBegin int NULL, + ProfileEnd int NULL, + ProfileCurve int NULL, + ProfileHollow int NULL, + State int NULL, + Texture image NULL, + ExtraParams image NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.primshapes) + EXEC('INSERT INTO dbo.Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) + SELECT CONVERT(uniqueidentifier, UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM dbo.primshapes WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.primshapes + +EXECUTE sp_rename N'dbo.Tmp_primshapes', N'primshapes', 'OBJECT' + +ALTER TABLE dbo.primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 15 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_primitems + ( + itemID uniqueidentifier NOT NULL, + primID uniqueidentifier NULL, + assetID uniqueidentifier NULL, + parentFolderID uniqueidentifier NULL, + invType int NULL, + assetType int NULL, + name varchar(255) NULL, + description varchar(255) NULL, + creationDate varchar(255) NULL, + creatorID uniqueidentifier NULL, + ownerID uniqueidentifier NULL, + lastOwnerID uniqueidentifier NULL, + groupID uniqueidentifier NULL, + nextPermissions int NULL, + currentPermissions int NULL, + basePermissions int NULL, + everyonePermissions int NULL, + groupPermissions int NULL, + flags int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.primitems) + EXEC('INSERT INTO dbo.Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags) + SELECT CONVERT(uniqueidentifier, itemID), CONVERT(uniqueidentifier, primID), CONVERT(uniqueidentifier, assetID), CONVERT(uniqueidentifier, parentFolderID), invType, assetType, name, description, creationDate, CONVERT(uniqueidentifier, creatorID), CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, lastOwnerID), CONVERT(uniqueidentifier, groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags FROM dbo.primitems WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.primitems + +EXECUTE sp_rename N'dbo.Tmp_primitems', N'primitems', 'OBJECT' + +ALTER TABLE dbo.primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED + ( + itemID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX primitems_primid ON dbo.primitems + ( + primID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 16 + + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_terrain + ( + RegionUUID uniqueidentifier NULL, + Revision int NULL, + Heightfield image NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.terrain) + EXEC('INSERT INTO dbo.Tmp_terrain (RegionUUID, Revision, Heightfield) + SELECT CONVERT(uniqueidentifier, RegionUUID), Revision, Heightfield FROM dbo.terrain WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.terrain + +EXECUTE sp_rename N'dbo.Tmp_terrain', N'terrain', 'OBJECT' + +COMMIT + + +:VERSION 17 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_land + ( + UUID uniqueidentifier NOT NULL, + RegionUUID uniqueidentifier NULL, + LocalLandID int NULL, + Bitmap image NULL, + Name varchar(255) NULL, + Description varchar(255) NULL, + OwnerUUID uniqueidentifier NULL, + IsGroupOwned int NULL, + Area int NULL, + AuctionID int NULL, + Category int NULL, + ClaimDate int NULL, + ClaimPrice int NULL, + GroupUUID uniqueidentifier NULL, + SalePrice int NULL, + LandStatus int NULL, + LandFlags int NULL, + LandingType int NULL, + MediaAutoScale int NULL, + MediaTextureUUID uniqueidentifier NULL, + MediaURL varchar(255) NULL, + MusicURL varchar(255) NULL, + PassHours float(53) NULL, + PassPrice int NULL, + SnapshotUUID uniqueidentifier NULL, + UserLocationX float(53) NULL, + UserLocationY float(53) NULL, + UserLocationZ float(53) NULL, + UserLookAtX float(53) NULL, + UserLookAtY float(53) NULL, + UserLookAtZ float(53) NULL, + AuthbuyerID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + OtherCleanTime int NOT NULL DEFAULT ((0)), + Dwell int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.land) + EXEC('INSERT INTO dbo.Tmp_land (UUID, RegionUUID, LocalLandID, Bitmap, Name, Description, OwnerUUID, IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, GroupUUID, SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, MediaTextureUUID, MediaURL, MusicURL, PassHours, PassPrice, SnapshotUUID, UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, AuthbuyerID, OtherCleanTime, Dwell) + SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), LocalLandID, Bitmap, Name, Description, CONVERT(uniqueidentifier, OwnerUUID), IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, CONVERT(uniqueidentifier, GroupUUID), SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, CONVERT(uniqueidentifier, MediaTextureUUID), MediaURL, MusicURL, PassHours, PassPrice, CONVERT(uniqueidentifier, SnapshotUUID), UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, CONVERT(uniqueidentifier, AuthbuyerID), OtherCleanTime, Dwell FROM dbo.land WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.land + +EXECUTE sp_rename N'dbo.Tmp_land', N'land', 'OBJECT' + +ALTER TABLE dbo.land ADD CONSTRAINT + PK__land__65A475E71BFD2C07 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + + +:VERSION 18 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_landaccesslist + ( + LandUUID uniqueidentifier NULL, + AccessUUID uniqueidentifier NULL, + Flags int NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.landaccesslist) + EXEC('INSERT INTO dbo.Tmp_landaccesslist (LandUUID, AccessUUID, Flags) + SELECT CONVERT(uniqueidentifier, LandUUID), CONVERT(uniqueidentifier, AccessUUID), Flags FROM dbo.landaccesslist WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.landaccesslist + +EXECUTE sp_rename N'dbo.Tmp_landaccesslist', N'landaccesslist', 'OBJECT' + +COMMIT + + + +:VERSION 19 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_regionban + ( + regionUUID uniqueidentifier NOT NULL, + bannedUUID uniqueidentifier NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.regionban) + EXEC('INSERT INTO dbo.Tmp_regionban (regionUUID, bannedUUID, bannedIp, bannedIpHostMask) + SELECT CONVERT(uniqueidentifier, regionUUID), CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask FROM dbo.regionban WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.regionban + +EXECUTE sp_rename N'dbo.Tmp_regionban', N'regionban', 'OBJECT' + +COMMIT + + +:VERSION 20 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_regionsettings + ( + regionUUID uniqueidentifier NOT NULL, + block_terraform bit NOT NULL, + block_fly bit NOT NULL, + allow_damage bit NOT NULL, + restrict_pushing bit NOT NULL, + allow_land_resell bit NOT NULL, + allow_land_join_divide bit NOT NULL, + block_show_in_search bit NOT NULL, + agent_limit int NOT NULL, + object_bonus float(53) NOT NULL, + maturity int NOT NULL, + disable_scripts bit NOT NULL, + disable_collisions bit NOT NULL, + disable_physics bit NOT NULL, + terrain_texture_1 uniqueidentifier NOT NULL, + terrain_texture_2 uniqueidentifier NOT NULL, + terrain_texture_3 uniqueidentifier NOT NULL, + terrain_texture_4 uniqueidentifier NOT NULL, + elevation_1_nw float(53) NOT NULL, + elevation_2_nw float(53) NOT NULL, + elevation_1_ne float(53) NOT NULL, + elevation_2_ne float(53) NOT NULL, + elevation_1_se float(53) NOT NULL, + elevation_2_se float(53) NOT NULL, + elevation_1_sw float(53) NOT NULL, + elevation_2_sw float(53) NOT NULL, + water_height float(53) NOT NULL, + terrain_raise_limit float(53) NOT NULL, + terrain_lower_limit float(53) NOT NULL, + use_estate_sun bit NOT NULL, + fixed_sun bit NOT NULL, + sun_position float(53) NOT NULL, + covenant uniqueidentifier NULL DEFAULT (NULL), + Sandbox bit NOT NULL, + sunvectorx float(53) NOT NULL DEFAULT ((0)), + sunvectory float(53) NOT NULL DEFAULT ((0)), + sunvectorz float(53) NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.regionsettings) + EXEC('INSERT INTO dbo.Tmp_regionsettings (regionUUID, block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, terrain_texture_1, terrain_texture_2, terrain_texture_3, terrain_texture_4, elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, covenant, Sandbox, sunvectorx, sunvectory, sunvectorz) + SELECT CONVERT(uniqueidentifier, regionUUID), block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, CONVERT(uniqueidentifier, terrain_texture_1), CONVERT(uniqueidentifier, terrain_texture_2), CONVERT(uniqueidentifier, terrain_texture_3), CONVERT(uniqueidentifier, terrain_texture_4), elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, CONVERT(uniqueidentifier, covenant), Sandbox, sunvectorx, sunvectory, sunvectorz FROM dbo.regionsettings WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.regionsettings + +EXECUTE sp_rename N'dbo.Tmp_regionsettings', N'regionsettings', 'OBJECT' + +ALTER TABLE dbo.regionsettings ADD CONSTRAINT + PK__regionse__5B35159D21B6055D PRIMARY KEY CLUSTERED + ( + regionUUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 21 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD PassTouches bit not null default 0 + +COMMIT + + +:VERSION 22 + +BEGIN TRANSACTION + +ALTER TABLE regionsettings ADD loaded_creation_date varchar(20) +ALTER TABLE regionsettings ADD loaded_creation_time varchar(20) +ALTER TABLE regionsettings ADD loaded_creation_id varchar(64) + +COMMIT + +:VERSION 23 + +BEGIN TRANSACTION + +ALTER TABLE regionsettings DROP COLUMN loaded_creation_date +ALTER TABLE regionsettings DROP COLUMN loaded_creation_time +ALTER TABLE regionsettings ADD loaded_creation_datetime int NOT NULL default 0 + +COMMIT + + + diff --git a/OpenSim/Data/MSSQL/Resources/UserAccount.migrations b/OpenSim/Data/MSSQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..8534e235c9 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/UserAccount.migrations @@ -0,0 +1,55 @@ +:VERSION 1 + +CREATE TABLE [UserAccounts] ( + [PrincipalID] uniqueidentifier NOT NULL, + [ScopeID] uniqueidentifier NOT NULL, + [FirstName] [varchar](64) NOT NULL, + [LastName] [varchar](64) NOT NULL, + [Email] [varchar](64) NULL, + [ServiceURLs] [text] NULL, + [Created] [int] default NULL, + + PRIMARY KEY CLUSTERED +( + [PrincipalID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +:VERSION 2 + +BEGIN TRANSACTION + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT [UUID] AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, +username AS FirstName, +lastname AS LastName, +email as Email, ( +'AssetServerURI=' + +userAssetURI + ' InventoryServerURI=' + userInventoryURI + ' GatewayURI= HomeURI=') AS ServiceURLs, +created as Created FROM users; + + +COMMIT + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); +CREATE INDEX Email ON UserAccounts(Email); +CREATE INDEX FirstName ON UserAccounts(FirstName); +CREATE INDEX LastName ON UserAccounts(LastName); +CREATE INDEX Name ON UserAccounts(FirstName,LastName); + +COMMIT + +:VERSION 4 + +BEGIN TRANSACTION + +ALTER TABLE UserAccounts ADD UserLevel integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD UserFlags integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD UserTitle varchar(64) NOT NULL DEFAULT ''; + +COMMIT + diff --git a/OpenSim/Data/MSSQL/Resources/UserStore.migrations b/OpenSim/Data/MSSQL/Resources/UserStore.migrations new file mode 100644 index 0000000000..050c544f64 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/UserStore.migrations @@ -0,0 +1,421 @@ +:VERSION 1 + +CREATE TABLE [users] ( + [UUID] [varchar](36) NOT NULL default '', + [username] [varchar](32) NOT NULL, + [lastname] [varchar](32) NOT NULL, + [passwordHash] [varchar](32) NOT NULL, + [passwordSalt] [varchar](32) NOT NULL, + [homeRegion] [bigint] default NULL, + [homeLocationX] [float] default NULL, + [homeLocationY] [float] default NULL, + [homeLocationZ] [float] default NULL, + [homeLookAtX] [float] default NULL, + [homeLookAtY] [float] default NULL, + [homeLookAtZ] [float] default NULL, + [created] [int] NOT NULL, + [lastLogin] [int] NOT NULL, + [userInventoryURI] [varchar](255) default NULL, + [userAssetURI] [varchar](255) default NULL, + [profileCanDoMask] [int] default NULL, + [profileWantDoMask] [int] default NULL, + [profileAboutText] [ntext], + [profileFirstText] [ntext], + [profileImage] [varchar](36) default NULL, + [profileFirstImage] [varchar](36) default NULL, + [webLoginKey] [varchar](36) default NULL, + PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX [usernames] ON [users] +( + [username] ASC, + [lastname] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE TABLE [agents] ( + [UUID] [varchar](36) NOT NULL, + [sessionID] [varchar](36) NOT NULL, + [secureSessionID] [varchar](36) NOT NULL, + [agentIP] [varchar](16) NOT NULL, + [agentPort] [int] NOT NULL, + [agentOnline] [tinyint] NOT NULL, + [loginTime] [int] NOT NULL, + [logoutTime] [int] NOT NULL, + [currentRegion] [varchar](36) NOT NULL, + [currentHandle] [bigint] NOT NULL, + [currentPos] [varchar](64) NOT NULL, + PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX [session] ON [agents] +( + [sessionID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [ssession] ON [agents] +( + [secureSessionID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE TABLE [dbo].[userfriends]( + [ownerID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, + [friendID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, + [friendPerms] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [datetimestamp] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL +) ON [PRIMARY] + +CREATE TABLE [avatarappearance] ( + [Owner] [varchar](36) NOT NULL, + [Serial] int NOT NULL, + [Visual_Params] [image] NOT NULL, + [Texture] [image] NOT NULL, + [Avatar_Height] float NOT NULL, + [Body_Item] [varchar](36) NOT NULL, + [Body_Asset] [varchar](36) NOT NULL, + [Skin_Item] [varchar](36) NOT NULL, + [Skin_Asset] [varchar](36) NOT NULL, + [Hair_Item] [varchar](36) NOT NULL, + [Hair_Asset] [varchar](36) NOT NULL, + [Eyes_Item] [varchar](36) NOT NULL, + [Eyes_Asset] [varchar](36) NOT NULL, + [Shirt_Item] [varchar](36) NOT NULL, + [Shirt_Asset] [varchar](36) NOT NULL, + [Pants_Item] [varchar](36) NOT NULL, + [Pants_Asset] [varchar](36) NOT NULL, + [Shoes_Item] [varchar](36) NOT NULL, + [Shoes_Asset] [varchar](36) NOT NULL, + [Socks_Item] [varchar](36) NOT NULL, + [Socks_Asset] [varchar](36) NOT NULL, + [Jacket_Item] [varchar](36) NOT NULL, + [Jacket_Asset] [varchar](36) NOT NULL, + [Gloves_Item] [varchar](36) NOT NULL, + [Gloves_Asset] [varchar](36) NOT NULL, + [Undershirt_Item] [varchar](36) NOT NULL, + [Undershirt_Asset] [varchar](36) NOT NULL, + [Underpants_Item] [varchar](36) NOT NULL, + [Underpants_Asset] [varchar](36) NOT NULL, + [Skirt_Item] [varchar](36) NOT NULL, + [Skirt_Asset] [varchar](36) NOT NULL, + + PRIMARY KEY CLUSTERED ( + [Owner] + ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +:VERSION 2 + +BEGIN TRANSACTION + +ALTER TABLE users ADD homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE users ADD userFlags int NOT NULL default 0; +ALTER TABLE users ADD godLevel int NOT NULL default 0; +ALTER TABLE users ADD customType varchar(32) not null default ''; +ALTER TABLE users ADD partner varchar(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT + + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE TABLE [avatarattachments] ( + [UUID] varchar(36) NOT NULL + , [attachpoint] int NOT NULL + , [item] varchar(36) NOT NULL + , [asset] varchar(36) NOT NULL) + +CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_userfriends + ( + ownerID varchar(36) NOT NULL, + friendID varchar(36) NOT NULL, + friendPerms int NOT NULL, + datetimestamp int NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM userfriends) + EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) + SELECT CONVERT(varchar(36), ownerID), CONVERT(varchar(36), friendID), CONVERT(int, friendPerms), CONVERT(int, datetimestamp) FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.userfriends + +EXECUTE sp_rename N'Tmp_userfriends', N'userfriends', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON userfriends + ( + ownerID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON userfriends + ( + friendID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + + ALTER TABLE users add email varchar(250); + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_users + ( + UUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + username varchar(32) NOT NULL, + lastname varchar(32) NOT NULL, + passwordHash varchar(32) NOT NULL, + passwordSalt varchar(32) NOT NULL, + homeRegion bigint NULL DEFAULT (NULL), + homeLocationX float(53) NULL DEFAULT (NULL), + homeLocationY float(53) NULL DEFAULT (NULL), + homeLocationZ float(53) NULL DEFAULT (NULL), + homeLookAtX float(53) NULL DEFAULT (NULL), + homeLookAtY float(53) NULL DEFAULT (NULL), + homeLookAtZ float(53) NULL DEFAULT (NULL), + created int NOT NULL, + lastLogin int NOT NULL, + userInventoryURI varchar(255) NULL DEFAULT (NULL), + userAssetURI varchar(255) NULL DEFAULT (NULL), + profileCanDoMask int NULL DEFAULT (NULL), + profileWantDoMask int NULL DEFAULT (NULL), + profileAboutText ntext NULL, + profileFirstText ntext NULL, + profileImage uniqueidentifier NULL DEFAULT (NULL), + profileFirstImage uniqueidentifier NULL DEFAULT (NULL), + webLoginKey uniqueidentifier NULL DEFAULT (NULL), + homeRegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + userFlags int NOT NULL DEFAULT ((0)), + godLevel int NOT NULL DEFAULT ((0)), + customType varchar(32) NOT NULL DEFAULT (''), + partner uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + email varchar(250) NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.users) + EXEC('INSERT INTO dbo.Tmp_users (UUID, username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, profileImage, profileFirstImage, webLoginKey, homeRegionID, userFlags, godLevel, customType, partner, email) + SELECT CONVERT(uniqueidentifier, UUID), username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, CONVERT(uniqueidentifier, profileImage), CONVERT(uniqueidentifier, profileFirstImage), CONVERT(uniqueidentifier, webLoginKey), CONVERT(uniqueidentifier, homeRegionID), userFlags, godLevel, customType, CONVERT(uniqueidentifier, partner), email FROM dbo.users WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.users + +EXECUTE sp_rename N'dbo.Tmp_users', N'users', 'OBJECT' + +ALTER TABLE dbo.users ADD CONSTRAINT + PK__users__65A475E737A5467C PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX usernames ON dbo.users + ( + username, + lastname + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_agents + ( + UUID uniqueidentifier NOT NULL, + sessionID uniqueidentifier NOT NULL, + secureSessionID uniqueidentifier NOT NULL, + agentIP varchar(16) NOT NULL, + agentPort int NOT NULL, + agentOnline tinyint NOT NULL, + loginTime int NOT NULL, + logoutTime int NOT NULL, + currentRegion uniqueidentifier NOT NULL, + currentHandle bigint NOT NULL, + currentPos varchar(64) NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.agents) + EXEC('INSERT INTO dbo.Tmp_agents (UUID, sessionID, secureSessionID, agentIP, agentPort, agentOnline, loginTime, logoutTime, currentRegion, currentHandle, currentPos) + SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, sessionID), CONVERT(uniqueidentifier, secureSessionID), agentIP, agentPort, agentOnline, loginTime, logoutTime, CONVERT(uniqueidentifier, currentRegion), currentHandle, currentPos FROM dbo.agents WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.agents + +EXECUTE sp_rename N'dbo.Tmp_agents', N'agents', 'OBJECT' + +ALTER TABLE dbo.agents ADD CONSTRAINT + PK__agents__65A475E749C3F6B7 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX session ON dbo.agents + ( + sessionID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX ssession ON dbo.agents + ( + secureSessionID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 8 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_userfriends + ( + ownerID uniqueidentifier NOT NULL, + friendID uniqueidentifier NOT NULL, + friendPerms int NOT NULL, + datetimestamp int NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.userfriends) + EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) + SELECT CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, friendID), friendPerms, datetimestamp FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.userfriends + +EXECUTE sp_rename N'dbo.Tmp_userfriends', N'userfriends', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON dbo.userfriends + ( + ownerID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON dbo.userfriends + ( + friendID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 9 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_avatarappearance + ( + Owner uniqueidentifier NOT NULL, + Serial int NOT NULL, + Visual_Params image NOT NULL, + Texture image NOT NULL, + Avatar_Height float(53) NOT NULL, + Body_Item uniqueidentifier NOT NULL, + Body_Asset uniqueidentifier NOT NULL, + Skin_Item uniqueidentifier NOT NULL, + Skin_Asset uniqueidentifier NOT NULL, + Hair_Item uniqueidentifier NOT NULL, + Hair_Asset uniqueidentifier NOT NULL, + Eyes_Item uniqueidentifier NOT NULL, + Eyes_Asset uniqueidentifier NOT NULL, + Shirt_Item uniqueidentifier NOT NULL, + Shirt_Asset uniqueidentifier NOT NULL, + Pants_Item uniqueidentifier NOT NULL, + Pants_Asset uniqueidentifier NOT NULL, + Shoes_Item uniqueidentifier NOT NULL, + Shoes_Asset uniqueidentifier NOT NULL, + Socks_Item uniqueidentifier NOT NULL, + Socks_Asset uniqueidentifier NOT NULL, + Jacket_Item uniqueidentifier NOT NULL, + Jacket_Asset uniqueidentifier NOT NULL, + Gloves_Item uniqueidentifier NOT NULL, + Gloves_Asset uniqueidentifier NOT NULL, + Undershirt_Item uniqueidentifier NOT NULL, + Undershirt_Asset uniqueidentifier NOT NULL, + Underpants_Item uniqueidentifier NOT NULL, + Underpants_Asset uniqueidentifier NOT NULL, + Skirt_Item uniqueidentifier NOT NULL, + Skirt_Asset uniqueidentifier NOT NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.avatarappearance) + EXEC('INSERT INTO dbo.Tmp_avatarappearance (Owner, Serial, Visual_Params, Texture, Avatar_Height, Body_Item, Body_Asset, Skin_Item, Skin_Asset, Hair_Item, Hair_Asset, Eyes_Item, Eyes_Asset, Shirt_Item, Shirt_Asset, Pants_Item, Pants_Asset, Shoes_Item, Shoes_Asset, Socks_Item, Socks_Asset, Jacket_Item, Jacket_Asset, Gloves_Item, Gloves_Asset, Undershirt_Item, Undershirt_Asset, Underpants_Item, Underpants_Asset, Skirt_Item, Skirt_Asset) + SELECT CONVERT(uniqueidentifier, Owner), Serial, Visual_Params, Texture, Avatar_Height, CONVERT(uniqueidentifier, Body_Item), CONVERT(uniqueidentifier, Body_Asset), CONVERT(uniqueidentifier, Skin_Item), CONVERT(uniqueidentifier, Skin_Asset), CONVERT(uniqueidentifier, Hair_Item), CONVERT(uniqueidentifier, Hair_Asset), CONVERT(uniqueidentifier, Eyes_Item), CONVERT(uniqueidentifier, Eyes_Asset), CONVERT(uniqueidentifier, Shirt_Item), CONVERT(uniqueidentifier, Shirt_Asset), CONVERT(uniqueidentifier, Pants_Item), CONVERT(uniqueidentifier, Pants_Asset), CONVERT(uniqueidentifier, Shoes_Item), CONVERT(uniqueidentifier, Shoes_Asset), CONVERT(uniqueidentifier, Socks_Item), CONVERT(uniqueidentifier, Socks_Asset), CONVERT(uniqueidentifier, Jacket_Item), CONVERT(uniqueidentifier, Jacket_Asset), CONVERT(uniqueidentifier, Gloves_Item), CONVERT(uniqueidentifier, Gloves_Asset), CONVERT(uniqueidentifier, Undershirt_Item), CONVERT(uniqueidentifier, Undershirt_Asset), CONVERT(uniqueidentifier, Underpants_Item), CONVERT(uniqueidentifier, Underpants_Asset), CONVERT(uniqueidentifier, Skirt_Item), CONVERT(uniqueidentifier, Skirt_Asset) FROM dbo.avatarappearance WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.avatarappearance + +EXECUTE sp_rename N'dbo.Tmp_avatarappearance', N'avatarappearance', 'OBJECT' + +ALTER TABLE dbo.avatarappearance ADD CONSTRAINT + PK__avatarap__7DD115CC4E88ABD4 PRIMARY KEY CLUSTERED + ( + Owner + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 10 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_avatarattachments + ( + UUID uniqueidentifier NOT NULL, + attachpoint int NOT NULL, + item uniqueidentifier NOT NULL, + asset uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.avatarattachments) + EXEC('INSERT INTO dbo.Tmp_avatarattachments (UUID, attachpoint, item, asset) + SELECT CONVERT(uniqueidentifier, UUID), attachpoint, CONVERT(uniqueidentifier, item), CONVERT(uniqueidentifier, asset) FROM dbo.avatarattachments WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.avatarattachments + +EXECUTE sp_rename N'dbo.Tmp_avatarattachments', N'avatarattachments', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 11 + +BEGIN TRANSACTION + +ALTER TABLE users ADD scopeID uniqueidentifier not null default '00000000-0000-0000-0000-000000000000' + +COMMIT From 49f4becdae9faf4afbf03c4e464237e323de496d Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 16 May 2010 08:10:57 -0700 Subject: [PATCH 135/260] * Fixed configs in StandaloneHypergrid.ini, it still had the SQLite connection strings. * Added a comment in LLLoginService.cs, for future reference --- OpenSim/Services/LLLoginService/LLLoginService.cs | 6 ++++-- bin/config-include/StandaloneHypergrid.ini | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 9d151dfe18..712b8991b4 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -746,8 +746,10 @@ namespace OpenSim.Services.LLLoginService m_log.Debug("[LLOGIN SERVICE] Launching agent at " + destination.RegionName); if (m_UserAgentService.LoginAgentToGrid(aCircuit, gatekeeper, destination, out reason)) { - //IPAddress addr = NetworkUtil.GetIPFor(clientIP.Address, gatekeeper.ExternalEndPoint.Address); - m_UserAgentService.SetClientToken(aCircuit.SessionID, clientIP.Address.ToString()); + // We may need to do this at some point, + // so leaving it here in comments. + //IPAddress addr = NetworkUtil.GetIPFor(clientIP.Address, destination.ExternalEndPoint.Address); + m_UserAgentService.SetClientToken(aCircuit.SessionID, /*addr.Address.ToString() */ clientIP.Address.ToString()); return true; } return false; diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini index 32b240b8b5..52e30e211c 100644 --- a/bin/config-include/StandaloneHypergrid.ini +++ b/bin/config-include/StandaloneHypergrid.ini @@ -42,7 +42,6 @@ [AvatarService] LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" - ConnectionString = "URI=file:avatars.db,version=3" [LibraryService] LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" @@ -54,7 +53,6 @@ [AuthenticationService] LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - ConnectionString = "URI=file:auth.db,version=3" [GridService] ; LocalGridServicesConnector needs this @@ -82,7 +80,6 @@ [FriendsService] LocalServiceModule = "OpenSim.Services.FriendsService.dll" - ConnectionString = "URI=file:friends.db,version=3" [Friends] Connector = "OpenSim.Services.FriendsService.dll" From b7e6b58857b147ac526669b38535b13c75d0dbd3 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 16 May 2010 09:01:27 -0700 Subject: [PATCH 136/260] Fixes mantis #4622. --- OpenSim/Region/Framework/Scenes/Scene.cs | 37 +++++++++--------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 401551de36..de8ecc2713 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3355,7 +3355,6 @@ namespace OpenSim.Region.Framework.Scenes ///
public void RegisterCommsEvents() { - m_sceneGridService.OnExpectUser += HandleNewUserConnection; m_sceneGridService.OnAvatarCrossingIntoRegion += AgentCrossing; m_sceneGridService.OnCloseAgentConnection += IncomingCloseAgent; //m_eventManager.OnRegionUp += OtherRegionUp; @@ -3376,7 +3375,6 @@ namespace OpenSim.Region.Framework.Scenes //m_sceneGridService.OnRemoveKnownRegionFromAvatar -= HandleRemoveKnownRegionsFromAvatar; //m_sceneGridService.OnChildAgentUpdate -= IncomingChildAgentDataUpdate; //m_eventManager.OnRegionUp -= OtherRegionUp; - m_sceneGridService.OnExpectUser -= HandleNewUserConnection; m_sceneGridService.OnAvatarCrossingIntoRegion -= AgentCrossing; m_sceneGridService.OnCloseAgentConnection -= IncomingCloseAgent; m_sceneGridService.OnGetLandData -= GetLandData; @@ -3388,22 +3386,6 @@ namespace OpenSim.Region.Framework.Scenes m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", m_regInfo.RegionName); } - /// - /// A handler for the SceneCommunicationService event, to match that events return type of void. - /// Use NewUserConnection() directly if possible so the return type can refuse connections. - /// At the moment nothing actually seems to use this event, - /// as everything is switching to calling the NewUserConnection method directly. - /// - /// Now obsoleting this because it doesn't handle teleportFlags propertly - /// - /// - /// - [Obsolete("Please call NewUserConnection directly.")] - public void HandleNewUserConnection(AgentCircuitData agent) - { - string reason; - NewUserConnection(agent, 0, out reason); - } /// /// Do the work necessary to initiate a new user connection for a particular scene. @@ -3465,13 +3447,22 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence sp = GetScenePresence(agent.AgentID); if (sp != null) { - m_log.DebugFormat( - "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", - agent.AgentID, RegionInfo.RegionName); + if (sp.IsChildAgent) + { + m_log.DebugFormat( + "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", + agent.AgentID, RegionInfo.RegionName); - sp.AdjustKnownSeeds(); + sp.AdjustKnownSeeds(); - return true; + return true; + } + else + { + // We have a zombie from a crashed session. Kill it. + m_log.DebugFormat("[SCENE]: Zombie scene presence detected for {0} in {1}", agent.AgentID, RegionInfo.RegionName); + sp.ControllingClient.Close(); + } } CapsModule.AddCapsHandler(agent.AgentID); From 0b43b263d48b576df12fd88fc303ce9cfe2066ec Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 16 May 2010 09:12:40 -0700 Subject: [PATCH 137/260] Fixes mantis #4691 --- OpenSim/Services/InventoryService/XInventoryService.cs | 2 +- OpenSim/Services/LLLoginService/LLLoginResponse.cs | 4 +++- OpenSim/Services/LLLoginService/LLLoginService.cs | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/OpenSim/Services/InventoryService/XInventoryService.cs b/OpenSim/Services/InventoryService/XInventoryService.cs index 4d7103bf73..974caf0d7e 100644 --- a/OpenSim/Services/InventoryService/XInventoryService.cs +++ b/OpenSim/Services/InventoryService/XInventoryService.cs @@ -386,7 +386,7 @@ namespace OpenSim.Services.InventoryService XInventoryItem[] items = m_Database.GetActiveGestures(principalID); if (items.Length == 0) - return null; + return new List(); List ret = new List(); diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs index d1dcfe7579..32809caf30 100644 --- a/OpenSim/Services/LLLoginService/LLLoginResponse.cs +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -217,12 +217,14 @@ namespace OpenSim.Services.LLLoginService public LLLoginResponse(UserAccount account, AgentCircuitData aCircuit, GridUserInfo pinfo, GridRegion destination, List invSkel, FriendInfo[] friendsList, ILibraryService libService, - string where, string startlocation, Vector3 position, Vector3 lookAt, string message, + string where, string startlocation, Vector3 position, Vector3 lookAt, List gestures, string message, GridRegion home, IPEndPoint clientIP) : this() { FillOutInventoryData(invSkel, libService); + ActiveGestures = new ArrayList(gestures); + CircuitCode = (int)aCircuit.circuitcode; Lastname = account.LastName; Firstname = account.FirstName; diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 712b8991b4..535cf42a5a 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -272,6 +272,9 @@ namespace OpenSim.Services.LLLoginService return LLFailedLoginResponse.InventoryProblem; } + // Get active gestures + List gestures = m_InventoryService.GetActiveGestures(account.PrincipalID); + // // Login the presence // @@ -350,7 +353,7 @@ namespace OpenSim.Services.LLLoginService // Finally, fill out the response and return it // LLLoginResponse response = new LLLoginResponse(account, aCircuit, guinfo, destination, inventorySkel, friendsList, m_LibraryService, - where, startLocation, position, lookAt, m_WelcomeMessage, home, clientIP); + where, startLocation, position, lookAt, gestures, m_WelcomeMessage, home, clientIP); m_log.DebugFormat("[LLOGIN SERVICE]: All clear. Sending login response to client."); return response; From 2fb79646c6622b7a6318d9971e0462c559e4ccfd Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 16 May 2010 10:32:57 -0700 Subject: [PATCH 138/260] Fixes mantis #4691 for real. This time I tested it, and it works. --- .../Services/LLLoginService/LLLoginResponse.cs | 18 +++++++++++++++++- .../Services/LLLoginService/LLLoginService.cs | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs index 32809caf30..54d53fb2a4 100644 --- a/OpenSim/Services/LLLoginService/LLLoginResponse.cs +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -223,7 +223,7 @@ namespace OpenSim.Services.LLLoginService { FillOutInventoryData(invSkel, libService); - ActiveGestures = new ArrayList(gestures); + FillOutActiveGestures(gestures); CircuitCode = (int)aCircuit.circuitcode; Lastname = account.LastName; @@ -285,6 +285,22 @@ namespace OpenSim.Services.LLLoginService } } + private void FillOutActiveGestures(List gestures) + { + ArrayList list = new ArrayList(); + if (gestures != null) + { + foreach (InventoryItemBase gesture in gestures) + { + Hashtable item = new Hashtable(); + item["item_id"] = gesture.ID.ToString(); + item["asset_id"] = gesture.AssetID.ToString(); + list.Add(item); + } + } + ActiveGestures = list; + } + private void FillOutHomeData(GridUserInfo pinfo, GridRegion home) { int x = 1000 * (int)Constants.RegionSize, y = 1000 * (int)Constants.RegionSize; diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 535cf42a5a..6319cc4a13 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -274,6 +274,7 @@ namespace OpenSim.Services.LLLoginService // Get active gestures List gestures = m_InventoryService.GetActiveGestures(account.PrincipalID); + m_log.DebugFormat("[LLOGIN SERVICE]: {0} active gestures", gestures.Count); // // Login the presence From a6023ea89e4ef4bfd76ed7759b273f2ae8dc16cd Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 16 May 2010 20:31:02 +0100 Subject: [PATCH 139/260] Prevent a crash when the two agent dictionaries get out of sync --- OpenSim/Framework/AgentCircuitManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/AgentCircuitManager.cs b/OpenSim/Framework/AgentCircuitManager.cs index 49d7822416..1ce8c34079 100644 --- a/OpenSim/Framework/AgentCircuitManager.cs +++ b/OpenSim/Framework/AgentCircuitManager.cs @@ -92,7 +92,7 @@ namespace OpenSim.Framework else { AgentCircuits.Add(circuitCode, agentData); - AgentCircuitsByUUID.Add(agentData.AgentID, agentData); + AgentCircuitsByUUID[agentData.AgentID] = agentData; } } } From df04ccfdc3918acf40e3ecd823ee3e323fcac49d Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 18 May 2010 15:30:08 +0100 Subject: [PATCH 140/260] Change an exception reporting to include the stack trace. "e.Message" just _won't_ do. Might look more elegant, but it's too little information. --- OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs index a29ac2887c..d7cb015f6a 100644 --- a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs +++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs @@ -158,7 +158,7 @@ namespace OpenSim.Services.Connectors.Friends } catch (Exception e) { - m_log.DebugFormat("[FRIENDS CONNECTOR]: Exception when contacting remote sim: {0}", e.Message); + m_log.DebugFormat("[FRIENDS CONNECTOR]: Exception when contacting remote sim: {0}", e.ToString()); } return false; From 3e1f2ddb8e296a7fef9b7887fa9fda48f4eb2abf Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 18 May 2010 19:25:40 +0200 Subject: [PATCH 141/260] Allow remote admin to be used on a different port from the main region port --- .../ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs | 5 +++-- bin/OpenSim.ini.example | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index c5346d4ac0..7ebb5de6dd 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -63,7 +63,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController private static Object SOLock = new Object(); private OpenSimBase m_app; - private BaseHttpServer m_httpd; + private IHttpServer m_httpd; private IConfig m_config; private IConfigSource m_configSource; private string m_requiredPassword = String.Empty; @@ -113,9 +113,10 @@ namespace OpenSim.ApplicationPlugins.RemoteController m_config = m_configSource.Configs["RemoteAdmin"]; m_log.Info("[RADMIN]: Remote Admin Plugin Enabled"); m_requiredPassword = m_config.GetString("access_password", String.Empty); + int port = m_config.GetInt("port", 0); m_app = openSim; - m_httpd = openSim.HttpServer; + m_httpd = MainServer.GetHttpServer((uint)port); Dictionary availableMethods = new Dictionary(); availableMethods["admin_create_region"] = XmlRpcCreateRegionMethod; diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index a5eb78b777..7b427a5fd2 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -591,6 +591,10 @@ [RemoteAdmin] enabled = false + + ; Set this to a nonzero value to have remote admin use a different port + port = 0 + access_password = unknown ; set this variable to true if you want the create_region XmlRpc From d95f232629aaef110bb9e0cd2fe7d209781466e1 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 18 May 2010 01:44:51 +0300 Subject: [PATCH 142/260] Massive bugfix in MSSQLEstateData (now works!) --- OpenSim/Data/MSSQL/MSSQLEstateData.cs | 94 ++++++++++++--------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/OpenSim/Data/MSSQL/MSSQLEstateData.cs b/OpenSim/Data/MSSQL/MSSQLEstateData.cs index 474f706ed3..66931e2423 100644 --- a/OpenSim/Data/MSSQL/MSSQLEstateData.cs +++ b/OpenSim/Data/MSSQL/MSSQLEstateData.cs @@ -101,22 +101,30 @@ namespace OpenSim.Data.MSSQL { foreach (string name in FieldList) { - if (_FieldMap[name].GetValue(es) is bool) + FieldInfo f = _FieldMap[name]; + object v = reader[name]; + if (f.FieldType == typeof(bool) ) { - int v = Convert.ToInt32(reader[name]); - if (v != 0) - _FieldMap[name].SetValue(es, true); - else - _FieldMap[name].SetValue(es, false); + f.SetValue(es, Convert.ToInt32(v) != 0); } - else if (_FieldMap[name].GetValue(es) is UUID) + else if (f.FieldType == typeof(UUID) ) { - _FieldMap[name].SetValue(es, new UUID((Guid)reader[name])); // uuid); + f.SetValue(es, new UUID((Guid)v)); // uuid); + } + else if (f.FieldType == typeof(string)) + { + f.SetValue(es, v.ToString()); + } + else if (f.FieldType == typeof(UInt32)) + { + f.SetValue(es, Convert.ToUInt32(v)); + } + else if (f.FieldType == typeof(Single)) + { + f.SetValue(es, Convert.ToSingle(v)); } else - { - es.EstateID = Convert.ToUInt32(reader["EstateID"].ToString()); - } + f.SetValue(es, v); } } else @@ -288,61 +296,45 @@ namespace OpenSim.Data.MSSQL private void SaveBanList(EstateSettings es) { //Delete first - string sql = "delete from estateban where EstateID = @EstateID"; using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) { - cmd.Parameters.Add(_Database.CreateParameter("@EstateID", es.EstateID)); conn.Open(); - cmd.ExecuteNonQuery(); - } - - //Insert after - sql = "insert into estateban (EstateID, bannedUUID) values ( @EstateID, @bannedUUID )"; - using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) - { - foreach (EstateBan b in es.EstateBans) + using (SqlCommand cmd = conn.CreateCommand()) { - cmd.Parameters.Add(_Database.CreateParameter("@EstateID", es.EstateID)); - cmd.Parameters.Add(_Database.CreateParameter("@bannedUUID", b.BannedUserID)); - conn.Open(); + cmd.CommandText = "delete from estateban where EstateID = @EstateID"; + cmd.Parameters.AddWithValue("@EstateID", (int)es.EstateID); cmd.ExecuteNonQuery(); - cmd.Parameters.Clear(); + + //Insert after + cmd.CommandText = "insert into estateban (EstateID, bannedUUID) values ( @EstateID, @bannedUUID )"; + cmd.Parameters.AddWithValue("@bannedUUID", Guid.Empty); + foreach (EstateBan b in es.EstateBans) + { + cmd.Parameters["@bannedUUID"].Value = b.BannedUserID.Guid; + cmd.ExecuteNonQuery(); + } } } } private void SaveUUIDList(uint estateID, string table, UUID[] data) { - //Delete first - string sql = string.Format("delete from {0} where EstateID = @EstateID", table); using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) { - cmd.Parameters.Add(_Database.CreateParameter("@EstateID", estateID)); - cmd.ExecuteNonQuery(); - } - - sql = string.Format("insert into {0} (EstateID, uuid) values ( @EstateID, @uuid )", table); - using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) - { - cmd.Parameters.Add(_Database.CreateParameter("@EstateID", estateID)); - - bool createParamOnce = true; - - foreach (UUID uuid in data) + conn.Open(); + using (SqlCommand cmd = conn.CreateCommand()) { - if (createParamOnce) - { - cmd.Parameters.Add(_Database.CreateParameter("@uuid", uuid)); - createParamOnce = false; - } - else - cmd.Parameters["@uuid"].Value = uuid.Guid; //.ToString(); //TODO check if this works - conn.Open(); + cmd.Parameters.AddWithValue("@EstateID", (int)estateID); + cmd.CommandText = string.Format("delete from {0} where EstateID = @EstateID", table); cmd.ExecuteNonQuery(); + + cmd.CommandText = string.Format("insert into {0} (EstateID, uuid) values ( @EstateID, @uuid )", table); + cmd.Parameters.AddWithValue("@uuid", Guid.Empty); + foreach (UUID uuid in data) + { + cmd.Parameters["@uuid"].Value = uuid.Guid; //.ToString(); //TODO check if this works + cmd.ExecuteNonQuery(); + } } } } From f4450ccf4f76db7f6e18c483ac48f30e0907eb2a Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 16 May 2010 16:22:38 +0300 Subject: [PATCH 143/260] Migration.cs supports single-file migration history format Scans for migration resources in either old-style "scattered" (one file per version) or new-style "integrated" format (single file "Resources/{StoreName}.migrations[.nnn]") with ":VERSION nnn" sections). In the new-style migrations it also recognizes ':GO' separators for parts of the SQL script that must be sent to the server separately. The old-style migrations are loaded each in one piece and don't support the ':GO' feature. Status: TESTED and works fine in all modes! --- OpenSim/Data/Migration.cs | 347 ++++++++++++++++++++++++++------------ 1 file changed, 238 insertions(+), 109 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 06defe4e36..7980c35ba0 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -70,61 +70,111 @@ namespace OpenSim.Data public class Migration { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private string _type; - private DbConnection _conn; - // private string _subtype; - private Assembly _assem; - private Regex _match; + protected string _type; + protected DbConnection _conn; + protected Assembly _assem; - private static readonly string _migrations_create = "create table migrations(name varchar(100), version int)"; - // private static readonly string _migrations_init = "insert into migrations values('migrations', 1)"; - // private static readonly string _migrations_find = "select version from migrations where name='migrations'"; + private Regex _match_old; + private Regex _match_new; - - public Migration(DbConnection conn, Assembly assem, string type) - { - _type = type; - _conn = conn; - _assem = assem; - _match = new Regex(@"\.(\d\d\d)_" + _type + @"\.sql"); - Initialize(); + /// Have the parameterless constructor just so we can specify it as a generic parameter with the new() constraint. + /// Currently this is only used in the tests. A Migration instance created this way must be then + /// initialized with Initialize(). Regular creation should be through the parameterized constructors. + /// + public Migration() + { } public Migration(DbConnection conn, Assembly assem, string subtype, string type) + { + Initialize(conn, assem, type, subtype); + } + + public Migration(DbConnection conn, Assembly assem, string type) + { + Initialize(conn, assem, type, ""); + } + + /// Must be called after creating with the parameterless constructor. + /// NOTE that the Migration class now doesn't access database in any way during initialization. + /// Specifically, it won't check if the [migrations] table exists. Such checks are done later: + /// automatically on Update(), or you can explicitly call InitMigrationsTable(). + /// + /// + /// + /// + /// + public void Initialize (DbConnection conn, Assembly assem, string type, string subtype) { _type = type; _conn = conn; _assem = assem; - _match = new Regex(subtype + @"\.(\d\d\d)_" + _type + @"\.sql"); - Initialize(); + _match_old = new Regex(subtype + @"\.(\d\d\d)_" + _type + @"\.sql"); + string s = String.IsNullOrEmpty(subtype) ? _type : _type + @"\." + subtype; + _match_new = new Regex(@"\." + s + @"\.migrations(?:\.(?\d+)$|.*)"); } - private void Initialize() + public void InitMigrationsTable() { - // clever, eh, we figure out which migrations version we are - int migration_version = FindVersion(_conn, "migrations"); - - if (migration_version > 0) - return; - - // If not, create the migration tables - using (DbCommand cmd = _conn.CreateCommand()) + // NOTE: normally when the [migrations] table is created, the version record for 'migrations' is + // added immediately. However, if for some reason the table is there but empty, we want to handle that as well. + int ver = FindVersion(_conn, "migrations"); + if (ver <= 0) // -1 = no table, 0 = no version record { - cmd.CommandText = _migrations_create; - cmd.ExecuteNonQuery(); + if( ver < 0 ) + ExecuteScript("create table migrations(name varchar(100), version int)"); + InsertVersion("migrations", 1); } - - InsertVersion("migrations", 1); } + /// Executes a script, possibly in a database-specific way. + /// It can be redefined for a specific DBMS, if necessary. Specifically, + /// to avoid problems with proc definitions in MySQL, we must use + /// MySqlScript class instead of just DbCommand. We don't want to bring + /// MySQL references here, so instead define a MySQLMigration class + /// in OpenSim.Data.MySQL + /// + /// + /// Array of strings, one-per-batch (often just one) + protected virtual void ExecuteScript(DbConnection conn, string[] script) + { + using (DbCommand cmd = conn.CreateCommand()) + { + cmd.CommandTimeout = 0; + foreach (string sql in script) + { + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + } + } + + protected void ExecuteScript(DbConnection conn, string sql) + { + ExecuteScript(conn, new string[]{sql}); + } + + protected void ExecuteScript(string sql) + { + ExecuteScript(_conn, sql); + } + + protected void ExecuteScript(string[] script) + { + ExecuteScript(_conn, script); + } + + + public void Update() { - int version = 0; - version = FindVersion(_conn, _type); + InitMigrationsTable(); - SortedList migrations = GetMigrationsAfter(version); + int version = FindVersion(_conn, _type); + + SortedList migrations = GetMigrationsAfter(version); if (migrations.Count < 1) return; @@ -132,57 +182,41 @@ namespace OpenSim.Data m_log.InfoFormat("[MIGRATIONS] Upgrading {0} to latest revision {1}.", _type, migrations.Keys[migrations.Count - 1]); m_log.Info("[MIGRATIONS] NOTE: this may take a while, don't interupt this process!"); - using (DbCommand cmd = _conn.CreateCommand()) + foreach (KeyValuePair kvp in migrations) { - foreach (KeyValuePair kvp in migrations) - { - int newversion = kvp.Key; - cmd.CommandText = kvp.Value; - // we need to up the command timeout to infinite as we might be doing long migrations. - cmd.CommandTimeout = 0; - try - { - cmd.ExecuteNonQuery(); - } - catch (Exception e) - { - m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", cmd.CommandText); - m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); - cmd.CommandText = "ROLLBACK;"; - cmd.ExecuteNonQuery(); - } + int newversion = kvp.Key; + // we need to up the command timeout to infinite as we might be doing long migrations. - if (version == 0) - { - InsertVersion(_type, newversion); - } - else - { - UpdateVersion(_type, newversion); - } - version = newversion; + /* [AlexRa 01-May-10]: We can't always just run any SQL in a single batch (= ExecuteNonQuery()). Things like + * stored proc definitions might have to be sent to the server each in a separate batch. + * This is certainly so for MS SQL; not sure how the MySQL connector sorts out the mess + * with 'delimiter @@'/'delimiter ;' around procs. So each "script" this code executes now is not + * a single string, but an array of strings, executed separately. + */ + try + { + ExecuteScript(kvp.Value); } + catch (Exception e) + { + m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", kvp.Value.ToString()); + m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Migration aborted.", e.Message); + ExecuteScript("ROLLBACK;"); + return; + } + + if (version == 0) + { + InsertVersion(_type, newversion); + } + else + { + UpdateVersion(_type, newversion); + } + version = newversion; } } - // private int MaxVersion() - // { - // int max = 0; - // string[] names = _assem.GetManifestResourceNames(); - - // foreach (string s in names) - // { - // Match m = _match.Match(s); - // if (m.Success) - // { - // int MigrationVersion = int.Parse(m.Groups[1].ToString()); - // if (MigrationVersion > max) - // max = MigrationVersion; - // } - // } - // return max; - // } - public int Version { get { return FindVersion(_conn, _type); } @@ -206,7 +240,7 @@ namespace OpenSim.Data try { cmd.CommandText = "select version from migrations where name='" + type + "' order by version desc"; - using (IDataReader reader = cmd.ExecuteReader()) + using (DbDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { @@ -217,7 +251,8 @@ namespace OpenSim.Data } catch { - // Something went wrong, so we're version 0 + // Something went wrong (probably no table), so we're at version -1 + version = -1; } } return version; @@ -225,57 +260,151 @@ namespace OpenSim.Data private void InsertVersion(string type, int version) { - using (DbCommand cmd = _conn.CreateCommand()) - { - cmd.CommandText = "insert into migrations(name, version) values('" + type + "', " + version + ")"; - m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); - cmd.ExecuteNonQuery(); - } + m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); + ExecuteScript("insert into migrations(name, version) values('" + type + "', " + version + ")"); } private void UpdateVersion(string type, int version) { - using (DbCommand cmd = _conn.CreateCommand()) - { - cmd.CommandText = "update migrations set version=" + version + " where name='" + type + "'"; - m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); - cmd.ExecuteNonQuery(); - } + m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); + ExecuteScript("update migrations set version=" + version + " where name='" + type + "'"); } - // private SortedList GetAllMigrations() - // { - // return GetMigrationsAfter(0); - // } + private delegate void FlushProc(); - private SortedList GetMigrationsAfter(int after) + /// Scans for migration resources in either old-style "scattered" (one file per version) + /// or new-style "integrated" format (single file with ":VERSION nnn" sections). + /// In the new-style migrations it also recognizes ':GO' separators for parts of the SQL script + /// that must be sent to the server separately. The old-style migrations are loaded each in one piece + /// and don't support the ':GO' feature. + /// + /// The version we are currently at. Scan for any higher versions + /// A list of string arrays, representing the scripts. + private SortedList GetMigrationsAfter(int after) { - string[] names = _assem.GetManifestResourceNames(); - SortedList migrations = new SortedList(); - // because life is funny if we don't - Array.Sort(names); + SortedList migrations = new SortedList(); + string[] names = _assem.GetManifestResourceNames(); + if( names.Length == 0 ) // should never happen + return migrations; + + Array.Sort(names); // we want all the migrations ordered + + int nLastVerFound = 0; + Match m = null; + string sFile = Array.FindLast(names, nm => { m = _match_new.Match(nm); return m.Success; }); // ; nm.StartsWith(sPrefix, StringComparison.InvariantCultureIgnoreCase + + if( (m != null) && !String.IsNullOrEmpty(sFile) ) + { + /* The filename should be '.migrations[.NNN]' where NNN + * is the last version number defined in the file. If the '.NNN' part is recognized, the code can skip + * the file without looking inside if we have a higher version already. Without the suffix we read + * the file anyway and use the version numbers inside. Any unrecognized suffix (such as '.sql') + * is valid but ignored. + * + * NOTE that we expect only one 'merged' migration file. If there are several, we take the last one. + * If you are numbering them, leave only the latest one in the project or at least make sure they numbered + * to come up in the correct order (e.g. 'SomeStore.migrations.001' rather than 'SomeStore.migrations.1') + */ + + if (m.Groups.Count > 1 && int.TryParse(m.Groups[1].Value, out nLastVerFound)) + { + if( nLastVerFound <= after ) + goto scan_old_style; + } + + System.Text.StringBuilder sb = new System.Text.StringBuilder(4096); + int nVersion = -1; + + List script = new List(); + + FlushProc flush = delegate() + { + if (sb.Length > 0) // last SQL stmt to script list + { + script.Add(sb.ToString()); + sb.Length = 0; + } + + if ( (nVersion > 0) && (nVersion > after) && (script.Count > 0) && !migrations.ContainsKey(nVersion)) // script to the versioned script list + { + migrations[nVersion] = script.ToArray(); + } + script.Clear(); + }; + + using (Stream resource = _assem.GetManifestResourceStream(sFile)) + using (StreamReader resourceReader = new StreamReader(resource)) + { + int nLineNo = 0; + while (!resourceReader.EndOfStream) + { + string sLine = resourceReader.ReadLine(); + nLineNo++; + + if( String.IsNullOrEmpty(sLine) || sLine.StartsWith("#") ) // ignore a comment or empty line + continue; + + if (sLine.Trim().Equals(":GO", StringComparison.InvariantCultureIgnoreCase)) + { + if (sb.Length == 0) continue; + if (nVersion > after) + script.Add(sb.ToString()); + sb.Length = 0; + continue; + } + + if (sLine.StartsWith(":VERSION ", StringComparison.InvariantCultureIgnoreCase)) // ":VERSION nnn" + { + flush(); + + int n = sLine.IndexOf('#'); // Comment is allowed in version sections, ignored + if (n >= 0) + sLine = sLine.Substring(0, n); + + if (!int.TryParse(sLine.Substring(9).Trim(), out nVersion)) + { + m_log.ErrorFormat("[MIGRATIONS]: invalid version marker at {0}: line {1}. Migration failed!", sFile, nLineNo); + break; + } + } + else + { + sb.AppendLine(sLine); + } + } + flush(); + + // If there are scattered migration files as well, only look for those with higher version numbers. + if (after < nVersion) + after = nVersion; + } + } + +scan_old_style: + // scan "old style" migration pieces anyway, ignore any versions already filled from the single file foreach (string s in names) { - Match m = _match.Match(s); + m = _match_old.Match(s); if (m.Success) { int version = int.Parse(m.Groups[1].ToString()); - if (version > after) + if ( (version > after) && !migrations.ContainsKey(version) ) { using (Stream resource = _assem.GetManifestResourceStream(s)) { using (StreamReader resourceReader = new StreamReader(resource)) { - string resourceString = resourceReader.ReadToEnd(); - migrations.Add(version, resourceString); + string sql = resourceReader.ReadToEnd(); + migrations.Add(version, new string[]{sql}); } } } } } - - if (migrations.Count < 1) { + + if (migrations.Count < 1) + { m_log.InfoFormat("[MIGRATIONS]: {0} up to date, no migrations to apply", _type); } return migrations; From ade2e5a9d21f7ee10afea4f315e37999b4b760a0 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 16 May 2010 16:24:50 +0300 Subject: [PATCH 144/260] Embedded MySql.Data.dll updated to 6.2.3.0. This is necessary to correct a known problem with the DELIMITER command in previous versions of the client library. --- bin/MySql.Data.dll | Bin 294912 -> 335872 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/MySql.Data.dll b/bin/MySql.Data.dll index a94dd3def8deda655b3e0a9304dfb05e135bd398..7aa95ec345a5d23b2cb6a7da21c534328e65da90 100644 GIT binary patch literal 335872 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

    ^6&qHKa`LQKA!SalzK~7> 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 literal 294912 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

    ^abKQk1yq46uo8E;QUde6j9mXrk zjlCmyCA+bA6z?S+?^3*y-#FB*6en8E6Q+4THkAwRb{4qX`0b6~gn6Z@qnpnm;i%Es zQ>+HtiXyI)eNn`9vM-9bPWDBYy>Cy$1@;>r$c)>4_!7^C;MrDqmb+M0(HQDyM@;pW zyUD6X4SeFL+{J9sb~VP@f%YbrUO6j;m+Qx(Ez4cJFAcC!%-tlrG*NE83)w;Xr<2su;8(wJ7zMZ+1Z1!h%3`wF8zgoDJ`k`W^ zzd&an7KJZqx9~WkbX)Nin7L%qr!1c-qmYde!o4+c4Wi)$6Rbc)h?mx`Sco&}%|=aXw&MZDh@gV8p=(U`y=E1xsTWpB2=+ zcsNLx$tyuzT*{;MLq=x;_y*nYV%6p%Xxyr@*ZEpEy-)nHnVmM*qYV`L3rUw|1$J?r zikn&XNvj>=sss7_=g*|&tN9BoMf zDtRk{%h>hdI;(Lp<#4HIJIZqI^TKC>k0;XaQ^&BC$JE$&kw8C$OgxuP#`W<(0Qbv; zyZN*kK4~}kNgg6^UM|St<0;4s1i305=QV;{pN70ykawpcHw*IFG~}Ct{96aYOej14 zQ=cP6_IoCZgo3g2&Kf( zO(yel$*ys(ajLrDhh|tPh9y1ZR4)yvs_xt^#QQT%aX~KxmiW~3e5~jN`gx%xQYVo# zPURH7oGN2qOJX}#n(^}Wey=u+3Q+3}Y{uYPu4UT%5vcS1UU&t_Y`e3ZuRm!?##MZ< zhM|OT+TI0NXU)ni%U)nj+?@N!B?1AZSJutmIeqWlB{D1krv}E8! z%1~@BIzsWkuS%aRwnV4!Yg?l{#7 zci^aAGPvts{y-x2`@WlJxJgw!b6Bmhd2h*|3b<5m12%*Z|+)Rw)R#uCqqXdL)S=#j2s>@eG-GwWS)&y>{b5e z9(9*2x=Wi6$3^$2wE5`oLF3%ft8msfBz+iXU)WF7w*|g0qH^D&^ZC6Ty_$HINq4@7 z_YW+j*n#Thrs$1oH1_M>f?a`f@#tbG(Qc}~Ocqm_PJfhr-6O%uXU{bFDmO3k*jq9E z39O++g%Mg^v(ovhot!E36Ud^$tL?I**L2GETAb8&be%whj&IL$2z)~j(fgIM(e?ap z?uJ42Dcc{I`QX=Hm+ zR0R=W7JPagMF{ijTMlmu`xxRPB;LF@>R24TlSIJ9ZavDbx%~$y-zzC!%%Tg3aVz;7 zzsy6E>v+05hpgE6Xv1My+nsW99xLV3$t*Rp$!=85$J9+sdByO%aKRBc_b#&MC4owA zZV3gB3!q{YXItz=Y9GqV$dXP>vod7uy!SIy23OeVAHcGiV*yL8qz<*d2&&vKz0hCX z?fLGspvhgyFQaJK3gDt}a75hg)*+gFVS~>4eBWe2=g_Y;UP*2viQU-r>}qeY&O_&d zoCBm}l3mUZ&Ap!@bQg5cqaFQ1JCag}K1@*F5TpX9L8NM-Ub2}w)2F}hP-R zZ(Km@uk$76nASPMSQ)+>;nCEJnSKem>9RXruYv}>3dRC9`X{V%zOC+>?nPZpX2NC_ zhbDbyHvFlTLh?|sd(f{|IkGKw=Xf(dTiaM&QwYXMc=_3->~Pz6_}O&0L3MBh9oJ+Y zv{n;e-J{ZxXor`0NP}l8e15wt+@0I>QwopzB-vpPWeE2N{VMS}XS$78Q|zjrq$|wk z)~_yGZ^(Pw)^)C~TBqq&U*U^I!9RoYl3bOLed$i!3N4Jn$PuTwhV~oX=vuS+<<4Nc-@h>_&904nkY#O>Q}hL4YXCZk%arH zy!1hWz_%B)2~i#8_>N_Fsvm?0JBGN_8 zKR@}we4EjB1}|oGa8Yh{b-DQjYHjX`s;~t@cnO+Ui&OS&V2hJun)Oc0sYqa zds+r9LE!qCiNETiWnDBTpB6ki{s*cBUC9Fydtuo6B~6qFH*{0C$m{mLPxI!L;bhSK z>LawheVcw&w@!z$+Fvf@lxLzt`MuEI(aZfl`3{WRtWO`#X083@8q2(Avfb-#)lXEV z^RLa_M?~}6-G)6EM~F9h=Kj!*wo{C3bTEPA9IS~iR=ghqQVI412P2$22X;HLo&^2F zrdD2KN7agFYkQRzM``#aY#Djdw^<$MKu<=idShR_re}fhFs$jD?^0{M2sXwm_~W<) z;S_}zoucrYiI7^|r!F`A-?h45K-i}i>IE+@Ruo=rBDK%ha#E+>b~jeaNGZ52XFK`J2`a~`|fAm zs34)>&C2M5hF{NJXKsd=c*wY?5H+2dxq@=-!cgA70ljW|wPTG<_PaFi*FAa1KMdNY8{>$I%hj^@f8 z`0mV4&amJLyJ1?{kA8<0y^A#=k5I_@JuMU0g?~rCpA948hlkU?C&D=9KP-yb7rt&` zOlFtv9Nf~(h(5scC%RvN4eb>&?j;+DRC%jwp6Lp*+hRmM#f(12k4rJllBNd)u8?jU zHF^(Z>b+`}+ksGXZ%M?;eDrbL?cxV@M_)bjn0ZH^`rZ}L$IU#Z9!e5@f)ooG$ya_I z7}dO)&Rlxk*q44{l}_ZD<_mPP{`ZHYkVVN zZVS=WxAMgyx?1jF?pPt^lSi})2jH7!#l9f+)j!ad#>Z299vb~T#~ZO+&RTpj}$S`|~% z%Px(x@{A^PO?lj0Q{V1AyAw+_v4bwc#{YK% zhPzhZ;aw^oB;Q{2SzcUVwHNlm;<3XZM{|7x^2&=y8NZx7EPYjTdV$8q%@3#hm1@b< z9?mtc!`@SaUN{DrmzTnEZ1u3ZCX8s2h&au^jTX&zNoa>)tp=f!l74?+IKpq{MxH$8DuP@RuNR1wT*+V$uazsscP)PuF$| zwGtRTMhCD8KzE+ArQZArD*C6zlx(duQDt&iX!P;eQyb1xI3=+pE_8D{O;7LslvVXCLY_Lsvj~%Zash6f+KN#R`%ZjvJR~6e8@&fOk> zyThTg@5wm|PMzBVOt#%}jn=Ds3!3-9CU-}#5VI~J@lTQ(-Sd(VC3r7`H8)CTP1?g% z#3=4bOlwW{@I4o4NX%FC508`FP@xV>Jq#1|0{c|ODi$1o4gFaVn=nlbE6+?0Be>ivV_4!>oZk}`*p=N4$CPmQ0y{J|Ct{~f8ou^)0t0I}uBjbO z;!POwZ7;&o=3Oy7u!`Ss3}v@F{hr~FM7kR{w*ya`wQ5%VT|0)vJI6u3=j|BcLW5V> zs^5CQzhh`4j^bAR*88(NhI(*RvRh@7mv#&>7<-k{ zR$2A_@{Xa$%U|9qONrlulx%%k?D?cTs_}aBKE%`whT3v{0&*6AIysEsmi+c1#06YM ztKYmZ;EA0Shbx8rmcj*CiiH7B>ZIVpUsp?hOW^`6MJM2dc%W?_!P)NCS&GY-mPxgI zzeVd!d(V`YuQRDoh5&;yYFcECz~%s80Rkj347&j4aPzRIATw{EEvQ3P!bSHYlvL-< zWZPWDUWl-m$;4gPC6v-#mvByNxJ7D#UfIF~s6{0p4q&}c^LU+SXMxA&NT+Y%{_k0W zYZ^6VN8EY}&Cy3LfUud%j3zt;ta`N|;F)@5V-tDiHo53usL6ry#PR)Qz5iVrC{*(O zh51b)lV+#+4lTX3%Du0E5Qb|(wS82N#HwN^y&a=SU$_n^w;apcGa&jbDJI{&y%*`L z(i)aVNLcAkFFcS)Jor%BPXE&`Y~BiMmqHA7wc=Jv^{$XM$SLmOpi5ssyF5-nKW?I{ zS5S`=)^{G$H7>W-9{OndM)gxBw~f)c6UeE=#Qa!n2lRsIFg$EX%uBpfRkFRo=yOnO z^HbmU0=F3+m94SVpF2^ATv5Sr6uuxjS$OoUq0KSaHIUAb%G~F{LsSXC+!t_{^U)6| z3#UsaY8?LmBA{ZyM1!wYE~qg1V||XYGAzfY%_3hDS=~Pk?17om>XJSve3``4Ka1mL zGfzVn-)wQ`zM`D?ZURkQ&9=-5O!dRBk+C`{Nh6)f*$zCP*yqTvUd9|p;y53O?pZ%s zA$qya0KW6nQOR2$9oH-{d0$LX-TPvMTe^ksjM?jT$sXBrsN-Dtk5s`!yffzBu6s0e zyFp0Lhqd3XJGBjtPg;eyP|&#!c){CsS3)epwn%q}$V%((P>~*5cgNA{+)jl2pxN1$ zir=q0uM?(2C#l_^|GVC=TR_;NZK?l;_v=btz5RdIt4{O856_RBTX)v5f4&|gS+9%1 z7us_r_TZhxI?Tnw8lENEVeM#}kO$2dv!+k`ChD4x3L5^flFO`oh?zx5%K1r-o@Bcx zN`4_ITywphf#DPB^sn0A*63uU+c}K`vF)O^5)P z^9?|@C&qV6>5Z$*eG>?;aQV@<6lDFcZYZb2qT2|HBdGlhEDmRHs1$u$=}+;!=-+Te z?BkN;JAx&jnAi|+J;j=J>w=_Jo8NbSTCKdaYOMw9rC}}56?5y~QEF`%ShA^GEd*K^G<4m6ZXUIBxZ4VBz9~o)LbO@0pq8N#)C$2a3woD>LOOM+X};ROnuF0 zRztm^!80+Yq~zr!MY&z_^0vZQ zF;3FWTU}0Oc|6S3AFqMM71G@`jvNwu{nB5_YxKlm?dY0$kIzk|haYOOhl@+Jr43Nl%=luhN$!=}QRfOOo`p z3F{+CZ=9r3sU%4%31O8aNu^C#g(OSjBs?vbB776`>Y|HcL^tL_yMzrn#?=slun|Bc_p<4J4it&%M z`LIix`4Y1LuQi_zo_yxX$oxCu=iC$=DK)=IY^UfT-<&#-Eaw2P$~T*Bx8KkNa(H@g zzjj??%;^=gnr{lB9BZT>uga8}{ z>#B)Qx12^YI&-i;@(@T)?I2O_O?<{Lha|~+-03=OUebxGZaZN3auGeJQXza9GQ?Jn zIs)TY!K$R{#OdWCU*hDTv5>uEA#~Wq{d60ejODC%O@=%2b{ladZ}*u!+#Ec}$=f>F zUh}b79oeu!v{Q#U)Ag!0fzBt+`?>EVU2Ih~bW@0W2}I79tRrCcm@2Qn)y<2~C6D4BMbezHhG1)Hj$aRp`iZA3MXH zdi0Q73nuoItN_s$K;m23VyB8{PIont{R*+j_Sxtl&SNLOyAXYeRI3WZ(OuY$!r(yR zyV{8^)FKWpmU;+~EwP*f>^?^}zc&Qp&K95T>hPJe`G*wTHh2&~+Z72Wifw1bslssQ zrGExk+cxadXy-4Ed}7;~M1G>E+YfM_$deNb(LK4!N1qFbF)4Hw@Ig)KkYJV7%|Z!phsdTVWNPiQ>u5>ic6M{f%44uLJd zHBezqw5MouKoe5wtkHUxeGl&TC+(s0>6foB%c{#lKB%~+aTSibC7)w?tW$;>F^uY+ zW!ZvMklh5SY`Y06(~aM?Y`3A>6688Nz4pJ}-?XyCLJ-3TiK9 zljds(|GfKDwf^0bamen2-nx56sS{dUF0Ko5jf8Nf3o5CkX$}}dfbFu zSw8T&NMgQuJ~b4osZ1V~w^Intb0|YtJLJH8^KEU=8xK*iHS96Kz)IP z#IvDDcK9OaAGG5L0$kw$;R|ICB7(?Z-t<=gGj#f{P2?JzxLdJ4pL^+rtJml9*ENpk zXUo$W+8urClJSYL@qQ?XwdB}Wg=XQ9%rk~rAxo4LX)Z*v@+1Fu2UcOV4BPUj5@sOBmZ?1g4y@~c-@Xvi( zEeE}s)~~*c&5SY^Q9c83m91UXSN{fD*wf}k>Rxv;BpZE>s_TnWMN8<0OS#TyF_2&T zdXlg$JqsQ;WLQ6+@AUHdPA{LgL9Elua|qo&k_`4_8%sF6zI3LT9lfrR<)-AX!y55~*n9+8sgZHCz`C@nt+}Yk;LEk5E>kPLq83@9g7*)CV`aL3E0(ly<)}6st z_x7Pm-o`CH)D%b)cc3s!-%C7h_OJS9Q@BU5qRgqE%q+QvAP>zv@43^S$DqzwJTnbr_DN@cjXR-{rNWRxD%be z4VYd>!Y!$9!>88~_82OTn3%+V@zreu!~pfGUZ>?4l)f~`aMUUD0pu-fla3DSU3cL( zHO#oCA7c`r!?UtW*PV?5c+xl_?nAaofReR9Az2CJo9_m0qR|-4H$T+&Vh30QMar#}H<`X93s+u^b(A=?ec<-S}_z{KXFrHX&2K=T5y;L3($wY?z;q=<3 zGm(kQT<8c{V@1KqtmblPk8V!4b6EFBj;tob1ZVX$jxMMjW#6eC>)9t)IjUUQI)Sdc z4qr*f8&|F74Hj;PZKn?QZSlFAbk~y3Z?==%q`A$Roo39m(dJ=HD4ZV3Ln#CY!PuG) z8c+6{nr(DwafF`Yz+RgFe6yxn@CnwIVwAnO_vl-$*R)JzcZrbh~!4PAV$yhE!$@F#~_q6=lY~8oJAhR3M64S>; zt_L!x>n*SSIC0Ru@J~aW(9D}P0o%KKv*@7CuGLNqAJe4v+P}tbifxo~;9!0Ef1wW- zAJ`e)(9O@mucR+6G?wq0vxVA*mEDtf1>?U~#XYf;Q*~4{RaQ$n!fd^y+^;rYw+JK zg!A>$dW50yOOn}j#GU&gauWW-?!0Vb#G!oS%;x5P)b<}Me{Xi~?luJ3UCKAq1F=7% z{xRaPYl-`i@x;4Cx6TLNh@-CT0lFQUA8n6ky4{_90`;@*40=8@zK`3ZIp42N7x@6o zN8-|Sx%n)*sJWNpzQo1`-`@NDD0#w)7GO4AS3k~g9;1U(EL}>IJ%01{#M4;i*X-~} znaw#DduXcPn-{->=xm~I7OC~-4W#g!A0jW)6g9^rTNj8I$2*^{_I>crh4?898ZUf% zi-;*rnzrh0a5HVOssJ4# zgZpEm+c*_n2%7T5Kr94CcrY0K45^7_;OK%Dk6(z;Sc|7^57x#-S}O*pwgf~^YO z$vB-QSMB&V!AHEEFB|$C3CFx-Q zv(R0Y-I;e@)|ls8^IFx;gmW*Q9RHH`?2re3Nlp&N{y6!&Iw{3WidoBO;pC3HX1l2P zd^P;Cny}6=^nvFSCO46qzbc0{qs~Tnwm5WUbbLCS;w@k~eLPc&RK>fVC| z9*a0JbvLo;jU>dr&0*-5^wylt_01+VCEl7iP11FinBr7l_;$%#wy~S8Y|u{TQY=QP7gBhphgV=};%7Friss|7Z-#HT z+)9sY2VY!x3$2r_UFwDYS-rc zC{U$Z_Tg<-uS#tDxr5Oa9J_Vc>>%$Xb*9j02+Sbe1c%{WXqrPFcct47SI(juOb&w@A)}*YK zzp1$wkjdpLb$kAEUwj6<+PVQSk3+O>lAXP^^%mK=)>~zEL}6TZp6iy`fn0W;>xbEaz@PsT8TI({U&P{W_WUif z*_`p`zlLkN?9YD{n~xw9I0eF4+c3Z_8x?4P^AL6!B1icv%+lY_z6swBGWTcs1_f{x zoH@Nf4Hq++lQ3&v!B3?yuCGXkeM^Z@1A5q(arniSNb%9dAzE0Av_xWd6r-Z#|rDzv z#l2DfVHfuu7WeRtljB~&aV|f?D>w<>^~SZeFR)nS8ynqT%(rYvdu>@KDW4B znH~E}pZ_qi(wi6E|MPXUa$fxER8q^lflk9xIf3fLvj&g|oZSg#uj)#+cYJ)ADQQdr0M&gUo>HZ$qQh{j5^l0NhpLTcvrnWPU-x-Q!7 zUp)-{%{fm#T>Y#+{}otN$*Y5f4dyAzQ@rMSo;YNvOgJ~=SGk1b8gXrdHD1qJ^t$(J z%y3hLwQu^52Ojuew$>R67jV{_*SKblC%Mc z6ogpw)~DHlf(v2BgHUpdexSJaUM9T{fuhKmbs)E^GDW6CJ#7?~td*w7%xi#8>%}DZ zBDwyji?_szSKe6arF`6#cjaS-&@M}Yz6~H7ql~>JN7;Wi)8h2kc3Bv5T~L@xvC<8z zDuqhVVXxH4fn&o!FIMk{>Y6i#&9Ua>g`7@&z2bW>-D)@NmRO`}Bk?Vavz9%mOgkuX z6oyUv6o^a+^ z$`F3!M$WBtPiHIL6SSU@bAjZQNxl-O=J^Cw=^oi+jfs_>R(!pjw6i5&FSjji&DUFq z7k%nAw8~3XeZ&ZC*+-g!t^3CQQa7q+AByUgJ?qZ7UxP(k=6BTy+I-qg3-7V^!Rn*+ zKUn5k9p<;!Isb!%!2C+lmh#iZN`9<#@EiUl!LW}0e?l1ShBJFdnp0x5DYFDKhB*v# z8Rh{QB0NBZ2Z-KqmX{_>+EudL>%ct6p?`U1@_TzlsBXWNzpjEWi zX9-K(pOf3&X&V1No7XPJmj%r^C827E?E9!^VIT49vZ}35$?^xo?`!3*Gjdmw#lL7Y zX>cFfo4=Lt*L(AdHaf+^T#ltPdDNc%t5TYO*$mgIKabas?gUMOwQ6k(pW2$9vnW}^ z30fLcy?GJP81qpB#JzeRBN(h!qqmTp=P;CMzJW1wgJ&O|WLnlP8nFkn1&t(R2 z5H)l+J6P{7XEH~1v$KR*`xSoR3!5e0#L-^fm8)Q-)aH?h@?HPIiY489up1yRAH?03 z5!*p-rz8tGS?2DVJ80_O6VDt9bP=1*E*Dm;M_20f)%oQc`Mglu*!lu=2Q}Li8Q>a; zJ%OtYut^Lb@-lD3tXy?`#V7z$j5e} z_7cQ$=|C~I(P_ZwQmmBxkZ&xgjO4o5wVZ&vmW9}LTu8dkpZgAITdMqYmh8e<7O&8E z*%T?>h=X6U7|t$=5^!QXwfyxS9v=U9SJ>G2^i}!u$NG%pcFtfUN@i)^8>i0FI^Pxa zy$^Rmy*QKBr(tAuWw-YA<>Y+lyyRvQMU>g)6J5iu3jSbY(&WP<$HBTkB6(_g>Ro0g zU31f7{I6(^m0%qool(59uyyDI(_h=7|K^+@p7PTnI9_YovWC6@JHQS~anttPBq+ye zJT~%+!TNaDjAJoc7@+@wUTLLOSHLW4(CV-~Nb zF<@<^FaHcRt*x7}<@=U`rRSP!O-jIj@o)>cjrYoNy~~aROMx5ZG!*5>dgCr`td~Bn z+WHrOY{VJE-Yn_-pXj;0dC|7zX3?^8UL>s?7^2G=Snif=-19C&O%@XEqelV~3fFJTDU1#*q3i>>D zt9i*j9?j#<>EY;hlL}aB72pcQXG3vdr+Pg2YDV6e>}yLC&;0MNsbKnQ&p1@Qc4*Zq z&)Adz@>@O$(eBd(%X120ld3p+I+PXp0M}v1`d0JqLu$Nr^^jD5EvdFzrRT=AKV&2% zN&i%y-`)*;%o!+~-yfOq#>xZT|0C?El-EzcC|!n1w<~C+Y#RmnN;xiHrA%Y9Y8_yb zC9y44$}GEfs^ooDtiG8GD&^YhO^XE1Z|;9Y=i|x4GwhR@R4+r6S^>FkpmGwA7Fp19 zW3p)d30l9bO|Csn-*jBv4&4~fih5&vNo@DUc7@gXSn)=#CNHD0S=sGzOzWd+aVHgL z*;`ps>8o&>$F)F?uecVti{?kGd{|l_)Ljd0*Sb8p4V}0u)ocB^+mm@)QWtK@_Fu`_ z)&M#vvoe5@}O4Ny}!{0)=tMQ^Vo)$}er6_&4=&e5(y}TKzWi;zjt)m}|Vn5#! zd-?XQg}H4uK)e3_tNR(kp>uQ<3}-Oyk0TkjKf-AY$%Wlp)e@G4Hg6(l>_V3IP+0PF z-j1C_jObupn)yU~f8^8c{Sgh$V*~NF$!FSN&A94QbN5npmw!X9^UoGUU7i{B%4tlp7(P+m*^RKQi#{pL=LtcA-GonP}kXWw)~8NY|YLW8V?mN-h6wvbl=TGRK+TyhxB% zKG~!n6V=4~ZW7NNukhR%EM}A6Qhd(l%GRTN!C>l@U(uyAmKN9&kMJn#=o`YDczzaV zVzei~J7VBBg+pz&%hdILjw+84ZqY!e=K@cJcka2sSr;@Jggq*F9Pzoz`6`#QH-9?_ zRYIfV*W@zMRg3oFgkxQLzCVa8d|r822^-_h3zZUZYr-A*b7b-ZRI^fUUN=79I4WcE0x99Wj{FWEEx^x76i+GK_W9sCI zZ*1z}9-~+=t7TU>j*J+4gXnUVGCpIrUuN~W?);X{@F9VjFQ~5@4ExBrp|g$c`ch_X zkDWC0lUU6vWsT1qhu=h?==B~QL|2kgqZ~d@mp}Sp#3ey=86d|*lps3Ync^s)Y2y1? z>F8dlGkX+U*`_j|Jk0awzDhPdele;*D7rnH?Mz(6VtfuzNyBeR#*O$|^dgmh+8bAk z4t3hlWX8{Q)4;#juFu+3IefZM9xS@!yq_=PH&UW2J0bp@ukxCL5ieFB&|C{U?53&T zT^Lu-vF{M)fDY2Ort?oda_tM9hpuU=A0V-&*2(V6Or0?C)cnfmjr2>+t0Y7h;WBg+ zrzh~YErcJGz`wWwaOtG@3asaJUED@%Ttu4f)ZDO( z8@CppfW{f&Ah`^NL;N-#<(bdqIVFLOedD|(-IF}bU~NKj?F3qna!VjPQXl6~Q4Uki z6QVIedPh*?PR}{FXMRt>O+*0bCgZ4A0C{zC8QMwD18s7E1n{0RQ-4Hd{{juCMa`Vu%^~_YP z0jt)jc8UYfebX(e`tYJ3Npyys9S>4@=c_mW0f}_oCf8KHPDD36?}?t@QOEbm{Fa|F zE9>iGesAwaz;E>-`eO1m@cZougQ!t_d^TE2y3Ne+mg>>UBZB;OTu3^g(0tiG@eQIz z0R=9cN9PGR2i%V?9?khxr|dU*%7Ra-Xee2a(gHLneAt_pB%`B|X$8u989+>%PB;KN z>Z9S1taa6~k-25$ez}HvCPQt7vV0m*zxG3cpXbfXcFK7Ei2SJQ^P68+)2vIJb`xRg z(GL;fsCI-~ca_CpI01GWr^2J@0-6^pzFc8L*{aT-0#BI_BXheT(WQUGv2m*A>f>-7 zMS5;ZjAw(FPu7|%c={+8TxU-Z*r;XUPCGJO{EkIP<*9q_CrJSLdyqWOBB!i9L1MSJ z9QmICm}`EWUmn!F83w2DK8q%5rM*Kb9VMNTGM3RN9Ba9v0RI=naVYnH6vCJ)y5^pp z#C~JMJ$F)F)7LzTsP5Kg^@+N9T-$@qKQ#P|2#J5mSx$t@l9_t2Pj^wr9&uVhi`x$m| zKU^WWDHFAJb2@3uO55nJ%}r9mt@JcbvFKhaocdQDyju}9sM%FPH5?=P+*CVWqG?p> z7{c*|VGHyK(=BOYxk!(&ieMMZgC<{Vp0V_y^^B%c9M_dRZ58Vf{%{AI^o*vSfBL^m zhwuWz*kMTDUH@;;Ans0a`^|9Xi zt70GBgV2O~Q2Sdilejv-*4tvyRj92M&2dSbtbB{&I$6F&Q4_It+Ho~b?}KbI2()$M zvKE(g7yaPYj&j^R7s~Sn($Jebd0rhE&I zrk>)c@0lpkg)zoAt%A*AB-MTf(0g z$6(^NFv(FSlB%AVW?tV}7~_J)fafkw^3KJ8|9&~QIn5EsgZ_ABhFWFv_-mvQ>*un( zxwmOn`b)V-TECE0ZQU!&ZHnCEAiu{lMev9OdmD!`L9mw+Pn16!YGx@N4B7A`0dlRM zDL&%yo7ff_%~O4JB5X8jpvP|+_QBR~3H9dXVU1yS^Ilc3%|i}gimf1yy~wip65nL( zc`b(^PSSHc9lTbC2Bo>p|2y$7)cef86KYPc%dU^Du%>8kNE90kU=H?iM2+?aWJjlI z3rbs0^FLL*KRMf<;;c=0mNBEH00(XmO=u_e@{Yi6PV;l(wUX?_X`wR?so^B7`j0rk zSWSdK%C9xsweag38aE83n{;Y<(bMlI%L`UlPiBqe_7qZCyD7cfLE)xpKXHO+B~mb_ zN88b9brcoZ+y}q&(6qgjZ?HgkI*h(EeoyVR%p+gy zRNXNPTm^b}T_3tNHnVETFiDK*=RWo^a@a83* zV7XdY>{hoI?L(fUGpx~spu-`yxH=qao^(Qzz^#GQF1{(chGtumQ`wRjFO;%FHf+WM zH{TDUbG25pI6)MWR~#`Guph@V!y>_C=4fJj^M3#pH%LEAu8uBaX8Ywq1tWIV@O(0( zp1sU8^f0;Mjx;?uS0HOGi9!%Pf(*AW1-Ws@-dnMk`cMIV1XGgjsK$!df4!MPHM3Of zCFv6DW22T)rBlA}90{yzXZ>+&dMd1U`xoW#LWxkky`!cUK2>~h`BtMp!~Vq#=4p(4 zo5Sbt6v}%T?WfZom8eh~tE@qV!C1w;ndfwj+DlV$3G>bSU|Fo%Am1#U7<;tW=xDG8 z%1W}`KqprwBCy2sg1I+Cw)H6!J@T@qbKW?`F{&Ove2SP6SNR|dazp3OZ6dUXK2W^J zlEla9m>=)VfMS0!VL)Tjwf^dwy}cd5lR-aMr|P8l%JZb4zBK+*_R!B9nqwwe>MllC z!TVK?X~pP2U~h3C*#0CP&WUcoH@=b`c+}y9PXrZvY;REYn7|Jb?*L-ms20J_z~hNv zi}N_;WKX!6$h>nd4LFzV7&QeHljM451s$Qw5Hy~&y6PXh9(w|k#_O|P+kLMjsO0cS z>~E*!W)K}6Y7u(+fG5OF8(gE1uSQowe`))$9$#|}CEsZ%gXKH558&>^Rx|x!W?1;O z#!l85{_w`eie`iQbv#J7k6O#xhreQDV|nw;2aDqR7)I*Mzlr8WJwR#;W^##NT~ltp z2xPI4+lC_T994InX29Gn?oI|hs_l)GF(pv_ZP;R|V)}1;E7{C>(6XSvyT6mAYs0R; zD>qL)sZGlwy6L&vEr+JsY55$5473Yf4KKuHYajJvx2x<<85wJQUD8BDe8i4GY3!h> zUprM;wGEuX<_40K{oSKo_jt&Iy~tQ4n{A;-TIZa zGyT?Y^rO{~6Ad#q(L6+h#%|f{I~yxB6j1iv;vMFx?Va#GX#8E!@zMvS!_6(!UQVWL zcdBeAEY_LM(quMJEUiA#=sOf^^sUj_R4-*l@AW7|ej4>NdDku#`GV86osrBQ>k?hQ z*^KtczpK2g7kAunbBVc=#ZoruG!{ljz&qUPbfTuYZ9dV`q{5rm93&8G4= zP_cp5-L_uD$!{0g#NU0~$zn$f<76$J|L2qZ8%3I>GMMN|vRHRIM~mHjk)3nLg1Dj; zvHotqG46&iJvZMTG_|K`9tx=Oa75L^yi=QMoht+M!HuaC-vF6Bk^DU04$qh4OzOJ zn+NFR6)$XUybpAIpK@&J0`5mg6NQhQ)#g{JU%&ZF)$)Phal<@m)W6W5W`3ZuqpcrA zrC{C@QkJ=;^7hiIYIwZ4GE&m7K0|-iYNqs0ct^u)EN`v5?q)inzqw;XmeO@Zyja06VFz9I14n((T93P8g4T&~zs#-Q#wZ?mUI)XD5tJ zfV-#UQl-0spOM8SxVD{6aTb^0%}S@Aol5@hb~*(wPWKU|)6Y(HeqJHB?JDG#3i%U1 zJLOe4B`KTYEY7P+=&rf7XFHwZEKWDAbo$vTzr)+<6udayqm)iRJC*IR3fZ))kV^r& z+EJXv7`G~&es;omT|1pA8C5$;$=AD-?gRYnRI9zH`f7ffke$l*O{LM#PI-MF;O-^p zS4wyPuF~~BnvijRcEWh5LQddkCo0w}XQ%FN0Yj+iL0zg*^#aUd6+j9>V`dDGr&rbPmQiy(b3VDV? zUa+f>>vkLRj@^cQNFo2SD{SA{ZOAWo8}cWGlpeDSs`muA>yZ6+8*;QlPFjdfn=;qA|gwwjyRe~X4G)^XP|;^duj@8~#n{u;Cr_=qj@!UkhZ#7J6l>%Ym* zEZ7E$=c)slBOdGafs%FPT^j3@&B|Cclibmx$YhcSTwcL&)7GD4(Y)w*g|W_zy(i#} z$2E^rXoKVKY|d`+i}dKkUE)`e7oBAIYjrE67oEIo{7hi* zCx5k-b^faJ`_9j#Nx^yxuXoI}<0;x!rjGU@%hF8UB5$(ee@sv3J-n6FSpeU1#`O4G z)(4=b{Vgl}pnObQP|a#ztzHcy-80U5=W@Uwj`kz)DRZxB!;`O6r%>J$cu%6TNfEEp z{LHTR?nPZ{?|%59_wG|EcG|n&j3~y~^o?r4R{j6(-Lq~ped=z$sCN7#oBL%$?Kp%d zXs*Ycw~BannPM}|hQs#(ZvBb2MFO>ri%563(uGHnsS!{O8*Dz(q-+|tb>^&AD3ZL zp2^S>3Tg>UIXBGHmwKBInvR5~)Z063aT(W9kIGu%>u_j^C(WJ>;<7(B6>)T_ z)Sa*SRnvxVE5&X3VZY!$Ll@jffo(ywigXT>J#3M;V-nHLK<7RUiXInc2XeGMH(0bM zDzx)v&t2FPDGm!eYD!kq{Zz5UR>;Oj6g;qk%~Vp%l#-k19ty{P{1xrZR8X8irh25n zY2S~PC7uWn&_bp3AW(gb%nT$!dc?q*%fxwc;BY5UPaU|xk_=nfV}b2uvPlHs(ZO1u6VS(QSl}B4Z!*iSMOiQ{Q4tZL#lUu zyF3(I{g~4{UE5&KLUSCvx@Ls)U3{@Gg;Z>5@}Af?Ha_(|oH~foQLu0F9AwbMn)!)v z%_Fo~)@g0=EC+TD!ma0LYOyHMW=aMRY(2$RQ}|Z;LOx|kG)yR>eQ@=7F+{JN7^Ia) zUhRpmf!NB$pTYtHyznN_NEj3?^$tg6?Gg|ADxG&YBn-x4(~BPKX8$U5t-G6sK$t4t z!Zu635fAqQLJGvq?zYUKQzZHdjY2B+Ovct-4u470erdp=DFc$edqZPqhr+i(a`P$5 zE#CCtMePChpV_#I-=FWm0nXi&&z=&(-4>CH+~5@IiVxL06MgkjHnxECwd zu&JWzsVr&k&*qNRdCLHR+|vlWPoW^^nW&-grMQcL1y~Pf2!5;t=|)I*ugd6pdbP4- zTcy8NDe@b?`m(#+E3Wn2&GjYTJ*l{ElPlu1o&nc-cu`^vSvYn31A0+#Z2X4GU=8K^ zmBHv@6`b23R=7Bho)A==A&WC)ajdKhDpW67mhpCaL^ClXe!E-vnWoyf^mi2D*Fuh7g*8QZwseg zvy;5_P={aDxzix~a)E;8V>0aGrf`=E!5*W1$}}a}Ocjo?^4t4t>J3$AUEZj?B(# z!OicPH}rzbzHiVwW*AiA!#`2paVX7fY+T@7IttqMN0KuVKlgKW;Fc@o^Lr7Yxdk3X zZ-#-$i~-(|0LuEp+x)b=);#+24cfuy6Tja7uJP&LbI~xr1hR6owb}JGed$JI z1@P@KPOiF!TmAB9t2uqch(n__IJXZo)v^zE`no8VI3Da6g*EMcyMqtT44=TXOl{*$ z!2g{*eeKi=YeWl5P_nZh+^TO^%`Zc6ZE)_j6q?T?7LT2Fv_kCc=0IG~SW9=JSZ?F8@%wT+}UbA%h#>-bSbHLKy*}FD-_+;x`_Z^JX6x)g5#!FbBP}l$Edmd_Uz=hlZcJ= z*lIJzZ=_4Zf+%9J(J=3*3qLH*TfWuVYO0~RZeccQ?Cx;=q(3~-H5##BPnzXqAN`i( zjnln(6=poc(dUhgW8zHJ7QOI;e=~kDy^`IDf{``_3z&)w~ye z%qUT3H-;XmVR+e*gxX?B@a9#&x@Qn`hv~zZJhix!b?q?7y!Q2(e)Z`(2XZ#;@Rzig z!5zVe$ZYO$$S0klvZ)1wbC1Vco?Fi^x5=N*2woWf6)CFG20&aqHgfp>x=YaJ>~mbw&i|}rx_dAA50{c^iv?#jojLP?vrE|Hvg6wOu4=E~dU^{a z*4d?r;Usx{r9v?dcga-GqZ=F9U{pxt6Epd6Zqjgv@a2NBaRsXBj1|`eK@F4ucWfm4 z%PBB{u3499ww66DH$lfaguP>IcFl~>LWVkZ%`6+NNMes?K)BIx;OIb25QpNVCZj3? zP@)7a8}J}R-)g>%tv@I44&Nkl`%#k?NmPsl9fOF18c=vFbUUH4u@jQ_N>SP1{9G!X z5zaTXSdMInid#r(lo=vI;{*p^+z_q&o$NUv-K722!V^TsV$5+@_G@~w=Vxv;+l zbafCWRkg!QqZE_=hLXkk-?|{LU%)#@>B2V4tmGpE)g8Na{ZWjo&Zb9v&M$Fhf*Mi# zt^YU{9XppbIN}pbZc1(u^P8#JHB+nej7|`?~h0IfTBOF4b5$)|+5+tf-Z?=T=oq z+ew&=-9tl}ocaun-CrOby#Vta5j)nsanc`}t+Tm!hGRy0Dx%p2tNPhBe2pEN4Q7_b zQPjQj3{CiHi75Mz*NaMed&BtKjaGi{yNS0t8xWk4j63F8)YuPMI1CBLLP$SoVq`XM zEttC49)5Gu02~qfOLU_rz2_7%f<@G`p`~m~b7` z?1j;+?HL%&Ji5q9%Aio3MVVK1cHghK+&N|`FmGUHn@>4iZ!Nk*SX^3-sAbJ{RVmDb zPd_)aMEusPCg*K-^Iq{@=WkSJHF_&!j>$uci&+;lVBA6_3l zL7ZZd*r~cwFf_Y^H!BR1ooDftwFw)lr&I!tlu%|7s(m5R$-=gSdvYy*wA(D z>sry(-{+irUouI?-S5}m_xH~)@Xp-Nx##wC&%N)xd+#%5ePM9D04Uc=g<~wj^EEY( zm;ID;+DF|&4X!^TkZl4M9l!{4Gf2Cb2F+^&rl}>5iJeqh-Hg&ixf8D#dNc46qnm-% zoR&_!rXmx1y2O0NYX?UbB;FD0 z7$9$ioZTEXZVs@ddj~=?wn~PO?P3zxN-4hKy6GvSU(tWHxOkh>T{P1(xT)!G6dCcJNMf?5sT9kFomINzJd)i@$VKwG6 zrs0hiFD6EoftfW1*IK%*V76q+hp9KAv-2Z4UbPWs30_U_j=XU)>pV~Y4`Fs9_foUm zEe1!d(Wu`v^&UQGzu0!DDE+CnqA?rGqwj_7QOl!y^k#_C1$|d)Y;Uz6L1$sGY4t1q z*xoXj2l>}WC)?d#9EyMz-LtlGr*ZuWrQv(|myyqJS;uHPsoxVi>^(Pye!+V39TJ33 zbA8mOM6%^ow~&g_tEuRd6qJW}zAqVqXF;P!;Iv1R_aNG<1c%^d*iFTeW<1ZJyl`df zA4@b9V{_hn!S>NZGSsxmf50QFi*q5X!|bEplg7pBRx=BKF`hi(UXcoKrw!R`mF=U0 z3})96@M7Js$qWwaJ+p}SyH_wS(eG=xHtFVt_n<#);3gMs3t@}9a7S!fOT;n2F+ghH zdEiS{gnGf&&53t@94bT~u{hob>FpMBgsClBJ)CIu47-P8?)K|a}+>c}H6UGtJx1JoK{Q`ye$%!Mzz76lW3_VFZ z2HLkp_vXa*izcogC*Bd?rE~~B)sg`2r!mTz z+@%KJB(ynpwc>UI;>?6+tn#c$_VZ*WJTYRL+=~ppSrn3+Ne|9Gkcd~oTm9o^COp$s z@C_XNB23-%;Oqm5p-SSWeiG~h373+1te*t?Kmr2<7w-lCEHD$E!&LCE`UPhnNTex= zhzcqrDW{%E_JPD;CE;R;Klv4deISvfB(nNRun#0ADT&g466^zs7?MCMttSaR3(SNk zrXTFN+uxI!@N}!VZ{oQ9tC@WuF-l1s?2}>o_lYc7g1BsDJ zBCDSS`#>UDNmR0ge@(IvBr=plSHC#e2NI)|#J&9_*as3>O5(+S66^zsF-qe5eiG~h ziLpu|VqV|UvJWICD~U<{B-jTMXDf+C{Uq225>u7LW&I@B2NLHfiAVZLun#0sl*C6Y z;a{ih1Bu~EBBH8qS=a{>4kh7a3I90Q2NF&tF{7UZ`#=J3g4k9x_Dh9*Adx^4(D1D! zp=&rZ;WX7_q=wLWrpvcsD8mtb*@yjG0d|?H1z%0sV5(`Ci zYft{}LjHc>{Lui59hx`OZ^+*+2md%Zc`=pDb*!rnqlFYWwQ`7|gNw9K{^~_4)^keP zK2Hb3P43;uiLkF2< z>}k1~e%;5}NMp5xtykXJ?{=vtv>*;k(9z-}=)@V)lD2vjkQGIG;57=Xixeg0U{O*Q zDM~EEYStRM4>yV-s5b}d(@48(Xt+fZG!(P0*sxJ_JhBv99m{V{p){l;UW7SAZQ!Ee zTFPUF%)#hQuy+i|5Il~af{1wj>1}{uNJe6m=JLR0GhnsSA~^7~7(cGzP*34_6o<6r+i~2EP(=Fs(NSVpwFpNR__^{rGoMR`yp&VQv`@o^bLuDJ)9T{q=(R@> zaME3PSLB%0HXnZJzDv+vcGtP=z6|bIqH=!( z_h9Az7VaeF4xcYt9l;8u5wIyRmOzLCQwfABa2|m$1zHJ&E3ksV00pif5TU>W1R@o9 zgFuu5J2C*G75JKbuw$9a9&;|hKm~>n&})Ts-l12^B=XVgWi|o5Vj2nPHM5q0UNyT3 z=ymf50ljjL6VPktCjt(YgR}(zP6dhyBrDKLAVq=o1X2~al|Y&T#|We=@Ctz;3cN>P zr~*F|a4C?o5MY=BQwg{gs3VY}z*Yjo6}W=H2nB8@kg32c1V$=whQKHVlFkDdt-x#o zSqiKoFh+r!2#i(Wc>?1U_?WX!sqjE-Y%}qiA3X`M9SE_@!&Ix@bupUD2yr_?-HtFiu*5OIje|=f+>S`MBg*ZFb~|F+j)87RtlJUicEHQ-b|knRgWQfp zw_`A!l;B8qJDhHZ!|h0MJ5t?_G`Az&?HJ;Ab-Ny<^b+^lo&tsHc@BM;sLPewU5rCgXMHLEkl4ez0cHH-}$-e z1Wlb!@8cj!OA7QX&;e(A z(druB)))_3V|`m;d=6UUEC;RezHBTFJZN>*;PsAt#4{c)JnN;2ja&n`1JZI0U|2B1 z2gjNY2Fuld$R6@%;BOuK>p#+B!{Wj(8If_grsFEYMV+|`*D_pJ;JO*t{kWdQ^#ZOp zaeavEU%0T|Nrd4dJ;&M@F%}oTk}k4wor|jxR|l^3xOU>Y2G?!4u$EptgzGr2zv22A z*LS!~=%;Rji6`Zqd`GSWz5~Zz?_^YCCqE3(efaSTd{W4VZ+~?kzTwe*_&JE~!;ejL zAAY)_1A10rSWPp#Nf#qSzUxu9d z#rVEZ_u*SW9q`?r4)_KWAdL#iPfGw&3Gm|~04_e?|LK5l<8{E#763R0{0Knz;d^-< zU>@C5IKGG1efV}=2YhF)1HNn2OUk!}I^g?Ah;gF)^hFQDH|{#%dvqP}?YIu8r|CL8 z0P4m%%w36=O}kNkccp0r4vqI-K)noS^O^W=SB5bP8mRtF_XI{)GX#wMc#W6bwPTFb zul-S+=`($Hcgc0vgG~O}1f+E7ImCWPyG&o?O zR{M`N9>0E}c-zSfneH*LIG8%K=k^mBAzMK8=r3>gz5z=r&z*sZ?&W9V+f*q)Vf*@X zT3E7;|FqrOwTjAqHZ=>SWfF@JA8bUVFQyI~!PbJrxK=|38;e7H`vKc9n$l6jqw(XK zf`HN)(!GRL?wi26GzbT+ZA0a6TY)Hfg)o+ku0dot@`r;WO0kMwFAGjH6Yfz6E1uk%zGJr@v}?*?8Xn;j@BL+u#&ZGs@$hx z5KoKiYo(iXYY)U+=loXPn)cgui-NLl8EKyf&F}t~y5-lP#GqnqV^HD=!iFL<0S2yTVPDUA3=V!v~^< zo3q}93h-QSH-3%KfwodaVKeYCsnTwYqbb@$BXu)EFhcN|^#)#HOy{eVBT%R#j@Q0~ z8STOL1pXL=)gGLHhalw$g2g)|6m4?EuoS)zoe*iZM*_QY;Gwf|hLSzfVz(+bPEV5g zdjR&&g!~y?b0~vyiue)gOl=d1i;S~d?EAN2--C8AGA`B<+wFGkN8`e(&g4wWC)kXeAferk5-P1rD)C0&{`628j`ZWkGi!h+5AV zjg>G|M0`RiR64?Gt?)TZq6g?PMcAVwVbsQxMUJxL3y5L%{rJkw{@Ta^3$ddmEEXtM zqJbGB@x7gB`p{2wR7}*s$N{c=M4pP)#@{*9=DRLOK53*OOW-O*Gf)yIU&Hrc^aiiXb|v$s?>*lDhOG}DfzSQP{Y z#_ou@h7~N9JXkgsO&^DELVuiI#%6i8_AFK}vKA^&9 z9kaL(864SJVFt(iQC7IB9kwdB!BI<`Rm7=PoKe;8-3CKJ{F3>`f~pEbdVy(Sh0#y| z;7DFl1z;#>HZ`9s*8+cn{01SvQip7&5xnwT!z#0$oEzLm)-5m<(68#(%h&4L za%41F&zOeK>aZD1xsdC!t;`>cE7wv*i_VdvM-s=Hm2JArxGlGn+8`zh_PDWS< zjpPIR+&&h3;__n{(QXuG-mCxD7iDHqy0*JW| zass|Oxb05ND@07J{?x;8(28TuZiGO44Kf#k8zEW~yAS!mw>~0WAJWP()Fn1CQG2Y)9h9u$nYN9(QQ>4#0pr z!W!hg1wJ@H(CWPvj#HsS6erqr${rNkb{m*sL9V$F4wlp%$@iu5UEpF7+$us-kngLM z>JHjY55ziKk&?kNjqPw}8v$1tDIM0BoA^N~iia-?Ea~);pS{yZKdUK}eQgWTeFr3r zWf-f&IEHaLjAt0H!vuy2fcDM{5eqG8p=1q4J1y0#Ag8OE^Fi~s>yZaM!@;{YqZu3J zO!h3R339R3I5~Ycn#y+uL{6P(=H#<10`F`P#WPhwdh|Ts?B0CCYA$g4YARH^Ew-wW zi)S&KV9g^8>bu3ST!U5DRQe=*S&M6dR#w&khlj*gJ-Le zj$*Fh5IcsXwUI#y^P{ckI5Npd>n=xJ-2vd8)ClPp64kw8t9Cge>ds$ByucE1RlAb@ zqdB2w~q>Ws5Y^EoZdZkrd3b(I#t{B^38zRdhGzh{^iBBL;@&?4GD7&N$~fv zC?DCS-FZd>eV^Ota2l%NXgFWqOPE9~=FfERWMHkgSsZ7Lo+(w8&OWd?yU;hUQ4Gg4 zOSNl0Ob{%Jv=rPyq^})s+oazF_RARdtI|vd)SaCh9DT)=*!f|7p$rV63_4o)Z*Tzb`31D88n8J~hEB*YNj3&T0m|=KyFJw?%sghCjxTc z3dqIFzW_PQ3dk)C$n^!}9ty~PB_Q|ffZU{}KslQpklPTDds#s4odLPe2jq$+f%4!A z$gK*=>DKtS$O0lDu4{NI>qV0l6`)f$}gaAa{O1ZhJuPWdXT&2jspMko#poZs>)9@|zftTNseL zDj@ejK<*O(xnBk3ruYKoY<6$1d~Z{U@vwvL|At$QtDnXelo3Y9&u*j9z69UkeFE6h zd6+iI#B@GMKP)2Y>Z>pyjlg%bwZWbH;8fge$Qk2(2Rn@H?q!D6gy};T%=f+rHkOSN zuwxI2MO?@uGM=-_1e71{XDD`)ePoEqgQI6+B}~dPV|U5u5WFL}9pmB<&< zjP=TyAw!FaHPLje?QJB>i6Z1}6p}tgh_TutyqJ_l@I6t43{3U#uwDCWr;Mad&LmHtR>Y2RXH3Bhxg6WivhD#jVuLW+x-Z97@D+Ms^*00-8qf> z-{zEJks6^_eKcMaaNS2*RDN+6>Vy{jQnRG<3RIs=I{vq$l)hR<(5rqhf0gM4>FGt( zEkT06h$S^uekmsWpQVV$O)^ERN->EN#IwNO2@d){OAt57GQr?oE&R-}J0YbCR4x3u zYKap$vZe z1ynuU`pX1E^aKN|p2PNkmL#4I$RtDmqwYCE{?Ag>ZE0j!U*(f+PVH>p-=tcZG_k3l znf)oCIePvFz4aYZAF#&p-)<-*k3HAGW?-<0wA;)f#^H7u|L|~?4h=JjnLHY=J^~{Y zyMzvOIt1M{uEwe=6TJS7ftvm7OJcAupZvIw;BZQrb28>M0A|JZ<-}Hpn1ibIA&tgk z+93Zv$KGQ&?RXYvqO~}*_Ob6AK_)@jbcAKb%M|sV$bPab$zd+thKa?G=!i6E>Gqw5 zqR@=dQptmMt-Ti5x@(Muo_1%TBasyh+J(Ik_-06gwHk+R;)^0U!_hL_WKGCAriRHN z8ELgXS_ZND7%g%r<(ydA5LhvqQ=ReP8fIKg(;GZ^Q&R$CupDe7O+4?B<&oou-F?4t z_&~|Q_m*YBsm|nNsRw&cqXPN4IxWTxL7gS=BVn5*PFgkB0tnz$b9`^uMJ0C^8e;jK zNhc|sI{SACUTj=X(A0Sy{m2iZ9UyV~ejt0?m2 zTT$fVqWh(slis9c;?xidcXXHz-v*RKv9X4^)wiPuU(FF;1PxLGFh~9;Chw+ptG9YH?&urG)67`glhYG@8Q*}hwg3sLqQo2f zWFs4l80ERp&%LtSjq|%H!+cb&{+{lf3d}fh`(y090eXzidn5hCbRMK1d*@B~>G8Eo zUsEq%bXf{RU6-Q3qPx`;Y6SMWVM+aZiReHSin8rKJQv)Cm+Dv|&I}4h9&ViX#=%04 z;}&F)?D5A*P^~PaK7vzAXwg67qLgqF)em4)=OO?NPrP4&Zgyx%Al?Js4n=VLo?!YK z(~pV5lWfBu@Qd$iN5#Bn4jO|@S6b{ay z%8zwDi9BRvlAXhW(pku2gu(O|9z;TnL3WcmE=QLSay>rPMS4< zLw0M!|9iXDW9i69=^c_S(__?_(q{aBW|`D!1OI=pOl@SDpxZ|A5L$@eNvX44y|lrj z^HwMlmRZvVkIvi3ZCFh|op+Mk-iCz+w$8iAy_zN2b3A>zkfmhk&Rco{w*3xWyz^#1 z#skE_WYvUDg@fqGWpKTQ^t=zkhkMBq8GB3CJID&{7iqK6m18BP9?53)s1! zHhja*^%pQ~IFZYR34-mU${7aAv0q1{vOHFVjS`}dLIKC(*2nX zqZfNTEF-M40x`qm8p;?Npxj)aMsC@jvaTUzay?FQ8dj4sdGW3vtNQh_MTx;9^|GBW z!ds-9>ez#PV_z5A=|AQ>y;r^szmi^ec~m*V4X#(Hm9Qegj`ATp>RSR)V%*H0JkXdh z5bdm@eFDY6(Q_li{gc5~Y1dy7S4dFUsT4f^WToMg`7BocVyeIJ&k4bLTKHOl2pJ|) zQjj$D(K@7syr8@s18vQcD_XFzo>wh}W}Ibky@`~zpl?mpSb0C{S=S8+DaTK#!?{Qg6Gy!t_-k-1_d7ar&A*H6K}9JJ zceqjSbT_kJCIhxKW=xUwhv>!TAnY5;$;mA%%b#A5>&%`p!)CEuWX0Tj-pgKQq6uocQ^L*`?La z=>7E6d0~tXU?qb!kPS2N~~GgrP(=@cFwHaoH@>%qJo0# zauzNssF+&_M&S%3Uc|1#+#GfyTv1_Jd1*E!4rh6OVFjXuqcnedMR`$a8AV-GSeaW2 z|I%EFF{iYsjM){XrMZRWH8Ys0KzaULNce%W^6a_A6p7+hlu;HdO3R8$od}sdor)4> z)175tR@clbsvzI$nz{LuS}t-x^(Z-o703iR6@Nx<4*BKg76aN@Th8x=bvH!r)Q zfNFF`Zb5c+?hJ4Waw%jknu54xIVdAxRNCD9!u+!G{2b>D`YFueaLQR)G!N`b=ZvBX zYH9>6Eh=`-s6b-5**VlexdkW#k^DSoZedOVa!lDp3faYQ;LqfgF@2e`X=^N{i+?^C}9UXI16SnWfpRHZx0$ zDvF)6vMcioXF6x)&zx0LT$*20nqOXxAXJ^bs!}nyCfVIx#V7%iAvw^!XOma6f&MWiX9`FZ)sbzydSMJZ~fu!uqz7S+r{6Xw>G z7uCQn6co*L7NX%n=H`|{S;K7T%|*p1U_NXOIe@`r2P{Ul6Z%tBI)ht{+6))mpxmBe zw&GIsnlcInZJA4Ur?|8zCwE2#+8@3ZrLZqhFruh^u;{0ZK}}I1;wLQgC_M|n$;*XV zfMtYpCX}5$&}dM0@UvkaN^^^0NSKeV0Oc;tEvd*ylBKyYMfu$MN^?;Wh2T^cp@mDK z&>VoDTdYz7ntxH!8!A2nZA zj4H|F$}KAaEGfvX%E^^g3jpPTiXvP|!MyC!LQ+UL(K<70kb5#X1fg{~HF-r9g)?ek zJvfWta$4PDWr(~yyA;-jRFG{j3R%P253`8=ChZc^qna%Is1#kikVm6OvahIY+bhP_wn}<7#JL;O7>gRY?mU}Pov`ncx>#W*w_2b5mo;YT#XVip=sr?h{ zXlZX+>d{j#^R&}bh}o1={1s=IHu(A;bz0> z!&z^i{rIsFHGlXy@BBZw-gxv6=M7x&+X~0h(H!XeX(w6TZ-@fu#-m{&T;34!C zsJ13NgrO?@5p}s2Zrt99?=TO0Em(*WoJw&X-0Q)65^t=}f_nnogCX|}t}ukbNqOQO z$b~?DaHtT^L3MUP4!an|0Hm=S;kMvrlHP>VBY7-XSl;!E%zMjRf5 z!TMM{trg;Y_&*6e2EW_j7mqMbr1b%?7h!HiTwlQ-dyxem@QSVQdk_~M_X#{95tqWh z27YPyHR6guxUYbxBFqF*YW%$g{wcWMg8X=-QH$$*@aBQ{Ec|B=z~1(390O4+T17i< zGkhXnv|&SBhiJw>`Ww>Qy9`3jxauM9FG@S?ozoFd15VALc-pYZ-iwc+<$?OdQuwrs zRS-&>j>s3`9|`4v-GYB!cuogLPxwX%y8HzTS)>5sKCu{rxg4ki?nd6YKPm^|=W@2& zk#IQ#>o}h>U>SU{nuW7M#cZIjhKmFWL;`cbT8Yef5u^ioZQ;g%cNu$AncHz@h6f&t z;c65f)E~Sl^YvgiL(GGJ4Jh?EY)rsW(ITz4H9OACX;QF4Iv*m)LJWXC_*dTEv(ee zK;qLO>=hTV5H=DBS0Ev({5E8Pno*DO1qh>f{WBnQt&7pK{(uSV^+DAi3f1PqbRetk z0W`;hwjm{J!pROEF&*NGGni2;O2MGAlFI3c;;FV6I|I^`X&-*;PyneL<*3<~Uu}#h z5O7E?f>5Q93d@?Lgh@Thz^&)XY8O^?`^-F*iDoFR9%u&AqBJQ$E>fe$k*T{8#P#2T z=;c}XYw_y>G6h0yh`$--y97aoZauC zkrS#gsk!BFQGaVgB-CiSeYq6kuAbce=OW>;v2gYX`|E5^QND#x5(zEqE}dSWLU5KM zLus(J3DRmT=Ug_hy6EPs0p3IUdk3-rGMGbnOg#G(i8|-rn?1WxzwFpj1N|JiV>T4CFFfJwOg`{@G0$)T=;$#Fp z(Ch0Vg70CC^a;T4< z393Od24gV}A`@QZj0E*=8>pyl;w}g$7NLJHfiTs9?DaGn$i)8&f#F3wC=uho8y6#n z(G>pxffdOCvCLvr73GW6hVtn{vHY#?4Dc4Az+|Bt#B2n~>K~;4IN96Ubs(7ge-ccN z>(@d&t-m-mN^iZVISalS-M0k(#qFK{7ZG4A1)vV9cMV;f%mdkG8cE4kH$w$z=%dNP zmk2nd7#=><##}T_3p`~nSPVoqWEOm}oLLOP1m?ep>iT(Jf6=}+N{@BTe;3tbeZIda zS^Y(5U)@mXvA*};MfF(E!n_T|B6C}>R6h^Kk?dd@YJ&7f7cGKlLXUBkM!RQnv=rVh zeF)HPeHn5`wv=L`@udlMNg8(u!ii;K;0xX3nlIh7LC8HN&mrSO&=RA~$COsbUFucRd0jQF^sI+!*60A|ZSu^3;j(^h5 z1~Qq&VdzE`sB(SwNHyDAkT|n&kYCi?ZMs(gb+|=NdG2xj6#f2Jb^?BObJPl>- zK2PmZZBkBcOTDL=YNxiv=~>~a?`S80Bz!BKKF@_6O^~OS!vm~wnWHG(#-_!M9$%)G z*DGvoeS6a~j|zx9cw3rRI#msLkd2m}NDvJ)z*$@8UFOMjYD01wYrSpAx3{^roif_& zS>|ch%h4T$HklepwQ;#;MY~g~Rr^Y3W38<4I*+GCaglgaOH+Fj>R^?pAyacu;CgRM z3-pB2Kn`o0Q3VYvsfJoIwbYy*&yE()idG1E8W0WCa%ocw8UwK;`w1=WXk#sr)l3DV zh_pfd6C$;diHz;z8Mp*(9>{n1g-k6TrN%=bf6eqPMpZ5IXf=q?=kYHMnvv3S&Zup# zmCvV~wQX(Q`X(xY)S~uAl-{o-%Nv{O8=BMXta zZVj*R@cE$2JuU5QXrj(m*FlNlfmcf#>4mqYEmL#m`g|x=Ju01540*Z$an*NMyEZxx zd2V)kSkBW@PlZP58k`Ls+<)t(>Cn~Gi&h)0T@6sol%M z&UPpZ^d9{dZIP*+mFM#=g+4WSR=pfb9;Nb*0s5C-aR;dTo=vhX65lT|;K@}h|Pdf}HN(8&*Ts^8&rq<^1 zEu;SEtgLN@WuVT0jC8bg_Z(&BGPN<;P<+%Y8m+#a3fPL4@i>S&RjbSTO=)KBdf zK_eO{C{;pTEwVmz_2|x_=)8EIy4>r7rs%e}wz=86oGm!n!45pM)$F*iLU*Xp{kkjJG*=8QD7iH(7!q=lsoP{vlrQEZe%cFR_99n_)@6ns--i9Vm+fa&SQEhV@T85Hqt@WXFup(`;DH>}rMj_26pR;{sE5`8^ zt#}&W#1@Qt`jA2op7r-OUES5^r!&LLp}gL@CDf0QqHf`Nctkhgfe5A$>P}Uh;4E6S zh*ZFDZlPQ7Van@2^%T`hh863jf!T&_1gcCZEBl|P zB26uucK;OWo14(BNAOTkUt8Y@BVjM|E@}^uSR(iC7V6@9xAxP^vx;kJRBe;fR46%( zNlR;2)UZADG}N@Re|J#Dh6Sa$0(G_?`_D5(*0DvkP0gfLXP5QwiORHe=YglJ)Rq{N z=v5fiJGYgVUM5F;Ob{^jY=fQQe$&^=O#WXCeccTS&EZ;0p{9@t^zR+gx@c)U+gC!} z1BRXrnkF~P5H{yUee?QNgp0s7Ef~iwrqIoXqG~a zUh6-ibZbDX*W1h`LTfIqrD-;dr?;L*kp}dX`dXR+v6+P=3aDoZvkK>Oo|j@CLLxBN zP(mj~h#_y4KINb`gIbNRXyLmtt{I-8Qzg~5U}`RFrwx;F8h0C{COI3uZS5G)hxLS$ zcP`ZQy@v{ED3JtO6Pilh4;?~QU~4TD6oseihRN_WEpFj^Dz;=iOUcyk(0bDF$thWY zM06v9%F4xU8jQ_ijK1wotPbF%18DeV=s~z|M7fr(Y`d^IlQfqN6;H-dNia}8uB;TQ zu^z>)<8f^Yk7+4#{_b3f$%S8Kt2qEyNYe_ZoLHlvfx6~AOgp{H+qA*+qzY4xSl?uC z*L(U#t*@Pj4$vqkr(l@hlNs$Ej`-%3qG7wllQJHNX}*@}H)N2v8i|#K=fsjd={( zwT&cZc{TAyJ9rgk!g*Tb{V%CAEAnT+Zxrw>EdEQK-t4U-Vf^8RQ^dR{A37%(kDbLC zZH%zv2?m@vCyu{Ud(yB^H!T>=YNp zkh>giybR&*|I*p}198EN7XreY^G3f|j;Km(5x-czw^&3(kR`|>1_ym_v5CB(F+ny_ z7Bm!`8s=QU?&WY>#2QH2#KkPL6MP#U%ai0i@C_HogARjp3ceQc1;_a#hq78J<`^r* zoMNR=qd_fVD)Wn(?_s(U)FL)Be;0GE1&tEVS$}7Z5+8%w#4pT`45rwUKrLcaFvUL! z?r>2ad^pH1HUuvYwuw0*l*-}|%K0mxHZeYoI4$AlhTFtzQRF@^`rL4fm>WYfbxij$ z-7}E*7bZ}7rwyV!uTCU-7&Kg5?Kq6Ee{nqNu!s=nm!R)EUvb(*T^h*@PN$f=(%aK5 zV(CZ=E*lqj7=b@eW&O_UT+Ds{y~H-TD2e)*SZ}N)XnrTQ_D&U>s`BWElS7qMy7vYdV=ZKOb1*gq6l+W`wkbB1ls=e#k{V3Bvf!f5$4OH9zWIAGF z+(w(o*+{MYJ;zqOk;2x1hKpqzCs*6VB^#-gZ-hHs+y)ArgrrTJK`5KBZ6X>Eit=uv z)EhUEM(y5o81z1-&wxUoArmf!ZT@+aMV!5vB+q5KXfx&0xA_$(@(lOe;;PMLJ)Yit z^Ja@U&iv0eQ?9;Yci1IVZ;nfdlM8paxbzZA>v~X|c*2PD z2ewd;2#Y88>&!X4m2&&iR?@e>!j0Oy^hrm8`2D4KVte8%mr_4EeJREB&7~A`_%_nC zm~F%#wvFOBm+4}r8<}3q)WWGexQ$Z$dK<+&al36h>SsIE?au9#=j%YD(2uAucy>@d z@7qB!FX6VhZU@DG^A4(+-+_jUKf^a%yw5arC%MP(+>72ZpJ@ZrO*^T-Jh}7woj9X; zC#Cx-({FZuZzB~&tWop??@-a*^L1E2yQw^-y-LyMgJh+?s>7%tCP;UqH+=tW3I;+;kvr*Mi>SV&)#K~_J8U$UqX_b~dF zkxwD3Mt-Y-G)RUsx>ETiFnUV)r89a%p(%|1Mv}<&TrF3mi+2?|7k*~(kvPgZTLLr; zd3cv4R{~AMSM6~ACjNG@PXE7zh}TRLv9NDy~ch~ z+Ek6oej2e%!7L$9qq_cHqq??h3)s(SpuEIuOEjw80Sc|=CbS!|3Ws5`FM~Edt%;s1QX*iPwsFcyO+K(J=K8Jfl z(-1ebfKxxIMKNk)^ocgrKr^RR3gsH89kv1`W5U$P=pZ8#5Vhu0oPrf780r0qQ;0V# zfn7)z!CErj0-jHUq90=>jJs{~31dRgY$NbNAv z@ByP%pmdp_kj8sdb4c~Aq+Yrtp#u|*6phbvlfz0A812$73*LMOXi%Et7?Dq)! z%>tsbKf|cnFw{t8e+j4>YXGM*`bwecEIGim7jNBa40(*wfK1x?hB-#c%T%B+QD-P( zRA{1{d4MP{^G(#!iwz6dZ!!BVHZ0{>dvRUMEnpXf~|naNB^Y&^Bw0S~5xm zv=nGR$8x7>2vS&Mzy^4v{t&0oWjKUbBE%Dryilw+V22ve>p*6)(eNV2`4yu}41Zz2 z5Hsa$i{VX1F^ujoyv=z@GVc|g;#I@nInL2Q>%?CSU$S2|P_=m1kYu77JP)V|CI&VW;a&jBFOl zS+MaAMuUMiV2Rn?$O~Qp18o=4#^dZa6KI#1X2e%g;MV|@3^WFc&4xKA*B$42q|!&V0R1G`UzJOTH6A;dos^1gDL#LRG` zW)el=l!q1JRy6&x0d=5V11LW`K}))dBcerR*P;kJ=yIkTm|o8GE~bw#eU9l{Og~`y z4O2rTg&n{&j_F{gZl>dzPGvfm>3pV(nEF6Vy0$Xi!}J=aH!-~nw5;ne)1yp}Gkt^U zdrUuP`W@(KVU8ji20BK>Mtz3c-c+U|nVub01b!~lVx|k3-vnwDtD?RIy%=GQVq4T1 z)Y8*YRG-J9>cD>~Y9Pjr52F%5sa7@dE&Kk=)Dk@jlF`u)_&TFgL5DJDY_tpRsnJy1 zGovZCBBqVe!@+N7+7&$-?h_&NVIipnEtc;GQhf^h5@TgoIMaBhsZ7T(ox(Ja=^Uo> zm^LtNWx9rG7t@_guVH#K(}$TJ1JzS}&3-#lJk9i9On+e-8cY0vOb0V{gHouw!5<%c zAL!Xkvzg{IEn+&KX#>+{rfp2uFx|{_FVkz7-p2GErVlWEl<6_1e_{F#(@&Uw$J7!> zc?f4}XX<2{!E`*+Y*4+VbunK+er6oe0;Z)*tC*h0w1MdorWZ0@&9sZ@T#jKQyLT|% z!}MyVH#5DB>GB{-Qh z$#f#q>7aV+%#2@Us_ZIaTF!Jn(;B88rp-(*WV)K^2Buq?J`hVW>}2;9Os{2nGt=9d z-p%w8rbn2*$n;gFr$F_Z{~&$`V*8BgcT5clJGy-bBgFm)!6h1lR|lWIfYVuhuzt!YkV$Mwe;agn z`t3%OxGw!P-1nw0ft&1}N&G(jAK)BIkB0j-4)u@p;RadHCh-lse@-V!+mK11aYH_b ze8v#soHgWMK_y+UyU4cFI8xH}0lU9o`fsMDVZ;w+8qPG9X)@E1OeZm=F{q@A#-Nfe z8iPu@XbeJ}Olb@%>7p^HgvX$gE*gVMx@Zh4>7p^Hq>IL&k}ev9O1fwaD(RvzsHBU= zppq^*2IX;DB}^AEZDQKabRE+jOs`;i1Jm1>(il|IMPpM*7mZCNT{Je8bp4KfA7?7Z zI61n>k&Z^Uk}f&Y(dZT_o*VX%b+q`)u*X3EhEV<^no)c*Y&OQGFTpp8?}inD8r|fM zVd`Y+VmjJ=H*O3jGo8+yxlGHMp3Ah(O))gFyTeUm-zugTF=r#wolLJ}dLz@jnI2~P z9Me~rzQ^pIJ@7CrPCHgAqk&JE_(GSsP_9_$@j)T6~H;Sj4wT%OZYecW5SY24+qIP0A$x zQ09zhI*s|WGow+W!pzCRMp2PD4RkbG#3B~J7bg#7<_4E^Jq!8Y)>F#5o=4cSu2-3! z1RW#Z&7`^3$4vjl^bAvNB=N1Fn(()=Cqi!RuE}{L$@Vskq}nEnq_K6<#Hx``L1r`a zZ9}N$_l_)s%(WxQeTeCu%z232PceONB-y>cjHK3y%A&Dg7}ImIVsOGqCES`=#I%EH zC)3SYl)9XWUBR3im~&ee#dZ(7pJ4tUIi7c!|2?~nV-gT^=okm+oH1#jWuW?aS2I4r zR@v3Sv=Ow2?*PATJkiyl|C2eev<73u`tjeQR(FC%i)+T80ll5+BcMj{9Qa1@>UioY zr^b_R|AXllpt|;+8Bb%}FU&EWMSL66aHi2r6PPA54Vp~#5IdP9vnJQ!gE04{HG|eo zUIF_26l#~hPNDWXF{KFZ519TmWgs3s9pVvyY&<6#ePq`;)Kdlogrxw?;@;8e9Ugr za1+lWOoC<+Ch^@lG|rM z)Wg)r^kSy_nBKzlL8ix;zQOb~(=(u&NXVo3)AFc9yV-pO(*sOz%A*2gv#dsXqGOJ=hq&Sg3-0<5hQc2gS*_76JrjwXvGtFaK$h3^< zJf;hno)0=&G|r};(g8O{K&ES%b1}PjFx|uSa;7&jy>&K?uy-?kfaMP}|0ug(fcyWA zjVcQn(n zOwXG0G9DOCVVUVnXEH5dUpZS?h?%ER;OsBZQueKyGsbKb=g*lW#*4jkK0*w&1=a|o zx(mLrAPk3auPHbUtG=-y67KBE` z{H`c^7QWINoCx_l-1=DjUANm-+|#q>6jwl_<`+{uEyY1Nc5+!Ujo|x>L*c%qI2W_} z`-@3?pDvCB=g$ah6mPSPyj%aAegDmIT1qJ90Zfxh?2u0@p|N!$)2dSHp%cp~ww&@W z;9FV#ePpCKzdSxFQuxYKKs(DbqVU$Jociw%EO{d22wuX=QWz^JpJ5e?akSoGc2mEI z6zeM55OzPPNxWV`cIF-Un#30sG(Z2PVg>lp>JF&vMA+C$nmyLdyAAHvc@*bzrd>>B zt!`%bKBhGGC5r=aCyQt2J%CWJ&m%j0lIiD6&oH%A5kH3M>lHij1axTCZczCQDyxd> zXFAilOe>hyFl}Pm&U6ja9ZU~0y`Sl$OrK-=7Sj)ye$O29W1Fg?KZW~O&C{T)o0Scwtl3}M}6&l6Jq0nS}uhN7k9~@5P+A@KfQwgmTm)NENZDMpZcsEP# zQ0RJFHvBGE=x$pco&w#Z&|yXoFrqTdgWuRRP622ktnLGhj*71lr&YWm{V;|#0Ie7z z^I{8W#;MMCFgl8JFQ)JsE%G}WHYz094^0l~@I$2`tNhTykS;&e9D?^P6b1f{hOG|C z>_O)3AzS^>6(M`^ZbkkbH6IK)Afm-1GG{15x`+`kbfahRmir{54}&RZc5#}~yTSiJ z&g|kNh3uiLfId}dYA7Bjiys(mvMvmH9!MKXDZU%*3Ecn`rqHg?OrRKvw5LM10y$kQ zseKf>S0sp0!z3CIc0ddk1&sEHX<^6l#{Hz5{Emv*VVO7;WI+a@b)qcnb)b`ssEyJw z>U`OaJ`^d~f=iOJM5)5cNR}v7geXKMN)-bXq7tQwL`GDiPk_>tAC)Loj8ce7lqx1j z#3f1 z4|f6mONP@(KgWr06nZ+G(02;`k&$&Q<>jdMa(FWQ8WFaC=pYlc%BSI(i1SN2 zAX6OCi~}ZW6U6zv8gZR)4w$M<6n82#WTWq7ljG{C(aCL)Urh}E#N?D!y=|&cYPb9{dkJ-ur^(+VkBE-x>(O>k7$efC;V<@bg|eU z^^2AxPEMm(E*3Wc%@8p;g!YI#qb!D8afw0?MGY{_5})LgpNw<1urk^e=XCyPo-Mv+ zbTQg}ykWMWGrbX~_0s4(tQATpwJ79 zZd2$@mb_n~&!T4`&fj;VxrRdVf<$c3ibT#F&b9b9dZJc@**l@5K&6Ia%-^M-7BkOK zB3hN77PG)mD(+N%!SE{+k9DJ3L%H}~Nk+viGE|7y3S=yUVpeia{|QhI55! zu1qgAW|d)qxK^RzF&hlE;y9yC;_R3$hC1OJeRcEEbO_zmAyS85+eg<<}AOh@nY{A{om?F^?OThz>@Z#I~3t zh6}_7<+m;7kA`OPnDVGusN5$I+w?f>j{QePRHm(#;b)!(@Dsfs#eiJjxuv%O)Pp0>C zOtf*0c#hE~VI3H6>=bWwqa@>6ky9npiyD|}yhxOHBbTvDJgHFPz!AoE;&?a8GOibs zv49Grj5cK8S;mXS3`R#q;lNVE22svvotQT;4=W>HP<{;qlYu5IkU9GrGl-3%eIcQB z!aHz^aib_dPomC&Q^aQREu*92x`8u{n?+a+`N>gcvl!fs78)-RBN)k1W{VidNRBdF z#MuhbD6>UOQ;0^HEn+Ss8fEH?TSSHOqfusys8xtYnJuDGA|7S7h$|JMQD(b%Lm?Vv zE)%C2$x&vPSY9hjM6;mXVizMh3)(HNRETClyTw6;Xcn|b+@lc9g7%2V7*W~h0manm zHL(z@(3VIf@^Bc|ezAekQK0>%%f)316(?M6xeJK%J zl&i%w5BrH)_M5BF*R}?PD^_T6!i}Z_qEw+Z3AdWA6O|HK*Ciy28$`82+ZioT=t@R4 z3LQ$wgJiuz_c2UH@Lc7GQL9dwZ6!&@w9o6PR@-ETnlc)~pZV}NTk#EqO zrhCNcWrWs=b%Qd+eIjDHMB4_vXSz?Mt&r&8pwCSYiU(FobpN0qO^=G76nb)y(R^5} zp|ix0-it`#2@$heqSt|*6wO_Pj%XhYI%9ZBe7c^{I`K85X`2Y`5@Cta=BLDlErgC} z35g`RW~cOXv){DK2<;V<6XPMdm(eR4+V05u)MCxCNdlm8}J#GF%z&PM_38VHA_Dhz?dORr^e!~>{G$|cuETfBs)sZK@77wd%_dDJ+e=UwH z^p4|w^S8o%fZ~)|cSd9}l5?;#Viu$QVyN>M^BM7&LL;44%a7uuLeqeL5KtJC zw}`ur;@l+`IPI37#TrKYMKk7gzlied$#1_{7bAp_Idxf^NS%fQq%l zTn4EV<=T7Q=yg%9eJ_#q%alw}i6wxP;wGyxH4!LSB4JN`#xhTf=|+FFRBN*strJP9 z$1MxAr@H-Kx727=hd357JoRsuTCM9AMqGpS+TGpgw548qTq5iFsh?OHwC5FSO+5|t zszP0<$vE@zghJP)rURX3v|l`t`cKOuZNlx8!Y=Vr>i3q#+NF#(iIb^XP@{H*LZ30Z zS)rd9{f-fOK-v`1s2x#gaGEeSYGd!9IFE`EKuy{aMn}XsKuffBcaq-`QJfYObb+>2 zq1LpBpl0nBh0aOaU})8zROn)$3$@o3x;kxekWY)bi(*-a_m!zZZQAt;J&wF|XjON! zAL{I8bBDH^5w+jwpk>^~9cDx` zM>o(PlpoC;S7|RQL^H=#+G&Y+=D13WyN7c2Elw+J<9coTeR>(D zi2sMYcaN*0+W-I8teIi&4SRD3L6JKGiYST-3MSqV?}&FzQL&`VyjG?acqh%Y%q-2U z>_S7^srWPT2FFa}l9#$V<`_Ba$aAUiPbzX$7>{a7N*d-^V667iO^$`eF)vMU zEH+MiX`16GS_9Zwo*UaE9FWsKL0?mucj9p)JNPW3L{U?5|p=r z$3c+&Tyj(`T9@3;xzQWdr71e?L+3|E_P;3ZVUe2lsdI->!ZcH4Aa19z z>|K?vOKlQ9;^%=jX;EO#|Wih4I7|6*n?%xwX&O>ZPUDSH=lVx2ENZuMPR0 z@|*%XV1#QbPy5Ds(8$s>Kka+xH^u=?kD%5^jE?WCbWf(8avm|(Z&g&8_KWkVvFQUv z8{qk!k-1ILHl`;uea7^arZ3YjI=?ggK2)B^(vrk+qr0YG(@x47qyKglXEna)JYkI2 z6bAayxT{*lB{j|!r;W)UE6QkmQl2)(f2yc0=!|i0m!fW@Kk%5!4lXxE>{*u9GGYur%$Wo*}UKXUoYShi2aEo+?Ql5+EYMK3q@ zb=h*(*F5!KXa2)ze6PH6aJV->NRFZJZHePzh^2lyX>P+GF2~s#%2GrHN`5A@?(VZ_uwF5z>8_ z%KP4+Uq7PcYEAU(M~vLew3L5Uh?PUWB~LZ8ij$+f6eZ&1SVf{Vy&>Mkn(C#KGG0Ec zX-fJ@nINk)-2=}=c~H|lrr$IzN$=`PltD+iJTVA8tAUL2QlTqZw$k)6;!)K>Nc6=%8`pSHEuG)l_$rZRB;`eJnHHyA9B#(r&_R= zyx>Em^6Vv7c;k{pFS)}@FS~ln^GvE26w1GtR4*u$cqxtYbkg;0q4d>6*SCc-L=#=# z_L228(e-T~naFg+MZKp;CTpVJQzSFB=Z${rT}3iS$5CG@l3g@WUn-Kl6>%>wlCv~X zFCQQeYocC0SYBdMy?ls#7|-XamI!hVm3zGOf$K(@glE|h$9K1iWiv(2Z<@r3V%b^~ z%_oXwdrhaCeCEPCahhlbF+vt;q8Y>pIaJf7CVO2YWr?PMrZ3qeB|Sk$Eg0K$sWnng z(Uj3NPK=UIYszcd5cHy^K27sLuW5=*KHw^mZ)+OW^e||vA~B_DF5bu+%9m?%L>ZId z%e6UTK2vv1OPh9ej+RxLUTylF>t>0UQ&F>W@ish5y51ZoqwpjRrR!byR_6qn zritz{PLw+|{n-?seU*v9%F{oinK@Ao(o`qoq?{y6G$mx5bWN5^G&Rj=DDIHQG_}uo z#=1lH=1bK%qGv{wxKoyCx)F4jd{)!#ps8|$rfC_Nv&%i2Dl*Qv?v{ag5`b!5fw*aM z2$Q;^ohD0|mWsU@!grdi&~zjt+~*#-MAPXEhwr`eeNC4Ucc1)1Q}4RrK4tQtCTFv7 zpK^JLiH_+iYla+vh0CZf(Oh_@9Ik12^D1km9HVJm^K^mhB~3G$M}eklT8g;YvRqRo z;%3YHHElxN9Qlx@j}SLUF4wdladYJ}nraX?SFX`?5pnb6dQFbZD9}5aLNgoUP0SB8 z#b)M#KGl?#8HcxW_iAdHnFRVqQ#+)aFMrT95b5U2)0)O-ri%sgf~NaW-U4Zat9DtK zndD(rk*2^FjX{~35?eI$T_kffwQ7+C z>ZGY_iyM3w%O08rwCDutuc@R(ci)HPFiqo`#wZeHEgFi4Hy@HC%} zW}@dOw)k$6&SWlu?+N@%?$ku*xGnNWraAasbd~RWGA%`waIsZ2C@PgmJ(ICbe#f-b z>6f+3cbgP^3r(FHKa@ussS=X2zVh8Jf6&w;>xgf)JfUeq)^XpDud6B-OBF^8LO#P>s(NZrs1tS`|XvDG?lmR?YCbx z)%1Mp0e)Y~=8X*zi}CY@HWU25lHE1+0(~tMJ0~w`3e4T^hZVi?Ka<;fkKb>awiy$1 z6C4-3v?TW{zl&a4m3!Rp&%dSn%NsW__h&!DRC#VQmgJuI!vo!1!Zw4_Im`&YrA+(Q zc00`^Ma~y<4Tsxors<7bkKs1k)bjM0U21s-nf<(R14NMd0F%1QA8bCZiSFwMo6l>a zd;Y=ZdQEhfKiJ%&iSF_To1bc;yZpiC0YyA-4KaV#M0e{$%s({Ij4ad)&7gfm&%%{} z&M}pXdTnz>9dko7iYph5+vb5{n=8s`yVn(AF3{8!o)PAInJO2Wkwut$n99ZQwsD|@ z7AlVBbrEKUCYskpnt7V&H-;#)HMosTliW18rElwqD>qVrKpcZT`9j-&HYhG}P0t=0J`!;DhI zXP^wTw_<0**vR>u3_`dKQz%bY`$r=E?EEqQFeoG>z#7O%+VtH7#Qrr0H3vF`C|BdQ;ODrt_LUWwP^B%?>hUDiS}o zn}ND4>Y!Tia=Tp7%{|wOlowy!wl6#quU*3geDr@ z?l6;>XmmT^y2EU&<7jld!_3x1v+X;~c8Yj(yThEIiAJ}(%~v(i=ys2}fk};S_nO^$ zQq7i%*&Xsd_nAGt)ZJ5N7J8}BQ*QS4Qh(13bAYCW9RlP`bE21qcxIc^y)?oz*L=)N zH+w3~)tZ)exXm-)Jnp4CJr9}rDMEmF^d!;FR(>ACl%2%5HnrI)bG$R!8K3ZvZ)kOQ~X>*&yM`#wf=+*UUNgmwibf%By}5*Gn{j6+>HoSp zemKS97jQgv^`;q7tUMQX^7wBwmuad_4)@<=UeNSJr+EL(X5t9)oGG5`l^@SZ zd#_Uq|E*@b5~9W8i%xm|+s#c(<>LEJ-TXf?qi$Al=Q{QE-(jxSH+iLCsy{(qWLw^6OjMP7cM_{+><+A6x|U-TE&1~1{me%2mMgYta> zq~#t*>9&eN`JMpF8pE^=^&H^mV{O%RQ+}>+Sedtz=Qc4mKihCv9lR78;Is-gP05c8 za9PEQoYV6g3P0;M9Y@dT`dQO8g(rM3{j8_FG{Dboy{c(Weq4aZ`dpFof&4fTV11+M z38qt;)-YYt>E6xXWCvIs$E$j7%a0O)RH~qu#nWAl~ZirBwl`)+|M0f7h1-(yfd4sV&-^ zz9AsPx}@n~*LMS&S)1lioXWGcwV#Rd+#Ar^%9*Ru-PG-yfLv>*ru|)gT{l>hAGw-m zp0z;J*lx?M_SObX)4E|^Zf(~zr<<>P3e5<`8XHfSzk#BWkQtg><^#IZA_owcu z@VrUWpP(+*eYHFbtV%CMi2`c_(@bY_k2Zl_t<9QR^~eKl*F;aCbhUPB>e?e8ar>{t z_2>>dqDU0?=o8q@x}f96^@tPQE%6w)u5(_GB#=wf(}?R~rE6N-V^ClZt0mJM=X*Wy zc8S%2X`6F*k91IPP2Y0djhZfS+-RN7?n!x0)Nv6#ha=DFn&Nv>E>APfbkeV0J*;P0l3ZCm9r&@0l!+MSm>}fr`F>up6n!CNa9 z^_&)1XbobTBj{Ox=9w(_-Tk@dNz$9m2T z>}!3mX-&^x0{U5}nP!SjJ#$4rtKBM9`g=^PnbdQf{Veg6iu(jx+t12l+9piQF8Wzr zy%g`?&nouPn^u48Tc)L=Pwf1_f!5EO=n2z7)^AMeh#O>~iM;o^t`J>6yc#$}$I(5j zHv?&bTyYR`RITr`wriqV-)HUBM76%py2M1a{wwf4%X*$ma1M*U2=XwgIdqwo zrir$p%*s^6+fZhW(L~!Y%lbqUZNpq^FVjqCc<(r@hkQs=YVRb_aVE8W71mvqSGF%# z+;4s0r6|xIFU|6sZyobeY|sPNuZo=cg`LGB>oU_e=itJ0kn=^#bDQ&)!YI%rrkT#W z3mb~X)^r_rKhuMno+!*i+%ip7g^hw9vQ}x@T$ll>WSZlo_XZ!fHfy3^J0G^TYodDs z4_l6xRD04plS`}sO*;$61}?GcXgXY&6|}^PWt!tN1t?1 z7SlEn+b0)voN10Y+N~?nU0^B~$sD(64R1T1z-$Q5gG@8|&g>&rotL?0cv><)@DVGP zsa!Pcv&nwM8t;wk6|~fP;Z>DO@1!#EsD(!s(Vm#s^%)TKs5Mm6F9CUCnRT3Lo1k|l zAF~FoCC|lTOrI|tk6AA;%@lX`85Z=ob(#sk{`a{lXoYpjOJhN4>nPnEv9iyUpeL-p znqKX5PtZzhmX~e{T4k-*w6)J%(0(TMy#7;G$JZzqHSRuT9cP*;rs4O8r!4n+ikm6+ zp*^3rroT?4#^k517nrs=&-95C&sbHO97Rc>jhgBgEeu+1ZPC=MXbI>ermZ5s=yJfb z)-FYS2l83#OHG4{mIpm+{h;ZVqNjtNvwqeztLS;qc}+`;)`09cRLx!}S{L;E-$vmV zyd&+z++jg4dTB}Sk)SnRqEWcY^5l(YIyr+g6sQrG484zhm9Sv{*dd z_YeDBt4z~+rpfP6x`)O4edhgQlsK_>o+DfDsH!a*F>Y@cI&bx8Wp!&zVA}$G%5}c-fjgm zsc~?-6|ITJ!R=OxA|3~~TZ1&wIJm=FrisSE&#cueM*P3cE2wid#s$TMB4;Wr7IUX{gcF3)@i0~f}X?u+KSqtYDQ1<9#omK zx(+DspmmOEvDnamQSd?Qf{xpXxFc5DF6DWYDe!YeXPM?Q%@O7R^4!FWm(;<&F+7Nh9b_ef7W!zkaQvK^P0+sM1kCg z)%GnwoN0$@dJJ)Mhs9f%9EPii`fxFFm9 zR&B#Y#0A?PO`*e*K*^fYhqZKv*iAHb8rI4u#4gY@bXc4SwF@QvES6RA9>cXcU>Vo{T5+=sfnJ9i?DyvbPDMr z?cX#R!;?UPN7eR)4^J0$?FdcHhev@jHT6bZJv&F!ZHTL94`f;_?jODPzXllQ# z$obqz`~q%o(s93rHVetHzt(Yd@35JDNYe&*HnUG@&+3tz>}Iy}tSY^JajTH#c8aDw zD4~VjMbqd}4~4X}2Qn=dw~hM4Ze{P#G;dV6Pb+)%uPT=p;F)EwXId=Q!86-V`HkWh zi_hViZKs_lQs;-(b|#a0Dx$TW!?akOAN5#BYdh@%c`g-Z$#Ws?>>ZkdLGA4Y7gbzh z$txip?Y@7g5}KF19@5!fuPMLe?T~!?kfz=xn?t(T)+Lp$UrBXHH~SLPVlld8XGl+b z@nwpmn(YngW$#oZrj;BF>0|Htlj6$7ijt!tMRwp{iYiM^g!HusGA$O{OHPOMvzO_( z?@G>x^tZdy%5-{b2ilVr@tk3x{jH_}qpi?^_9acDMlZJp*)JHBt{m_A=Zc~BD2r&Z zxM#Fq=urDErgAYKal`BtHkTllj-C)O%ue%R&j|WD%P@N|6Wzxo&l0Ak&aXz-3ms-Z zqv@y7aiAA9T^yYZTBph9rZbD6qp zTFEq0)7wmUY5Ig|p{Aout2G%nQ_bGeRF7$=rlw5aYbs#6q-i))uv67@GE<7Cg-mTV ztzzn{={2TXG=0ugrs)LJQcXT%sAg+4#W206sSVS9O+`#UX&TF9xm3;WWvZ)bDN|ET zRZN{VRWl9Mbcku9reB!mX|hVGW-B#CF|F6sis>UwW0($Wn$Gl_rbn3EzN(&UnBp~U zW6IL>4O34|zc7_(^4vmoxm!~L(;`hbFg>T~My5@g?qvF0(?X`>nkt$8)U=%`)KAs( zFjGTK7ns^<3cQu-(qB^w(^yUIm}Y1i$h1t;c&1l0&0yN9X*ttZn$|F#*7PqXAGfO6 zE~Y3=HB8MkU1aK_DKwMnIZTs#EYW05sZ954>cF&0(;%idG~LFuL(_bwZ#6y7bV1X* zOo1M%=S*>UVzg_xJyFv=>CvuYdy%HnbYIs9JJ4TwP63Uw7lbNWlAh!mZD)olnul~_ z>^9+wenFn4_9`ZIM!3Zu!=$bzZn4*U<1!q#*q?bRKXk19J(Iff9%ui=q^`Wj*}rL` zEAMgkMNM?&J^^~o@O_VqPTM5JE?!@ef9z-^``X<`?Mz7hS|2e zK6x(Xx6tO;?wBjvaKSOx{#22Ze$Sa_@6|+a6wkB2(L}%eRM6oSsOwKq?C)&kM4Xv;@Yia_z-|iSsapmH3 zc+R)0nC6HhOtA?RH_^KDw9ZlQ}bdh)+(jW1b9CBDc_;Y9dI(Ev){NhV7}PW4wm zvZt|~^1;_1M^mh_Q`p3pGp|_aE1DE;eob1k{HL_7yq<=*fy4PwDGE6Jzt62F=c9B0 z=UfcM$_P;Ub~n{_3j2^se2bebQ#t%RzTRc>mKx$7_92U8iz()_g*=r%tsHbUhv><) z#M}~&eTLhUwhKP2_rfBv8WH6`i`)M^%9Xn1RZTKcH&cAV{(D)^^`#t?*0x+5CX3`1 zg-!8YW7^VVu-JOv)y_eAoDrrMl9Aksin2;*M_gPsI>pwt~^yeuT5Lq@)_Fj z?{%wP&)Q*?r_v=jE~K!gP}=`}A5+Iy>$;R1Y3*Z59a)C(M_GSAzG%Nl5d^y_BDf|h zAF@ahUCWZh;kf!_Z^7YfwLPcm1U1Dy&1jEzXU}2((O-&@Y@Yy?Vk#6*-b1aw*^|1F zeQ_;2g$4V&auz2y>_iseb%#mBlJqmKj1L6KdrfQ)qW3p7q*)FmVq{bXqD^lp-#@z zC8`m|>d;)P|0`7^Cyl6B^BZ-;dJ3#6{~r;?mwDNu!uB1sMHGv5dDuR^JEe-{u}i74 zc;IPZbso|n4o@nfuu_%(wY@HgOH}>s+E|q;61ho{$~DR2oUgUqz&>Qh*W;m<7zMS( zB&Z?gu`Xp@36O>qOl zmgokxML#Ik1g_;dqoVk7*Ccd*hi)~i&!8sRJX^hDSc zMYSwL*h1%JOBDZC`!24@Vd$0f{!Q?g;ucPIHEfCrwJdZz{@qgBe>!{4WmWw_DV^^O zp+=YoIreE*I?B}9&=9Y|BH70j?;vc6kJ)EmEmb)mXV0J6PDio9$FkJpGg;*SYFDNiO{wbaGe`3$U+b$!d<|5)U2i3Bc1c{WDaIxoT+WUxp*qrk_{ z5l&{$2LmWv#9P;`Ii*!++o5bxWl>tJFT<+NePcO%JFA)2<|f6(~Q|$aVi-Fa_uEw@$ zNbT?id~9(TinANH3He;{tbrY?iPg&IB3tacullIcU*~zq79LSQf?_>5C~}7WU(AjI zxx}kyy({Ok|9%~D^;!UHLh)AA;e2Q-q)=O-u3Si^ZZWk*%A(q&w!hj7swb$qgu3d9 zM9wB(?_f<;^aoqS!r~+1p$?GnIwOjFjW%-cLO}nAACKv7H8mKLPhSEA*R8OlF z#ri@m;SaS%2-HW^g<_2rp4SboReyE9qqHWc#X5|fiqiUUT8B=nJXNo#eU|*^Tc>)C z@~_>C$a3YH{oh+v;tT9lbFyE#T32al@0ge$@^}?#JuFIn>RpqMiwf=NZ>B>0SCXQ;`fNT3W=KoSO zi@ulpydR1G`xJvSAueRflOLEU@&=V!D|ycO4K57@8F6aGoKFZc?W#qJ&1|WCm!_I)z7I-krc+r5*g|&PpMhH1uI`I^u#M`@kEwH0mLJ%E z7}^tSDI#o$KcJGY7S$D{*6bSw^aDr`S1hqw|Q3VBbFlU z_(wBmHCLgTxhbCH64fmIpX5(zow^iCCB;CV^Q&tUWufx|)(7X<+WFJfgybVR#pPxHaDs_Ip zx}{S8LZr5i+RzkgFVx;I*M{SG>(p;IO4o7gsZmj#tI!sC0ElOZ9Cvx0!;prYMJ6 zVlL-Hsjl4PQhTBHovvlgD4$2!Q;m@79+$eWRr~H1grqh9e;u?N`a2i8-y z!sii|;t~|=B;HHC#RavoDl1)^tE&jW57+bB)g2_9?_j}f9BPU*Z>$vL zX^N(7$zp8{l~@Us?2kYV@f7#Nb{wmeuAU50z@FXNuI{v`YbENdrYK}P-D$+RjKivx z)H(60N`3!Lo!8V|I2EpaNAarvl{wqBcO?F2$G957{&{X!kJ&4~-K%o1y)U80klOdd zWBK|hHpIq?jXwBui!l5};;+8&fa_pgz&coixsGUoH4g&?p7Rxn_!mz%i&XqI<-bn& zw*X#+`0Izi79tz=j`-`0zn)?W{-%l|*av_I;%^Yb!}0G}{N0AXY|$AjYL$wAMbipZ z9r4A!3Q>wL^;L+4_(ETWcmrSNs}P6qMZOAQXa5ClgfH+_h~D_}UWJ%~FYZ-f#g}@} z*YSnD3b7Ag)~gT~@kKp&;!Ap1=L%oYs}OhN%Xt-ICBB%qSZu|Y@@m9hywzSKcDKDH zphlG8E%r0weZ0LMWYRO0kA?qO45v--G6IiE0lSDad1vFPI zX8U7od5Xi8P<+P`nv37XNOwunjpm@TDRP$_RhAm&;`MBH$>;Eun_cqV{H9@BOj?z4 zi+OulR@g?9>_JF7KXF1j^G=f;`u_m)Nhi)`GmQRHLKG#gvCayfATlq(#!01!HENrns zPdyvfYkn)k3XHqUp9ynWaru>~Ra(N^VQ0*9t=>a;W_h*|X&uV?JS@%%4&4{F)_Acz zE;PvM>NyC@p{$y)AnRPK)9`$^{Bl^TMJ?dQ7Ue*vEk6&djQ$wXF1$d{m(4;2d2TdEb6WZmM4>_7ZYVb< z%_xKYz8UGF+?X{Z4tCr-C?8oz>LY^v`6lS{m~hVpie<} zN!n9e%*q*AVMn;Ewb+AsI38$)qO~#tUtCx#=}QtvurA8?sO?77w$Pv_(07?zW}FYt zwjyW7)+xYN<166Y2S%uEv~Hi(qYj0O>crux^ez%BTx0#bIBXq-X>~b+SI~^(wGSgL zJNwIE*s#{&2yR2tC{fBf!TNmGWaL9@7{>AHdK>X2sR!!>A%7~D)*&oK52a@f62a@>UmcdlumP z%ge0^a>DFSA`1A+c!ojW0ib<-DB?@(ukRzMws`MU%$)sG#72vH$YSnYU&?1@{~A$k zy*fKiP;ZY5AbSt=hmPgBk#YF`U>x_5K9ST<21L5V+o&h)*#J3#_q)sJokTtDff=*p zV&lEp`y&?{J7*t^q$52FNA#iDCn5_B`W7*@&u@|C*2&pXq8#=2t4r+`QkTjaRM#-* zYXxVl>^W6+&saU@oCx_HJ_AI7oH8dJO7HE(@p^=YL2p_a_CD8dbql!#3I(m?RVZkk zEc8sQBIXj6b7o;QXwHNgr6twdHRXf3Zo zK`W0H3R+{VP|ylqg@V@VilY+iO%SxM)dVg#P4{Q|LP?%|_uM*Bd3G5z4aarks1XKz z<75OM%bD=Z6C;e-bMvAKY-;~Z_Q}L*i1|@DV$zI(P&!W2@HWNds4duENn#L6ygO=y z{q@|ks8ZW6Yi`s8dp-Ki2-}{wAZn@|Hm}f!_VUW8a(Locuv^T_^eMI5%y<$07qdIV z;+Itg&r?lbM?UtvI5E|3J#RBCo$GFks^C;p?OF5oMA82JI;s%I@42YOuqTPfR4i}f zRPok4>_PDnYjMJbfT?19LVd?BXpvT< z?DqAiVg;gKkm^U)KNGscLMshb>QVwK(vg1*bfej%BHP$#PHI_Hf0vxpau7UeZ6P`e z>iJC-Srza3Z4})qZm#b#=xyV*oP*1tca2>Jy~{pT>6@S*q8T?W19ZSdME z8!Z|$sm^cI-^ItpMtkP$9rYg*cUF8?|4UIZ?l;k7 zITpRkCd&kQQ~pn|bj&&*UCu2`8YRB8zp3~$n)ay`Ge|m{MWHojm-)s_Kt1!s1cO>& zf^n`@5nc_WBl-xo9mj~EuZGe%Gd8Ax*NQCQFNYQg`fBEvMsVorn2l!j{UM^prf-r5 z8T9R~8k@dWUc>pg40^NwjQ!R9p}us?z7<3IUQ7-5-WrSvvoLuA`f7BkLEnci zHRwywr3QWTxzwPqJC_>#9cLqJeq3lN>iK<4De|A>TZ)=k9~|=<}tCb%`X9uVV&Vau&m?oRu1pw@No`b z zqj)2CZ`>BPY+*|r#D=qMglbsf9 zvT0A#h37yGnU7ku{`GF$%(n=;wN)irQ8max%DP<>rLj?E8*A@w(sKbF8f@o z76}FNoNr#jmxl2`Z)iej|AYz1Ay-WBnHYUzLZPI2+614S9d1boG728RcFEp#qoDLn z+G;+pRpU4vpD@*W7vF%wTZkCv>4+}zr@2s<#Es^_2YNw^AGjP=$j2ScY8Hi0-7hE)H#B$ zZwbs{=*wCZzKGF`zPU9)(pY^2TTL@7IzQ4phr%@HDKu1=zIs(?&>L%o27TqKkVpO_ z7|C%yGU)73XwY}Erpg!EUP#Il?}YvdJ=e;oLAm_=0ot?l#np286~Z)<=ZfFCZU$Na zz0{zu^L%OKF6^B!3;V(ho`w4O22Yi3W}Ij+RTeBf*WfX1QBbmB+`X_~@>Dr#MhbK^ zVk3=t3)5kz@AMc3eb?tHANtlS^^kb~36ic#W}&R_oz(Z{r&OZv(X4f0)*}d?YqcVU z=3T2()^eX*3(LzXF0NIiL0=GZ8T3sDmqA|wi4(NawacJyZ)`+;_V~FB`g+I~gp*UZ zpj?{&clAsT50V=f-j;fVR{{>@Ee+*0yCQ7gFruizfLf{%hFd?sBY==%+8xD=Xo=Zf{*hU;~hT5=7y z=NeAChI3oPK5n+S+2UqP21fEBjpDEeXJL+nGj({Jpq0QYF*1DIs8Xgq*cnG9eI0;i zo*19G9crK#8~qN&6`9YD2OViH2i}WJqq+30&b9JKtnIyt>$8b#5~*95X3>$n_o=tc zNTa!2l-PuJTb#BP$M5rLJ4EE7fl)g!8-E8Dnm=H_;Qh-;zH*5~Kg6|(L2qkjI0i2| zo|f&Pu_D_+y*I;Ax+opFQIE<)ebO2iLZbxjg-tf83S%AuJLa^gRcgox4yPLQW@V}4 z*U<0UOmO%uK8yF#$-h7bFUENsKDp55i*YV?^jdtT&0@#s#S8q-SQ9yR{^Cn*9&@Z( zZ03|ZURvy%^O%FLSy02koJt+0x|KWVO_y@VPm9Chc@er1ImbX_9!k!c>d1O1J*V2i zBZ-5?jZ}l);(Q7@-ecNUmCms%L>p~sEq2Giiw#`6nT3fo- zxYbGL=$>s4pbz9>JzS+Uo*&~Lc!2d7_v&n$#*+hlyd1#y{U%2p;C`OxOnb1bZJsk@ zNm*NeFq+gFGsh+TybnK{L3 zj(ve+*KlkV=d)g{MC=Cf40Mxt0lHbd4Bd)V_ncxo){%3H9bz+dr}zN62kWpo#eVS_ z^nlm{Ja)NYXFA0qoX?!7z7DG1}OQD;L<D(F_@S?CTfYbTer zhs)Z}WgXzM4s%(@xUAzS%f`xQuS3rnZ$d9%&T5NG#@kRK-+@~4U8ob+&o)`wr-51{xDWjdybdXo|7f@in}E3>*0n71kJK2&ovh9$1234%`Ms&8!XzJn=GosW{c{u)uK9VXUh(@>}1Ox z&SAer$I$_{A7=Y8_B_sditVS_evbVwSkzB0ajdW@f6J!)oi^3MZPUmQXw%3Lig&`D z_@)Al3{f_X46*jpPNzt;pNFR4jbm(=O|@-mQ*ARjw=B*rhb`^c(vdA)Z0cj(S$o?w zV)V6X#29E(I}EkgBDZ3jTA;-K06NCr3>|BK1|4tH$S|38nGgA_@Sze{`B42=`%wK~ z@Zt9Kp{=X(p;lebJ{#C)6Z>rTp>5piL)*CBhqiHt4{hU4AKJz}KD3RYjsY%gwS)4H za!~%U4$42#L1|MQls3&lX`4DIZKi|LW;rNrj)Tf==b&S-qvLJEU#xNX`}&J2N06^6 z);sF>qP3lrv(rgAyPcGCC|e@f62+EStlfzcozwy;Sb-BIVzo<@=%m)jbnbzkV*k_Z zf6hr;eZfil;u6kDPQ1=Qy~1+Q=<0OQ=<0S+4uP)Y&`=kRt`RO8U87tymaccb|+7I37Itbn4`WCw1^#k;P>lE~`>kRam>o=(IeZn85 z`#ud#^rh`h@ulre^QG-=>Psz|=}Rq{yd*H}%k1o#~;mI?F?&bdHBc>2@CK4;?+UXS;al*y-+} zW2d)=dPrYS`w+B@Cm%Z0(;ZsuDTJ1I`a{QfhCs)9ilO5@qoI>Mw?OamP+ywvq25*I zxgC~S_y#@B9iGVuFYru-F7n(9UE-MuUFMkwUEz5ETE!*q=UN@$REK%nkMXu2=WRd5 z+kTq2{Ty%U1@^hbKEj{c$MUD1;Pj`S;P$7U5a>@mA=ICGLWDo{geZS%)mVRO)kIE} z;!mxb=1;BK6kl^ji}_QlX8BXA=J->qw)3Y}?dVUf+Qpw*wYxvHYHxpsl`h&~wvaAP zikI4^i{?ggy>!uywaDKy5c4U^he*!80LGHLl@s4ae3X_A6LUx@({>g)~ifC7*Qn zhEiHr@@pYzlVnQkN?sn)COs884SLeO0Qwr*Je6Zp*}p*RN%yYw0=5@$4y8zSDZLb_ zLYtH(Qz@k!Tgv_w9Ijx`3T@eKFKkkQRC&;o?tV=w5&j7JOh`o2O7^czrZ%Z$|4R6L z-K08sdC-KW)f`(5O98^P6*XFyhuqz?2KEP`%Y#BQYS`12a?;&8!CiG_QouIH2N%vIf@}OTcQxUG$B9&uP5&I`BKr4&H1Mev#4 zqJZrM@ELTig0*VD(t_vN@dSfwim!sl2w5404Sv`fag63FAqAARf_OgD8-g?Y$?*- zRIdW|N3tu}UcsIf9E)(sz1fu`}jd&XyEf3k>Iu%;KO)6VbIW`rZZQ2yTJ{?N_ z1+YAhFz?^AnPI;-EMQ&T^eC=wtqjrSF@I6a%>B>ux_5R+wPZ?k2_EO@HYZ~ zx8ZLB{tR5}3taUZxY`$D0{+E|#bP4<#bXd+68^=bA@IS!`2Q4=2qEskzhwyCiGT5M zAJT&H3c^(U+Yx*>{td*x)9`O7{+%vz!}^2`4VxM^KWs@@RoEM0ABKGwb|EY*JTAOJ zczSr}@c!YqhnI&h4SylLDtveNcj1@AgX#>fGptT&o$@;K>nyADLY>#^yi;duogH;{ z*ZI26Z*_tq>P5syG>K>#(IKKRqJKn5#BC9iBKAjIiin786xlKI#>iVD7e=m$To<_| za#!Th$jgy+>$a#nqVBl5Z`A#y?pJkB*UhY#U9WAu9`!_2R8(?Q^Qar69*TN4>aD09 zQAeU`qD1`$_4DhGsDDTO#r2=A|7!hr>c3xqcl{shU#RaB?H?T$ofVxI-936-^u5sw zqL)Okj9wl6QuNyBjnN-Qe;mCh`b2bKOn6L2OqZDMF*nAHikTR5SIj*zvtt&-JRGwu z=E;~BW7fue9P@R|>6qVQ%-EpV=-8iP&&M8(`zh{1Tu^-7_@wx>_#W~7;!EQ1i=Q9A zH2&lGpW^?BcP4}Sev*VI}78Jj9bZ9+8fxcv|3@om^2@bVE#U5shDGV^Bhz7>wt4#=z@#JijwVG!*yZx70a! ze&h!h&Wk?c0!sQF&-%CwJnLf&5)sA_+;tp|77^x08sj$sn#{V!kNo|OXQAEv zUWC5pR|VbZw;sCH?=7hE8Rj9MxgPR)$wNM``5lCQ>LJhHJpbME=YR9;9zgy9yhu=S zK!4Qy)_|eVs6eV=L)Pp-s#`}6zZ^z&-VoN+6k!=v2`53lOiP;UiYVDbv>c5xREP-0l~Q}_R10@ZA$yOM z`*6|QFJ(5gENMP;SjuAPttph}oheHZzMsR(Sf5N;0n6$X+V*vvLp9s4Upjf#F8z9* z*Yl~}Vt?;%Z{bPA+NqXgP@UBlscm^G<2U$JWl)>H!?D#FwAHIqF2Sza;gJ^9lFzcP zXZ_YD%fC4MVGHvAtOe!#1&3?cey&9yJgM()N%n-66mH(KKeTL#;`Vu^wmrk@XzwAFS7FpV$0`;>qLM+gm^PQ&<|YX0ztA_RFObN9Iyl@3BRx z>QSoqs-9fC5C8Yr>({Ea`|q_cwL9hq8hC|cys3tf2JqxY9H`}qjYu=;q>77S) zE@r)jbqedftn*nPVqMPq9IGm;I+^yD=sw8jYPngS~>X@zRxf0>iJt@|w7wKL0Gq42pqCBH} zajiJqhQl4%r>IvYd7XG!j_xhe@(BD0CdQ+`77122GaS_#LUlG;q zDC@}HZ^2)U?P_nTk-txu&9MJcv<-Tph;lIcQrhso6i(^83wG7Jo^b9%c&hUN^lVsg z@P8W#Mx~p>uA>TtTS1cvR1NgV*Q-c ze#hY-IeeMbGm>&vqgj0pr;ntq%V#YfnSeUq$*RteH6!WVt$N^j4!<%w9h30AV>6+L z#vOE{uJD#0#<8=1ba zr$H%iKm46G-LRa2O0Wlcx}g$(0Z@ND#Vs*=4}pf`U%ZQh|0FI$BcYL^9<(lI^isrP zJ})t!kAudG1ZVt$ z#J&{mL>nkpE{3)jH$Xdp@C_mSD{&Rk3AHVRO3d-|p+$I7ONvpL{Yz1T`M(sS;VZ>W z@RH(Y#7kUj^n;e-Y5?D4!gK#p+$x4ZSBo2=&*Cird>KoOfIg3$q<8^&NKuK@QoM+C zQdA)Y-fMwMu?}ww;H_C)z2I$KT)|7R1=sBoS0_`T@AEbK2e>kq_;sE1IA4|5K&3b# z?m<4M#C-^#Wc?A}l{Captf%oENhyA2JtJnr{tN3lF&Fl;tiR&j1VjABdO^&G{XFaM z;sMw%vR)Do!u|*AWqi}w5Pz}?<6+qU!Z$<>;cGmCu#2_6@hHO4#$(VJV>vX|cmh80 z#*+xgvEE=j1xq{D0^=FjyFd-xM}HRKuB^k2=MgS8Dxo8cm!Kn!m!YGKSD_`wTIguw zHRyEXb?80Do6viWjmW3WcpKsSpoW-Xyn}E#>rCTagl8FBptFtl5j%%XCmy!{lXXxcm$Lbyy>%f$QoB*1FO}xSq5P1J51U2-lYmXf&R3H$)5+`$+mB z9LJh3J+O3U?IHtU$8*@wZZZh=u26hCN`@fZowcV7gQXV~$CRvta3K`Ol#E2Uh_#=r z2TNb5AqL3$2=`|lC}Ut5#5zRA!9JLEs7!$UM%Ljn3HD*E#S)LTi4m-$WGd_#2%#t*#p77EV?4r^EXk7mNIWzBlxo~BcXeBqXC^h( zU8U|Cbyu~kswK52lT65KLP!Y9BfOFjAPJBFd56G4AR(|U3n7G%Wm%SI7Q!Q0mRBGo z+3)Y1d;kA`JtTYPv)CWqzW@8a=bn4cx#ygF>jT8UVq%K$w@u6te&xg*;a5#uCH(4% z3gOpGyh`|ICKjptX9?Nw6U)TE&%z&=c#V|rx9|ris-*vrg+DT}PWlg9_~$0Blm4TG z6X9=7+#vq933>To;=_c0cjBXjKQmDy{Cg91!k?Ykq|DDvG>QLx3x8o^i}=6BcNfYq zaf|TZPV7?V7biNz|DA=uJnh za>8=yD+nJbeI@N2FMT!f-(=x$E`2TW6Q!>s{c|n+ywcZ`a?-+wOW#2HdkCTScsD@p zxq|>UmVP(kdrQBU@X^vY6MjMI4-h_9`a^`LOW#8HcSa+#3(prt=a{A%gD39ps@G-0LmX9*YZK<162(hm?{wD7gk4-sE2{Rm;T z^rM7prN2P9UiyoKua|y|@OtU55OzvGLAY1?Ny4{Ef1R*f`WuA3(%&TPm;M&ve(9$O zKT-N=!Y?iT4B;nBKTG&6rJp1G*3v&9{3E4*Ncioge?<64OaGYgPnP~Eqxw@8eoyJ2 zlk(jb{-x5-6aR_Qza;#trC%WY$)e}#}WE&VFtuaH zJVH1*d6cj`d5rLZ$>S4;_-)za#GjiyL3n=hB;ke0hY2rEo+5mH@)5#IlaCT!o_vh( zizgo^d}Z=U!uL-;MficqrwKned4|@e2;s<+&k)W`o+Z3G`5fuj2zfhd@&fUd$>)hL zTlm`KW#X%oFOXifaDDP6Qr0Ydee#QlU!Q!1+&2g(!i~uf5WYD%MaqW>C&EW2X9zz! zIY;;UB>l?>;UJUC#J^(lHR7ML@Y9o3QofFGg5Psn zC;Wqx*9rLz1j0W&`C-Cunfxf>w@%gwziqNk_??rRgx@>aB>cX~EyACfY|+l&BV_+h z-Xi|@E&SgncS-p<3;)4nhxGr$!vB+-J`? zpIZ3OCl5&f7Z(2Clb;~{=LvavX!4W9|D}b$F!|-A{A)s}#N<~H|F?wDhsm!b{)-m= zKa*cg$}bTjKTLit@qcgOf0+C_QvM?$7&!U$#Q!rP7&!S2#DAF(+A{e&iT^i3Xv^g9 zBL3ek{I$v7P0FuY`0JCum-J9pI8pv)(hm_H0x|yp@sfq*@*g7Q0YaqE^0yFwh!CDw z{x;&DLkLeSe>?HtKnPDP|1sjf(Zb(U{ti-(S$MqsU8MhJ3qQB~CrLj+$QvQ$?(5vzf5dQ)~=vDcLh@U1zMlJsc;cqGbDB;=i zUm*P)A^W2I7YVPFe~j>j@?RnRNckrSKU)4t%6tjoM5vYjI^oC4e}k}I{+omw<-bL^ zS^g=)M){`+o8_M&yjlKP!maYp5pI|N0b#5B4+%eB{zrtj%Kw;fr~FR|cgz2ruwDLn z!cO^L67H3Mf$**Jzai|F{~ckk{7Z!W^1mnCFaHO^+vWd6c&Ged2=A8vE8#);R|wxO z|0>}p%D+zdrR75`0r8(Me}eGumOn}OGvzPGGjSSA2cC&o zN6OeP&J%u!lMw!ioPo+%F0K%+VD%_-x_Oxs-N#9PAZ$?n0ZupP3AZWFo4d6005_Mf z5Vk4*02alUImKN%RN@r(p+nCSF5vNUG%OuDPq=#MBH`MhON6fJm{+)@JIph6c=|#dn zQ94KXr%LAu|4iv3;rDW5?hC{Bm#z?gXX#nOA1=MjY4FD>^HliDlz%GxCDNaQS6(Fi zYm`63?}ncvd}#7n!q1&NPxzk6i-ey)soeKYULpK~$rlMvPreK!K3IN?aI$<(P${1$ z{G9Sd!rxH7MEIM^R|r49{379d%PRlA@`HrGmHUS0@nJkdc&>bu@O=3g;l=WC!b{~7 zgjdQZ312KfO!#v76yX<_A0d2y`BB1&^0S1~<;MwU%TE%{m!BfMR(_iBL*+A^V*g*t zycGT;V|yw5XU6tY_-~BurSRVw+e_is7~4yz9bY7zc;IEQ;g3J?Ea7)PaE|a#JV3kQ zdmgw*_-7utMEJcATp|2^%Fp8A_%e9&i^S){KcoD7_!pF)5C4kt^Wk4pem?wL%Fl;? zLYwp9|DpU+_~Q>gNBBn{JWKdT9(;!IJ0HBj$@ug`WzNJK51k|Y*h9|}HXb@pxc$&Y z!jC_6iE!tkD}?QbUL@Rm=w)us|90YUgfFGm8{x|+|3>&K%D=&{U0fpkH09q2Z`0Zv z;dfB}M)+6sQVX+(UnabA_#EMjhtCs!@!^Yv?>~Hr@Pmi15KbR{k?_*tXTj|LkxPU- z+$z`!?IY(2yI9$F!pDy&_ck`TopATai-d0ko z!9(j4UuJ)=Pkf{Oy)p5J`TM~`AD;Lg`}@&}KWFLniOWw+kiI$b!6%g8oLJ&-IJ7l! z-O^jJ{H=+)7fS>g}+$(6yaYgz31}}g&!;Z{x3Mh-o5)Zhq&*5?VAs=o4<+hCnzat z_dSGvtu*;5y~u$Siav|Eg|GeGB~p-Cv;Pi-Uy^}&DD7#ial>i9Jf_?aU_%=$_c>t; z+rx0$_k8_u8gjtzQkwqL$iC?Jgz!!Ds(6Puzxsym>Ip27JL@b51F4*2&r|31OLFXf;9`{BTaEBpJX%_n^EVyCgc(|kX#Hdo);IX7GH*UvR} zb^;%3om+NQ@m^ahe-R-o)o0az7e!tr9HtW0L?VZ*}s8|E9*LU`t#qw(hd(ECF zthIKV)xCP#zpAbF)(%CR{kl30)qcI-+-xZ>&Gxn?2R-5%k8uxmo-N!QMt<3M#w|b%4?A~s6)y#g+Q)Z`UcG1iU2D2L)bC_>6 zcN#T|`Y1Z>c5}1e>aFg}l+x0C9R=4ZjW@E9r+vy%ecrVra^af~$wmWUp1YzjlQECXQgb(_6jvk~XK0yuAWy1QYCXMeZt_-geGMST?2&2DQiZk=p<%-sbw`s1eK#pk-+PIq*! zF;no#r#W21ay&8X&xfMffGO4ey}eGiKeJQsMM$krAAnOfa_hMeo#`~1p;8^UWpWIg zoqoO9?FrYWv}g%pg-x`x<2%$_27J4nz6eGUsE*==l(f-6Tn(cq44&TUY~G5)j)a3X zVF!B4AYr5h{!NX}BJ0hD9R=rbra^99Wv3dqTfI))3bUPkM(XY5fQs8TO!W@ho3qWG z=9cYC;R6;%gC&E3b_gyxsAeD<)n|BGnt8Q(aHrF4gqi(bzq4C`TGv^q zZkXvFfU>?3X4nntvU+Xm!nJyDJFGT)o&D}+;vLSId<@18&e0JRtKaekGje6wKClSK zR=b|oYz3Mcc6*zhuDDh8pa-`J%NrjDW8&Aj%5s>t8j*b%Gc(HW#cdF&uxQJ<$PH&V z3QVUm5O4MNIz8cFi0KTV5$4+ayK&}Wb|b@J%2-Rq1iC4F&g^vBO=~U=Nu-9Pb{{_8 z+zA@3BZO$)EX;9$>{pg^9I3=|EgPKLaqdbj%^=auIHdfukhi$M1NE)<`Wa>>YB)eA zQ(~k1xjbTCZ^{jEIGwm`O6;-~vv8_~JA6QDO~N3EV`DW+YK1E8dr)HmZiTUa_GrA#0eh7z(|Ov=Up0wY}zU{jLd;z@XJPqHE30 z@9g)sh0sCc$-mV58YF$E(zbFIN9aP#-Ppg`6HIQZs=oB^LfDL)80gein{9DlNK3sD z+VF;M(*X|cX@uUb7Q}L=(?j&jx=)>D9XTkL_K}bkHHs4j#`s6cg{TX}hC9k}M94m^ zzP=j!ttK#rstrIy9yRg@^ zhMWoA3+Ptl-2n34lzA6`ez!ojab*A&((N|8$k;j*MWL>`S%;wsXV*HG&NkEOLfcJ$ z*B}UqPv(@n7pA)1`oThr;~JaurU;cVPpp3DcC!Hs$|~C~^23OtIjOZ88AI%uoIvFe zW!>~wBuUJ?Q7DSs)9kkEJ44CU?auyAqq5b89xS$cJy=&(NhifteFwslC#7RCN5zY* zx}TBxNJANzM`7Su3%#(XW1zvIX(BqZ+e~MlGcJt455X0uufV=gO_1<;<`qVhRI1YR z(=yafpP7VCvoRz6Edt%BUsg5@c0SZ`p@*yntw%{T(b}3HQQoXAE!Sq27gtv2s@2N! zQh2jAJy%_;RafTbX4A)D{_NcR)cV5OP_6mO!rI)b)w#AlKfgG&R9jwJxIq)Ce06Pg zYHjYSlIU}OVZC~d=*raUT4ib>`>x(tn#n%q*BA1f`N~qosw}@sJzATaTCB}2X_QM0 zwl-UtStCBTy1KMnD6qP^yvpb7)Y_Et>|=3`nlV1TKA**3pIa3W;&+5tSe$yJHeFe( zhUo<+Jv&zov+?gjg*i_xRKvpD($%$VG1ba1cooK1n_gI+c{Qxgt<;{cUD4k+iCj4E zu?rV!7oLAHOz+>U8JC=`0DbjRqs@>yyf!zrQma;XjWcjFAOm=ckiW(OY!jSq z*XWP7AQUJpyr5Fm2?9~8)@=%Kl7W`@D0WlYnqIgq^^^1;F7-CI>$g!OB(gM}eb?J2 z1Rx|LoAyE-V#E=tVITW-L)zXt$M?{18ierGM@-|q0gh{&uBqxZ-*P!d4VhE{;2>v+ z#j^afw9inFu26&-u7Hv7{Yhnnc(6xFeuztt4c-=0!*c1(XQ!5Ci>S18~-Ea_ax zb>#~=S$sCCfRt8<`e~O$n~X6Lee87V{cv@^)lj$TuOC=RAi}BNBd8dNnB2tQF^cL$`R5f2p9&*Snu#1-di)-7d#BFMzlkDmolCIU|6$lrMZa1{!u`nBiDaz=(tm zm4peySYQ}n8>LnY3(HGap^nw+_2t#sFu%MGC0Sj*4y{qf{KC}LFtfb0G&i$WyIz6z z%uWZ$YIWw?+~O2DPSL|M>QidWh{9GDAYGNKOUtWs(D$jCxv;XjytX{EyimJzAyk$` z%VyRpug`^Qbs;NQxw=wWnF|o_rK+-*m#R>%>fGw<5c}%f%sQ>bfrx)B!Wt@2XGs0x z+!9376R1H3-M0&ey)d=k?}#F_?2H^1;qwvNoL4&i?PmA2{U&OH>3SocXFC3ur9nM2 zcNePF3k#3{wpo?63e|a0qPoN;2djn%9>qW zUeVvxxrOB^W^-+QZFc$kQqb@zS%D+~q8V6Yt+F(a52*t@OMJDmun=bFrq{2AmCDl9 zuvS@|<9lK12K=b91d!y;ZvK{SVGRQ#be=F4;8RN z>$TV?z*MUEv+vK=2Tv~Ax1wP7UyOw>x&+@t!k+jygtRUuB{4F z(jSzWO;A|G(v;6(z1_N7wbW3wFGP)Bw3URp-Hm3W0T;AG^^i>0*_Y(rR+D~kH5{-b z7aXqL;uPNGL||v>z>+PPU_h*SYq{InYPBtDJZUDf<1LsOaTQ7F-kvgIeMMI~y_V^8 zAeZ?^Y9+P6U#}f&X0>E;%1qA9dcZahd6i25`8)Ud0jU)9+at*?QTTFMYJwo*_@HlW!e(zgs}jq8#s zFp%x+_z>q?9Qkw{wo(pEwvrvT!unDbZUCiPoT|R+Cm-j2Oa$bi)NHF8)%J}n=2{Co zV(+P(@p;z>`(zX3UuU{933y)iB~=h6n9Y`F+Ie-iVBKrDk+0R}6l%gpv2C$%ROfq~ z1QF0O#p9OHGyfFw6+V@#mf*kB%_d09X^y0Lcvo&dPs_;Wsyx^2*nE{a^jTCDsB{K8 z2g^u`mC0wX{On74etuU+9F?{U<{f^TnzPYD^JYI_*Ksbi>EJ+JvcX~4j-$XU zf+VJb())WhfS#4`v8XI$w3&zvf7QwIO(nvlcL%=hWak!ZXRC#>SLc@IRx2|#gwENJ zJ{(Y$rPrqxDzkeSoxO66@AWx{rzHH^G8&16sg)H(Z3D6;BIY}YB}go<%%QJ%L((c= zW~^)47+2uc0=tV90-q%t(!}>J_8XTCrR54#1XOq z;&q#aL!9VEXNP=WpxH-)Z$(qS4%FHvi}mg;F+#KnSWGfogo_n}MMM@@tYR%=*vmJO zU$Mkg+R+SC-P=JB9V&{bQ{Snw3os*gS>$fp_LE3S1tuwaB%YegXX>o@t^C#DY9JzZ zD-B=8?~^^t#Bb-SNoVaDUjdY|%k|lvrVf!*VBQbK1V2-z606Od-DYn)%0o8qN+S&i zgnUb;ra}rW4iZ1vaa>`Vn#{C=M*XMu_I3`kDn_D;ZQv)d+1&1!iwau?Q7XA>Bu|Y( zQig~53KT(mAGo!87cc2h5tYVE9hO)p!1%3JB*p13qbMe`Ai-fclqu9__s14Q`8mqC zy)K?W`bLq^>?8^$YChLSqa`ZUOc5tag4%wspdN~icb3`9ILdA49`-3*3 zanT9DkT@pEG)YY_)(hM9-t~I7P5(Wq;a0E)A`FZF<5DE8MDZBiYEK~w-fkjcLi?cC zTOg;C*~+YaB(|>%;MTWxfmr^boNUU<$v(VJ?v`Z@B1>iCQ-P=}DUM2wj7e7MO0QBF zvI-8Scp0PDiW(16DT^p&aR4MrVe?1MHvrsZ@eP&UTBozTgRg@Y2YX5VX4A-abftnV z+ZjT&lT$_yt}0KNftf}_OK-Z~-`tj@By`ikB!s`$K!hDmO36o{I zN^zBOLBxftgtD1dQ-zBK@(MaRH5f=(5z5Y*Zv$s|N9@^<)}P_}+E)vHc2b6BQyjBm zgKJZ9wXQA2;4<7}2?hZs<+#-E3&;@*rdq#lx^=7sh4ktgN-B8X6zZDl!t!;L40zgc zIFklp^OmV_IooiQ1=@J$ahkO+%aAPtHHg^*ObKM#D8!O#F0jay5TzHhP%Ln~M*(Er zMuoj8fo5vh8Bm+e-dKt;3?!AMDKtuN)~2d6l?s2?mtI|x+Gw$|gcu~Pi_Owvo93uj z14WP<-mW#f-Q6C3dz4&QUVe27#r`a6Yy8i8PFIjSM0IMI;X)zq#(|W;2|s0GF5h+n zr{%BpkxY@fdq-Es3q!~c+K)(-w|n)QzX?FlY>>fa*_DKt#+zhjGq1fsuY#4*TABxzVyXRmMd zw8X7ViyACAm0QVXXTuUPo@m+mbDJ{*3%FYYtNWqf^}qV48KlGBTPz*o6~pS`#wn*Q zR%$ZA0i3!|2-WFIK0$pqY-Wimg2rX$lQV zP)y!p2XI1nsf;5Noz78_xi|(A5u)wyQGV|rmbEO8*E((5YPfqVh{RrRSSKEh71V2? z66!4G74bOLy$Raq4efy8oJF@#m?SjtJ}QE2tfTnF^kFBG^f8BI8w;6i)JtC?{H}nJ zA6qQqh&P0y!BDV}Xowtc?zcCiu^pB1e9g&V8_|Qs4)HHh^ElF;QF31{Hd(&NfEAR+ zuUJwUUQ{s=oQ7~uHaYuKH`m&l|3T9tA3cLdjI;YKO|y^DDqfCRd|>Xr9+5O5Y1bX; z5m)hkn6 z42gSA(c&Ou65G4J%ei?l)w%(z-`Yo_Yno1Uk28`*2AsIrgPeyD>*PVtHaC`IvKQW6 zp4w_JIAOxItAm$+=5S9e<`7d>tEWSg%Wrl>own9Zh^iFxv~LodHu)#2=?dkm#j&gW zK!Pe=tM|kuB~VxoFz9^rsxdC1AY4l1ier^RHAW?Nq$t#5jzdtFvyDVj*SfHCt0Mi& z=4J0n=WEWDs;aQN*XL-V^2kK>#*$!;#)D=(rbJr@9a=4-6f>PgXzE*veXB1GrX1Dn zESo-5!ur5eRLG9?ttfHUPXdyCdVHy4cELi5^^uoV!?D`Hf+am_T%duqD6Yc*$Z@ex z$1kFlf{K^V+=Y>kD3SS47g}i3ATo92M+3`Rv%AarR+h+^tRkbSe$L5+TJ{>&_(HLr z!CH_e^F}st#gsN!Jk>JNv-{!D!j9P%g6u*dIn20`%FIKKdgX2j1FMkPS3fzdemI56p zo*8Vj3 zm-zCG1H9UZTV)U&IfMt(GcXNy*+7pLfq*| zTs2`aez??}a$HJISp#25gpQfk27DafB@-lLRL5o%VY{Z}kPIwg-_9@xGB|UejqgVN zAm9neXRFPn8SrhhgJ80Y?AMBMEncO^skzmF_qnT!(#&kFCAWxDxIsaaO7fV}X6ZmF z`m672>P!h1v0ABTs(DnY-0#vrR5C`rY(6gQr!Y>A-A<*a-Yp?u zgyeXjdHB7I(AmSe5EbVp5^KeC(QEkSwaftHmIqxKIJee`_VO$;zLdKa;T~%&cYK-T z9MFl8dz4B*U+ZM>A73(FE~HcVFh}C?rIHWSS|`QEmxBl6%m8n)zvIgY7Tl_FSm=6< zbC2RB3m}#X`QPznq7*Pb(_qMWb)XtwLi?Fv*q`ww=o~Kx8;t&PmYUcHM0vb_CbY5c3|a$JW-vu%#Gx{{sK`J< z-bxOvyq!Ev=RT)NmL}Vy**L}o>9lr&aEhnDQVv5pbei0>>!Kzx8=+{mC^30|C(wybQ<$SyWC^y(n{~T zDFShR&kT`rA!$x+a>=h3YP+IA6>b8U0KZ_WHW*(UzJxMNvmeS-(!gykK?2^jii?K9 zaSsm|j!4_mjqf)xZEy2oaZT4219XDNxu1*>5-&4ulDBrPTFivi)Ny)TtC|1S$|{at zt2b(|&fUPZrGnd^Ma}b8ZzMbh+t5s$FNX&m-u z=wYX`Ws)%hBbEQpqskMGeIF{-r$cT(L?|miS5=yOmw@_cUZx z^FGkm)DC(yzpmt&xT&E>iHnb*xr-JIuc?@T&S4`(Z4U8cohxr!?MP0s$bhjX+J>rg zGceRgHM8B^yoIEborukVYF3eWG4v{aT;`+VrjkrnrX`0?K3Q*ac#`rG@d*|tSLwxx z=@LaYsY=f##2=rGO2vs3CjmfA6}Zy^*-=karE3#FBN25rGnyK?&1i<3+QpkZ77O}^ z(a2#Kw6jJ^xyEi)=Qh<@RvrDaFS* z4dJ`xMGzf2b`+JhrlFXhhk}(+ZUzJV_XhgUG7OWlFB79RS~Zpf+>$M+nZ1Gl;>mub z!WmXMT6%_N84o^v@X|CG!42xXb+Eo#H9LotL4Ge%edvpYcB#*nbo zzEDy452_oJquvSbg6$O2ZwhzL1GE=ZqlCtd4Y>!2og5hlrcA?${ z2v@lifa{G@03uFj?GuKe7b`5DaSPa4e>;;w2J^CHLk?Qb5_59S z*wo_?w(70kyckqEAgppxjkDyrbjmb-4Q+L_voC2Jr65BX0q7uu;rbRx6}O_ImDp@d zN}s#+kE3p+9-(XEd)>`=CQ`LU%eR=7QOp!5D8Z*ti`PLoFN3&G8=f~kRmA~Y1_3ii zaJGPWq7Z;WH18U>$IxYCbypeNr@AVGP@sQWZRY{L=}20wWAEX41$c<_f~$pv7O%+` z7C%Ml83YH6i#;NH2b^0pa=*pnqNUS`-nim1S9r(>SN?qI8J2J2$D4R8adfy0c?>*GAhkjXkppq|GtVvhpE!4*bS7!B9C zizCT;EjnjN9w~nJT4Ei=6wPURs8l@c6^e0N84Qn)dEDcIKiBEg5yLispEg3F^jsTF zPTypY&$5e5wEQgV7X}lB)iEiqI2fFL;mZNh?CJpwAR>nl|2qsuzq*DGj2w2Zvghq8 z+u+q0uykCqqt&<^+!b`UL6-*cIDCV<4Z7>1Kp!*9D>nky6~px@?ojI1pU9)$GXA6TuoC693q{8ylg&Vf@q0 z>e$iyw-juc@esEMUE;^Z3bJss?SDDxLr+ZdY2?=29^0q3*J;6vk_pj`*ieJMo?(ej zM8XvAR2v;*L{9c&V)m8Xl461=d2%`Q3I$>WS(Z;=s^YXYHLndR7h@}=X52lwa0~4n zatrO!4Uo$F(RYI;qd)^p`jA|%U&eQEIG@RN-aN8;K(O$7me#ZI6cR7L+(fx!uhg`9 zE+x{`^#0b?feV-FN8|@ukiE@g3Yg+|6jqB##qr9B=jT{KMGxepKJ9yONJ`2lmZHlX z9>2VsUB4L%#rDYfZi$n!oHFbqyP0THCfCitsutBJ#fEfA(fpK| zh&C7on634xWa+h!Xg0U_JhB3GMbcV-swYjjt^Qj6t$V^+-=Uu(vmOm+{eA7jG z$pnBbd3c%-dm!Udt|N8?9;PDPGa7hx>IJo|Yvi}PiA(8_a$S8b5?MKBCJG&Y2Ag$YL& zb>CbmJz|irzE;st-#&*|eH0WUGp=#3qK>KXAk=O(4_?C=*zO6UpyORS{24iBv~dEB zZX=qloea$c>&ik!{f^yLe`%oJP0!SkkU56uVtrEYOT8{l93Upig#8?vb`p=r+N@WJ zzqzKa%@UcDl*5Oe7Pou0r+a?vh8m1KOhZe4u^?Y+lClr$-c^C|j4<1(qx#``*(TRD z^EdXaZ}21&Y_8S24T&y#&@M|9V@ipf*i>dEi5)KIkg7sMsWjkEl%nX@D|=oq}MQ0;MT44u<~sr$If z+;IkL$8WR9CpAR6c*T@`j%b~?!9~+oUAk~V_rbDnx#}0cIkux5M(Kp**aQKaWW1Fp zEHXv%B5v^F{v!!Ia9{(PTb{Ry`WttEDGx9#_L^O;fb2yL7Gy%=M(Bag_Q9}m%>-S`$&qBC zb<;2A`&rU?cs3TeE^-D|tIi!KD%da>?pEOb4(fnDuFG*`)ruxA>_>pM%OfggHQ#J@ z&6qDW!@e{ng$xwB#f+o_KpHr=+T7ab!cWolg(SqcyxCZL zI63y_$r-hD))gtXjdhJT2wXaoG-jY%2YGm;pe5>!TqGTnz{-J=*F)^8PSyR;%nMjF z09}%9s6`~%f|Ero7q==zGtpK>a^!=^I8{;cG07^ew`~n~Rc7FW;X*kEP8g=Wb=*<$ z!l#--IyH1sR*Ku0(`Mm4)~u`IGX|EZl61)qkGQC21`<@lbsUD+$}fB>cSYMs=~*P7 zb=p(biu(-=aawC|Y)%|omwQujREk=UIbopJ^kF%^$rDA?vC@Y&COx*VeG=PORH+8y zw7)b3D=?a>vdp!(8?(~m(qoSUJ2iGakfw4OugXb|ijGN+trpm3I)KNv$Kx%%DFazkl@qjIjK_VST>n#viFi*#QFM-o+1g`77GF$C4PXXaqdA&xOvb0{^4$HHyW>?iEB)a1|X|n893-4snGtAtcTVCrP zP{6Grk!j?h&PJHg{sL7`RD6V3576bpl#K3X()<8d)Rur~W;7B~AR%agK2k=6Z?aKL%7u-=u}tVI*})#VHfF7qh?4#= zbD^|9=m}=J3CNomtUgdRNhKkd6^8JmFGiU|CliZl#s#7Q471Xe{6NeJwWmvLvLjN% zJZjfEi#TcFPC%>riG_P-RodKs(SuE~5x32xc4SW;Z0Q~Hy}36!U7RKAyV8-dmoHs7 zpY0<)fx}P==ccRxps&`sAVSYs+O_6g2Q%tgy>4!Y&OBF6SlgU|?a5PtM@lz3g52o% z86{~Bd_3eWcFW`^xzcd#kcF7-e|d88Ax8PQi@$2!ct=!=zKG1=~o?iS;2Fv(ru*seHw&LW2>eR&D!MZJ)BxU_R?6p+a8&&DM<|L zk-g$OZ)(Z>I4pA(6%8J+{sSS_?{J=a9Li7z@5$&dic^zL&{RR*)JSI z&;~9VDmz-q`aGOpLLJ>ovA-nIX- zb0H|>{zDm_W+_7mw%EGMj7PMe8it`I?hH9!z}8;S;*P9JM!dAZ@rk>EwBn-6?bZRE zJ>;oQm$<4R^#;=yA5zDAR zZEm3&&`%nTOV6-Cq8#VRgp$MMQ~GfEq;4H9pVIU4pd9NYF|7*U%F+$@GsiQnsy3GR zsWYc}u3<;m`ra+zOX?)wKh>2HMDu3iQWfmRp1#CuSaEBo1al8GSm`{6Lj0ALc6=2Qi+KeHHl0J$T7y#7BhQCICm8Fz}uD z#~9u*o^Ooh8AJTy9=u|dPh^C9h(~1UBd9f+Bi@ju=6oSb8srJ-fH8(2MA|;U3x?8+ zQjFsPLutiwiToPvkL6ODmm5R#2kHG-T0cPNQ-hATL-c(dZBJy}woUqQnto4uK1#7u z3cxmMoxVpYwT@Xn;z24sjzW*2&Wm$v*Oqy9X@SSZ^?VX<9}_-r;YABCSjaEf>NgFl zJZ?QV^Xeu4^SuAL=zlI?sgxaAkNxpR(H(4!;SSDt`043nCjZ`>dUga!3IeBcDFvgz zrXL+EJ$q-~uQ}_!l$%0ZZE14QPlgr_4i>?DW(Km2CNiP<^ung|mxj#Zq+iW5M#tH& zVxAGnbm!6--p)BG_N3`zb7nt;1LuWwP*(T)ft1u%JkoMqX(R{j+**e!u0>PjLbWl2 z8XC~RV35kVezOreH*fL_&VEryzp-F;;@A1xh?a7_K!WkJk4V~S>A?-}GZ6o`##ZjB0OO&{S1y(;j4U`)kpV+5sta)MkjT zIS&miM6|G5rV6!tQ^y3f8U=!|c zrb<}Yk&e<=L#Ai%Gx;f)w@d>)5o6GAu!P+MfLCj5Xdyyl!(Z&$jE?vXhKO0&9uHLh zW>g8>Li52(sy2=W&#m|5;S(LqHYx+pZ0_t?scqTHqhCh6GS#@FGza~)7N`AUf}R3s z+;NZp<(t}UJozCfj-5TZFDHe~c5JI26*z5Pc(=z~0F>huQz#71%;)j1 z@P=YjdQHh5#<%BU*S1?p`4bFknzBFA(;k9{wE=+bJ=qAQn6}K??#NwAj{N!9fDcd4 zW&m~4Kai*-JaYJ@lK?KU1{K}W>32K$7I5!!ljV)0f%e4M<_5bf#&`BMH*5*qf=vHA zyb)*@!2sq>tzA@Guw8&9IxNo1(k8477ptHI(|gOTpJtd?v*@%{YP5&{e@AU zPL;<7d$k+v$VwtfG~mBQt;SH zb~Zkada`&~ec&s)Q+j^p@)B3IfH@fwM0ArH4fTcgcI-BO-0!><&$yLlmPp-M8BWVvaYr$%@r6Sdo=;XU%>2PR$Anh5#C*?xJg zG~CyHQ;ueEhq-=G#UXAq>s9XNvO0N2T9PPEYmkUkplg(;J;CH9p7yWbwOC;=@$#pN zk+so+^c6sESLOXzlHv9C02#jCh%3-E?F!y^xV{astg>ZzwoyolOp%bcV+c?BQY4^H2x=ua%l3FOHXgu~Su7zh`xc(h~i`V+* zoftom>9H7((FHaZkF#vN!<5pq*O&Mq6z6hF{@dJ~$wZjbi$d`S!Ax(a>kf$^S&tO# zx&N$PCytjD1rPR^e!o&@H$~ZTnt*gpShcYnMTvc_gG5c{7wfJxqzNS#?_41}xx|Z~ zYHO$W{!d(KDHHnz=!iU$!86cNV&(zkrBMCCNkqU zb?mK;u*gqJNM*&Zi*ATO!xz_dUBILu#W3lDISc5d5pwytm@RGyu63Mm;=>SX_5+u# zO-qr&RBJDp{FMv5#56dz%Z-bSEniyiOkBjHW^nnxnJxBGz zxIsOCp9&XV8q>q2`&77m;o_JYm+w>K;^lFJ8khFMn3i7*(Mg9VPC1F>h~pxJk&Es< zkl5X4X4v@r^j$=ftb*|;jv>o8S9O0GZxJ3tHcryr;$aQFdXZ8hvYBaJq&;ZQ`I)(} z?eavpTJ(pEdz8Iq_p>3IN$sH0=eQ3fgkq<@wIelw5uLo>*^R>GNb@evr@y>`K*AX# zPqC4i=aoOO=f9)GY0i%mad?qmiW-Vc@~rh$Bt180@vwotm}9VVa#Z2)>S(GtIVZ)(R-3yAg%=InhljgRNkrjg0Y(YAf=6|_=8ek` z3rWGZg5~fETt@ek2P=Xx8-j-^obiEl{EU6XB+2&$q*`okb~`<8d7R@7rROi6)2|9R znF1$?U$19Be^Q8Qy@ztavK2%`d(@>+Qqg6WBWgYD>Ho&euXXIlSze zo}LM9R?(s$cxK4!FSJ~I`tMq?w{ckBdCkL@V#d9SV%6FCi?z_&Anx4z&U5_3b2VT%EDs+~(xMD-7`( z4hJu2tnX?=FV)IK14|JeLU^n_sONp?{*?=tE)DI&%Z5uK zxYyOk^JCJ+mAgEq+!g(nu^uwxKx5LPz5a~cAi)x^#BDC5$$11nfU2G`6ZJ9)^xo6D zaN+VW)4X^^kQf}f(k>ccqY#s1F|9&OTCb!;u+V&@hgNGa*;=y~@s*x@Dkaef;4Y^D8zkhojl%XePdF~t-f(V<}pcqkKKvvgDE+`-8J8dTUH6?4!_ByR8& z7Wd0IUaYOR=IT8|V2{qVZ?`bb+j|>v3*d}ouUj}?R^y2#@T))ln+VbA4n+J6e$Y;y zEBWs64N&bL=-$g!yae$tG&SZN>O-D-Lm2VD?zFbtb>yh@oRvB_BQMf`?E=uy$!LK3 zpY!DdSiN)8rjCg}5}Kv^685tPmZAW|BCXry)~vY!KBdmJ7D%@))y_5aq(Q!PS`RvG z46-*>8$`Z>3}siuIk__e9te}K;=NJ+%8NtDeYx?%2*O`(yf`+^H$jy9U{`uv1r|H{ zN{dc7JUFp!0-Wq)&|WMz?Lu-unBf4kBg*LydKy0t@$Fdl030By=(Ho8+D_>fae1Xl{{Tuq=BGoksBVR(! zD~QhsAO^UVdaKJ7Ug4Mo;X&ee`Qim|!6lnSCXj?OU>8QN2C{S)Q%&yVwMn?yb`-|s z%9b(vkt~RK52MVG4nwLi>6el`C*^9Pk$s-r=*i!@cn%p7p%8Jl*t7L_B@Y-#n-I{Q?O*iq&NX-6veh?c|kx_krCQ?%RZn9?&oc*ujh04LGk1s z9(`c_kF8YNHuho93LrD-29q8gNKr-$qTP!7=iYR5jFv$fl6zMeqcW%?RYV|(m(6x| z?TKS4WZ-cq=s_$7)@Wv{4x8KH!5a+69r-HQV{LgRo5|mKEu0gxgPQZ1F(;>?3?tEg zIF&yI%!D_dBN5D%edjFd1V_(O>6FFB=2k4DO1~F;u8)T2UDa`C_j6DQj|Kf#ZsCCt z$_?yM;l$0b6YAKdl(rRK4yQvnycQ~9G0cVV)-tIWnMhru#4a_eVUO}{QaX4ue4G^Q zk)g-;W=!88?KCME_o-8-ek;7qSA*0IYip6P8r~vjxYaQ8bofL#A3jNVf$%hcFEXn0 zmi97V&kuZE%D&E#cbOVbheL-#>1;T3e2rfA=y!=RXyoD16-I3H$-*M`DGFZkTb(4B`f$(6SYTx94 z%X?IqnATzhH(qkQ7sg-6uv!>@FZ>tAUjpO9k$E7t4Bo7TP+DaSgIFe9*50<2dr;{$6Dsp<-2XL^gX-Dw|0nDU)a7A!h`-Dj`&<`9gb-(Z6FxiJRv;g*Pg7Un09o7 zZ!Xe%uZMjv(caMp5ZzDsOshM53p(Lwk#dCN*xhXeJ6Ri!OB(ZSauROweHafiTcdD1 z#WX_O`-cx0n@WenS=xyd(=O89-D95WolzVXc8C^)2Q>qw;Jd~DjQv=OsYNI$EjC$u zFQa*Eu=n*>P}hzfOvy^0EW#@*H;|f@J)U5lmCI7HG6rk>Qkdldl-;4D(PwFwwU@LL z+pLrDK{UBfY0=5k{L_xmx(G*v3%1Us_rWcmB|ntENMY%V`1e8P*fSbl14KJ+~-vnjX~d zY3otg=v3icI8^!od*K3f^*sMBhVbF5w2A45`T00<9;LN$zUJJ@DO-Ywa>c81$f;(| z9T?}0Y38S$EhuSZf~c_IG|<>*oEo9GT_>is5C#k0#j=X4T;5i=HQaJ>&Vn0aB0c$P zX|1+^qgJs)xNW2KRo%8yT0^ac^9o;a-yL-zl&H|)s6O)7%{)(JXrqyp^w(Aq0m{CQzbOuqtc z4eR0<;%kCv!aK(~rSQ5CZHY?yyp>kOlQaVL%X>AnBS|SdERH33iL$NnFLLz(2_%zR z9?=+*T(Zo_MPaq0p+t|1>xzm<(%Qt6Ks^D4_^rXJ{$!K=2Mz8O1YFKv5 z>UC;L zx{K`@6tD4xLz=beJ**P1ld?=(qBG&>Ix=;JZ`qe&T=r!+_feHA(hI>~^h^9n816eg z^>7+U3714iwEF5b(Il731x<&`Id=RFzV<0AE+Cn3JQjEgK?5-u`$4a=$U*5+Zk<12gV#~Qz zamV=lwG!`!U-_DKj3%j$X2Q`1@Bjl!uKC47(7 zXfe^PT2>a?_GmR+1M9ET|Ex)*Q|#g?pfJb3aO||f>s7vttC!&HClU|Zh;7pfr!Gme zg6}fWs{y|$KaoN^XXs(hpkGKon$r^MKcc$R)Sjd6U|Zo-F|XKKI9bfedK^ey3pb#$ zbL?lHKxbrHd%ljnPPl4oy-Z3txoR+|an@QUU6=xf47B7WfXSm%;dPs1s-sb>?KS2Z z9!^lK*=(}($JFK<%i@@7qtI@4eg7Gtkb^2STJ=L4BbZGz#vmf?v z)e<~2?ZCt9z`|P_NQaMUg=XL-h0>1|p*urg+B<5qLL8VK&udi~%_5^3?01>oQfc5e zw8qQS$VPl@2Ha0P6MSF>>QAjMF{W&7AJ^*4+MI=JHF{XKRZRV848@v)=scrdr!*_d zIlOA^>07gLoC^gQXBcxq^P8f-DIgi2ZmMu( z@fnvGfgBhlEUU&f)#!vfLoj@De5nCwJ~qBg2GpF$ z5j^wG<+IV89bdMHiG>wGPT=u;{)q~l4j<(IDyu2#vk3gOlHrST%qY^p;yTZlpsEz^tbr88RnnsH-M<|;jkFJB`~`C27$Hg?@fmmGA4l}=MKQ6KD0Qkk!NI4QZx z&%drWu-#Ka!YDnO5msysE^m-8%* zyJBrPCve)Epd#$c#`NT9o^VC7$2xySo5N!(#Kg!SRJ5E~6l6pK|OL{Sklj zF_;XVwVUm>F>5=(XO86%G}yy;iaYqXf|uj5SZ57k*&q&I2(Q3lYsM6?08#L#N)yTcp>FyShGc*Qkk7)ygJVkaUPiOH+g5I-HxOA#oZeKJWS)QJ?ViqRpVrD!7)zIpE+~h?zZ;@~gC` zv50brZ#qqs9w&oJcAi;_jRTM__RI^TH$$7+cG#Z`88&EqxSCfbIBPn)pG)wfQ z`Glukvm+g2m%V=z{2$jxcy72{QTyR~E8RhnUeUBuxXB4-vUun`taTe#LnmZe0>vSu zk?=NLQ^Ec5d%(=;u4t~Hkj>@7IQR~h9n!nZeWo>}Ep*$Rv)JJDU2a_jN0`+#5`1U*)%9QUb7RQl!PHG z^d~txB>SAl-8zRAuKW@5!V$M^i*tof{p|a~mxPaS$oL3NeuAV94W#&fA-+Srh9LN4 z_-OcWR^oo8y_F>Z{z>Y6AvJ5%{jJ2#gf9)L-w$W~pC|WA<5)fxp0QZ5>=Q7cXZYte zo*+NfQ2Yt2@r1P=9&~XlJoP?${xD4JY$L_#U#@Q=%iIvO$dz<(41Wfo@nY(q^7E-NziW4^NBUX*C*g zRW@z45|?mIE>!86l;0yiNhRZZ4kuFCBt238RM%iMPAc)79BOysno#RVI-3#CT1~i4 z+EI<%En0k;1Xj?*ncso{^C`!kbiG`7 zT>1c=dvtcwxw~ek1=q{i8S$~v5`K=pP7TgmkBsK0b4%ctDRfHK`p5$4_L*D{FFUv7 zB+u2_N=|hPrRF@WqYtGTP|h{G>f6t+u4S%)BRb>hOcPEG=lLm!6OfHq^1o|whilEi zj`F;$R;yYV?P6i9&RJz&+GURN-q8Nat)jAT=6*8n|JB6Sul1GX<}CjNp(JA*>im!F z$)#6Ofh21my8b_=zwm+UtflwkGxhbq->Qp7CVUAcbdaC=3Xx7VqeH-`@FhU&&~z_w zJ8B7)PDk=60Yq*GYBd=vRbNNaWd8DjUWQ1X$3?@*XHg|`dhAHT;SWYdsvPP@l>nI< z0kzAp`ODEv$_A;PWPr#Vm&Uf09ns5DUxWu;1rwgqK~(0s`w5=mgv%+e=#?84LfYf+ zSgUjz<<)bx0iBhw84t_wwZV85lQR#o(o?aF4l?@wV#^id)7Sg$OX9TGlIfP%kq??A zC_ysJ$(d$QmEdkMDyeOw1B6(az8Z)gXDBbY6;*%XL07Ye6H>&;lp#YxOgnsoG_6oT zWn6STtk8NNvFIWr{fLRK9wNw;p0IBZLCw++hC_(9gr!rb6(cOY$6|y9bKmRMQ-#ot zAXQWxIv8`Om@pajg{d`Q;Eq7%YV}BR=u(=i3B)KZ?-jvEYSMd`sR1LAqU2uj3YZef z{+avL+AIhU;llk(jv>+~)5sFOYhA)ot)NIwTPmIcbX*x+G@Hl`7a<1*>FgoS7$sLS zYFsynN^K|A8}NKg#$4a)GQ8)C1hE&?Ehk+NOhj75mL5*(;40E9mmxVmiur_7!)3Fa zlSxz%nX?;LQQwZm zU2~OqP~|9bQmzb6Qnf$j#_>uB%X0N~`X@(2iC&_8QkaNMOF`A9mQ+%ke7l?9Ad?cs zlz1elhpWZ$3!cK&&!U~V`;SW^t?vTun&)ig64NY<=9TfId6%_cy8jr2UBVB!6o?}f z`n@o|-}}^vZh5IC8C=4i%@t zK)JHZ$Ou(^SSod?wsRAQp~>4gR5}bT{v2@KIi}RcNdSfJ z;C3vh3pe`8*qc=3cH#y?V@$wM4a$-@t zmU~=(S6B@5WsDz&>z>Ea>Ec`)sbF)us!(^#@nXNtyxq|84JN4Y_%ff1ceb=&}` zxribQE_rE(KI1=GbRG~6Ezn}X(2*Oy25X8#+sseO6eM;3tZ`oNH9Vz5gE*@jgsu+s zn$FSsBdExPN5BZ877Zd5vtB^6Ku{A$6+ zo8Y!{NO97zJPyh{twvermM$~2j5-fGJ|~D4<&tpX-8<2lG~1bBtiuQDQHSHAQ)8m# z`GMWfMTypHUx%6W&^5CZp-@UMcB8{6?He${g`ONgski&P&w3U?YAa z$!z$fCm4&{i0^XQ2KAsDCr$F{{r@*#$%T^ArOJ_${ypcs|Jlzu_2IM4dwLi1hU2)) zn4D7Nzz|M~$B!8CaRFF^kDU!KhC^Vk%hp_Acv6^oi+^%07W{K5H>t$l`I>^{R-HBE z9-T|5?;b}@YCYyW|)hqWqGsID{jwgD1S-sWCpB z;=+-F3XcA?ANh3=O4>y=F8oE4>WNX^LwJ17RV-w=n)ooZ&F z_=9G7?Vfj1$euI#E{BxVZJ%F4d!2lgc?kZBQBf z3MKEIn>zsvx8Wz4CeRwLpK|E`nc-SRHMGmAnapxHIVFUrhij!4hiaTrIZ?|5xqPjM zN}e2agm4}ePNj7maoPfdeTAju%rPvS$kWs5JK$a_nfEIHOJO_G)}zDu11&x^2mF$v zbQuZqLwvcSILA-94iEN`_0FCu=75n#uX2YWQXU!ITQX6K-xXZ}i;gTwYIKc{J1n~M zqO@5b3jw8O5$^|aUN~EHKz1k4L0HOcGOWX6tdEbnLJhg`AyLR(tmrH?0MR4)E(n*$ zQR?EUNpw|w)D0hzDxDdvCH>`?l3axwqG%7N-ivtIgJ$GADLk5FB+1^nD_K0T4LPrU zHjveY8l9Ph1>v|{RCNjyZ8sMzoI<4A(y2^toYJ4kMHyNCNIKDCmOL!1GR`?r>AcGNm7q%Z~VfTF0@Ft5}!hXe?{l9 z{1TSSq>`FM+a;~|6*OOQo$_-zjmv@6WlH&#zubU0ZcNheCLSjVPN)4Oxyh^{=};0K z()xwb`d&jOGD(esTYi;-8lU>MwK&ZC4Z<1r;1r5?KqR0^EhD9}Iuuaah&*jmg6@!V zzt#n+bmMbyvVe(gE0I-)rtpFQTNIV=uUrz~ZKrIF;5uaN6h=3N5E!150||;$9d% z4ZY8>(@(0eghS{U*jJ#qwyrBeL|X-;;BYDpjk$g=Q`!g&pojOZVJ*ux!Y%wB6Ug3Tb1Tm zvVd$7qVaF?H(t;`lv_$bedq=tK{OK<>X0MKps_ku<^~ce zt~ENbS@oF~9VEb~&=IycPoZd$R8#J$^#UApgHK|9nv)ZBO8vIWQ`Wo>#=8|}Ch*8b z&ViFanh2Y&20D<60}+>z{VwScw5z5fDfG*PYCGRw+HwBv`!8`7ow(JrFNo|0x&*78 zHS9ugjuhedHuu&;v{%T|bpYOr^RFE|0zC-0)gxW zks%{t6+-W?J_PPj6z}ZmNS*|34mzjYtX@nzZ5t{)`YMo;q2RRLqY)#KiY6Xf=f*MV z&rHz>Zhh*$JPQ{qY0W zC34w8Wqlid{y~=VbcNgzQCcIp+=T zLb-lEJeNx&eodsvD0Pui))47PUjeBv@EnvNRkQ(zC*`6e-lXl5v@$N?3xXmC;q1Hb z1s|HvpfuhAy(Jq_TB|{-U4)xp<6Pn1kjo)+8Vij)dIWc;|L&6@EDKe~H|KMS2qkTH zTEC)HDUMnsd!q8LzZS7H9C+dWZ93MawNH2q?(m3|WJYZ)PPIdVj;_U}t05xPTD@>I zKb){OWx*vtiZn)@6qEOe*3fwrTEnL#Ef0=i6pDv<+(#PG%ba@E((=A>l8YMQ!`kawZRzF@P{-qdx?VyebT>Hwta ztI#trDts~4dyIqe9#LDq8sWGo;#q6mQx35@5?*MbF!$#NzAmA!%HhI0j=qz}K!;f$ zbBlyl#QdjtBup_ua@+Q!uI-3Njf3t*o|4!GQj+6^N4{R##acsA^l;2aBHf^^^uwcT zbO9_s#N%BmpUMF}_NO?WD30ptIW%#^gaMbRB{=53TSFSSA@QU_JMrnEV;_tnMYPmX z(mmc>(0Iix>YofexrJ>;HZZ^JEUh6rx<_>q@k3_PW=cON9VWWNBRWM$Ov5_-WNJ6{ zms(bNX_F~`Ol5K_N;q?k*5!(?^Twd1?KN5!N0Dd@9JqL+b3*@}OPU9!Y94?K8ok!D zNjxc~+(&a<2?_ARfKWF5X$h+L+%n^k;P0mtKZ{8^tk|=~Jz~t&2vlQHP;Lu7W{f#aS=()aG(sLi4qBo9f z?)@o*@Pa&%#FL%B=|txi;WW0S6cGOwJVpP&_PNn|I+Lbrdp>rJ)#XL$-i4K}D(hM+ zK#U7bx};zinz+cKb#d8D_Msx&(s9LcMSDeX-7&m%Pl3Y4l*iVAkl?8{? zIv!r3FZtg}#wgBIW!%TawbkLdSw^2agE2I%?{qt+Es_7O@JuRiyq>;ln?&&F`2QJsXX{|{8>8A$aMSKtuo1o_8vsMrH zDf?uCpZ?Z?zE*CEk&8l#%Yi3j{l{l0?fY9Zh`aZ>)Y{+P~H^7cBMA zgyQN~_?6>uIFq+FOrwN3k{lzA0;fbP1dTb`OS;W)W|j8Rz7`~=sUcgZAIj$`sX4Qw zpONLZR8&a3;Z zyWgZJT!I~2LsasfH6GNnp{xBL#cJpP7(6lqgpwJ3g77ph%Q!Od0CCF=tF3^#>{6 zm@*N{!}HVl1}HD*n_TkH&e5tBB&U0{Gg_{Q8EH+&tp1aExm0R&MP4ULUF44AYN+*`I}X)&TwKxRp;TA2 zJ?}3(otMy96CW3Sm-Ldx6`m8fE=tf^o1%eXh&DV^Y*%?MiFvDnq^LoX#B!{6{^6WK zxT)SN4v(*UQ;I$HG@9ij5d(v5mhNqLBRzIG%TGz}2N>3`~=TLv1 zP?`*#N9TAhp-Kxn<;(1*d3^*OyQ~JnX2}oA(=!+;$i7l!>)Mer9#8hkAC?pVO+-S?{t`4ELX)EkE3-Z@KF5zl{#W9}brW z^9869tNExhVJx=k#d7Fa**`aNL;O5UNv3K8Ntp z&ty#ZdYob2Am`_~1A73@p&p*rPjC#wrdT?j-T0Xjt@N&)dNpsP>BA{4l+|+J=LkB* zeDm*nmXpHE+`ZE6mJ9TpzND4ZZV(n`67z|V820K35trS;*GHA+qb;UK>^bRY)eh^m zLVMy&jO@8_spmMk_&GqQPwCYBxf#}dOMAj+HEMm+O67JBx8`dHCOb^g(o?*}agLoV z-r-OBm<{ZRa4tgcaxwRvdwu`^*WTI3$a&OxzMg)4x@V?6{Wwm>NjE$pF|ilNOt7;! z3qCUTI2*IJGh_xxShLWcZri=|bWgf_98cKXc6*!^)`dHeK!_8TKw9e(X<303>m2Q( z4R`GAS|YlQ#NMr?LkGG=idSSek-|$Lf%|-`o_?9JO_uxP_H>$_o_gx3mtR%=>i1T^ zdY)=}p?wfZ)gDY!SqYe;c=J`|9TYY zsL5F*;tBCc?--E5x`#E*EmQPGKRnapSIz$&`+*^8 z%CCI!@9a16?QyH$`<@K^O|I=4+Pqg|npS%D%YNE%t3tJFw!K;T-Wbd^A@_1!Pjn|c z(jFwlF-A`MPtnWOKU*}%I2z2L)xq>&t(F*`WBJ}>Ny7uT+)sRXXQQ{D!A=SOIOic~ zSo&&OpPlG^%@07mLzFvj@hB`xHp~H2v#lSk(acC6h?)#PdDN4X~36ua-tp|)<+gZB}8`D-|7i)ujs z8bob!MBw1ZQQlNq+%{bLIu!Cw?I-jsKlP2Dm1LBnQppg0sgaoMR!LX5VdvH5-?Sgs zII8gKI*~Tray5Kmel;Cm`#{5igkICzBJ_Ape|19atZ3e?)ZCldiMi%+b;UX+uXDv3EW2g!lN+@+pt*xz%BB zB!l*J7Q@W-CBPlSt*xfwRh&!PgI=j^@ws}fW1Uy$^bTx7XYnrPDxc$BtM7js81j%f zIe0D0z`SF4-74IzE~S+F`ELQmy@T4bwv@A4g9;CZozXh|man9OK5399UY4x5Pj9{M zT$`_PYJ9ylvY@y*@7WH99XGe~7@nVBVXxgWz>s<{jK5X$y+!3DzmG?~bX4UePM@~! zUZTYCo43-LSNq_#2GK~g8e`V1&&92Kq_N$OzG&F_zxzJGJ2f|}wXoj49#~8A2#U)Y{G^B8rs_S%VKV(#|9xG(g`giW&<|!tG1h~FQ<(axdHGb_ zPj~cc^!vZ&p;m`$M{n*-2fZ1rW7`E|FaDF^7KYL}Yr?fLl$Pn&`s4#($vs1O6gj6E zk`GQ;(A!P&Q^{@-o<*rW3s33uHpi_yl`Hw_JpDaeU3q#^Vz5pd-a$2a{qUPcr1n&9 z@Wyk$f2;7;PwnHapRxv9y@z~%mjgLX`fTd={@t+`?)&J`26SMTTP6A3S_zUR7mOBL@MZf zDV{1OJDvS~O=`ijWlfF;UY?i9oM98TdNrKK7saYTvbk1eMgco$4iWU4! z7!SsYW@9OV={%L#s`3Ps-%9zdDo-qzNaZ_UQ6VlC-d9paHO?n#ITy24z7k7~s*|HM zHAg|erouGSg1U!JD;C70D8A8xe&;%E9LX2Wb#gd98 zn9*d`&qg#5S4O;-7PoYf^XW;x^PJGg#ob+ScNg5_8w!4+;B^HLL34?R6}+V29~JzQf|m*C%KhF?yq|inc|Y@h?!E54;r)WN zgZ$43aY@967m2W^CWP+TabIFw`=3*mO zuAC<+7aQ%z13%_iGVnJH5<5@B!=MM)8G{h$oG%s&K(a9)$8y0Wg0CRncwWH^3ce_w z*kwgdK_V}gpH|+BB2>#!s}O`)o6dbKnY2l6WV>_$WKvx9yt{cGYV-2b z@z5k~@(V8c1!1XRg%x7clHcJ zZxjo%0PePCUJ)28YKe0A4^TB=}gMRCo-{|Z;}%enM`Ja zS}_Iwgv7{%s+o?0dliHuChg}knP8E!VbzP;`GR+#hHwvD1y!goo^+@@ZWMHEHiNM3sESw zTlOVMp5SXKWE<)E&JSR0DjpJs!Q9_~SJme(KaFP6#m~}DyChP{%+9OR@MyW2HiFNr*#6ro;FY`21o7AM^&89k*e4iu-bL)J@D(eJhCyIs-U z_9m)#QFPNe16at+&y7pN2_sAg*vgKNjAu0wOe7i-5lMD1E^HQ|U~oDP@2QS2TBUs{ z$|o&;Uy`nWuT9u(!;6XEkc@dm!M~U6h#^_5PJI&PltV9VkEK$!@stl1W9Tug4Vp*V zOF|>eQ{qia6m&_`Sw#Q25-}gT!EmwZHZl^XT!O9cDULy+i-;{u3apP17!htiC1n1I zf=QZ^l_P3I&3qXta-$jXiz-}P^_Mfr{yk7{fW%i9A#uD{hN?xit?7GKGr@(KP$+xo zS#{zS`iGsxWGFv0yW1_dnDwt7S*pHEJmO%FMn?&G|$GM&a8>0 z;;|#JdcO0Ff{SY7M*xcpSlqU5>3}SI^Nj1)Gola*ZZ&b+w~ID>#v6TDJS4qnYwt)*!8T9rPmI3T5Gl72U!cdt`#gOI+n1YWATu@2$ zbkKQ?OTt~oxWMP+y8BUgfgM!{BZdc#6{mRtHK+3NB$ zT)>nsJC(-z0@o8B{~7=c<*yb0wu^sT6p2-NP!nXO6(YrhoPxW2e>_YCCBz9SNTi^f zRKiDx6D&i0)P^PDx?2{U!USSJ=w>9ig6VV*O}+iWMNdyts0V#A=*)!>OsW_Hu)oNm!$=6kYFboH`D z;>%L6n(CJkIl(yd8;nQB{>wtr>kdh;t53#_LRzSMtBc>No=N=aHs2lPLK|oO&{#Ex z{DB=^bTo|F2&H0>ixAOG^bpYnSz}h2lIr(UnTauJ`|hY6&!~EI*0$U^OZU04BRrch zENuuzMhHen3C6~(EuAIIVr(kRBA)zUi^K@a4?x>!(jXPZfW}#DS=P#rOGbZQBcGZkDU~H z?`B8C5b{DOUSz>&@5*=YhKadHFu=s!?YYD&*q4c~Z9?mZiL6~eB`eZ||&0eyF=M3T9mQXR>1D8WU}82QqE-9}3bWxby<_ zpawH%2mKHsa9xsX`9*7Ly@+fOS&(V%@Cx@13vShHSI{&Mo+d4_93x;E)EJ>C5c0 zh2^Xwp8b<>aOqB$UxP|yBTh6DK!KAbTb+O z<(b)xZQ_t?!d?)bg8PVqip6h5mGgor0ee8mp&MJBxRdZ#@*yGa5d{^8DRn~a_)ZdU zGJ8C`O z{T%W*=vEahs4i-g8KF7e5V6sjgz+*3f|%rI{-6zK3WB8?h#)8pI1&T}u8s~rr#Ya1 zUx%qZc;FzV6%do72Kkr zpx~_vZdLF$1-B`E6d0buo)}dwq%6%?y|d)o(Z~-=NJtJW49Z_hE{=M5}{;b86TA%be|yTTuvIs z{e;5KWvYRj%SqoR5q2)qG}gD1EYlzjC2l2sewwu!?bCBj@m9oLkDWSa6vGl=?clI& zFZu3gJ74Dag-oJj%;$uH7y()Lr^rr!RA_fLKKkM`Yo z`jH=h?75%6_d6e}eEQ38{qz6uk-xh4_zVB`v7f(k-{X&c;7i}T{oibSh({pN?pevt|O=YOt_6mS2l?|HaD8aWPVz0e>2z&94OO<1)7!GR$~sKf=yB3&<|KYC+Si#o=;~SUdh6D z952Sz18)4crMWzX*zkiRMH+b(b<8@F8(nOWPfI=sC|$5U$frlO;a6y-q~8f8cKaAS(J9;8~$}dXMzX=_zn=PDocQ4R4+&CL7~b z2ujP}I07c>PcfPAKI6dM5P^G+meU27wkt~ed(uXFy%}ffg1isCjAUVqSXNqKYm5(s z&Eyh?A#etyxs4U!7i31T$x1BT#6Zuz&ojR@ccKzbm)ezfb)gbg>PrjZ$x>~p5>{Jb z<7lfWmDYYu$68|bZN8ugD-~th1g;&cb=hOH!^??y^x1D~N;cMU)3#HsG z>8u1hc=?vFp|9*%OELjO6j{5yB+(UyaIYMcZ8^SB7)z%ZYT3s>Ls|5L&muX7Y#zno zr9au!@!QfYK%-XrOCtX10^XYX!V`5tZpnPl|90TjA0RNWX zp4JQzMF162 zBO`L)JE?A6!e>(OH$mg-d^JK9W$3I#o9~tYopU=qv*Xb6>?5@u5K+lR_tmPEdfUZk zO3l&&1lQcxsGqDfiLf3nCJ|zI(V2{KW&~fYbve8N<`9qh&KrCs5(E;FfVawqT$OsL z+L||J$uoih_+K$Zx z_Hf@)t-aK&?5S6l+RajJdpNUnv{s$}oyziD;}ezoo}=%6cj@l=yMObpdv@-sOzysi z_Qm|4M@F*O2J4;vVsQGLNZ|D6t@u1Aq(#1A*wHIwQApN1d=a!kdU$(0q`fGtd$E8f z$B&G_M&<#~Y|5X0TDkTr`rBx3Oru&Vf6 zJH9`me|dv*(2#VHe9Ezj2^G{rJ2}Bj`gUQhng&?ObQ<|Z8X^;dKPMi{A#IZAW{ReL zdnL)*;rCEN-eE~XtA66RZ+%VCJ|==G)41em7d_pQ9)ZZNi01UYgj+TSN!u~e8Q=EM zYP8N1^0PUSBihSZ%vFJg`^GMPsQ-kdix|(s>U2}5HKPp(hzM@)iHsG#cB_IMQ`K{p8SOhhgv;`c9 zNe3RDbxjs*lLi#L1d8R%Vgy(%iip$|(fB%NKti^#F`cC+k;pnLw!=`CO8kKa2L-%l zJ7A^B*@TvCqCR0?N!>X&e^No|NF8gA(ZuP5?sbDMVUp!x9ZQcW(SDNBomoE}X>!>% z)<#Z>QnD^OLXi=yWvh81k=O{@PQB~iiLr!ilYv5iWwRA-PT!zKb^oLFca(r3LX3S> zRR7;8_#XB4mVPir3nL>Vv_Q+U5+PU(ESO4Zn7WV?Op%oY zRzl+=uANG3wVdV#!%NCksMVy3z*zQ5uG4kcRYYa2Q@Zr`Vqb_A8PFGsh<8>#DhakJ;N^|>g3oenjj!i6B$+#~Z zL9d_ai8ar%N*P}&LI#XWXUkn`f|apd9!~(ZD&?@eBncdTaFLEy!efnQh+IYql*4A@ z(NE4*TJu^96 zbf`lMjdJx^^_o4>VB|bz++9`ol}TLGW>U~yl4?iyO6-GWE9vo>cP@Hn?(|h|ST5Er~+HOQ5 zVTTh2OOcH=-7Gx{Pe-XcK6qemd9f0ej|{9GQJMb}Lu+#9vAd7G``BG~mG7P`?SzRR z^-OcBT;A%8xp1*(MjpnR+#Wt~^!F?CZMUr17M>`z!lRW+9VxTetjr^G%B*i-{16Bq zuC`BnNXwsp4k&~Nv$o9{C8(F2Zm|Ni1ZR4WpiJ;}OTH7g%zgm9%;cOaa78_zla7JD zH>)DMyPY#A=z*3+f}}S@TF|KrLMgu&rOdLiqJMi@d-wWCL$Yznw|CR87+GoA8PB}^ zI{e2qj#W8 ztyURB?TlfajUij1$}!OS8ZtAMAf0EH;+Ql}<5)`l`1?I`+jS7_(AZd5DAmii4&!u~ zf^4oMpPbC6CuJW?ZEP2_OxlS?)}6JF7p;7h4V9bIS!iaZgvGjomwGGi{_b}7h06-; zf7qUFmkyLECN0B87h(5=q}}PSLwMPUFSR&EmO3Vdez?gYGYcNSN9TGTwkxOFLUiR+ zWqzq$F{blpp1F5u%qrNZ_hcK3*{J3m6^CVFWQhICQq)%FOU@dN=+RQOZSAZ1-cW}| z>3c>GHS#j0XZ*dD6Qz^Y2CMJeNFK0`Bgmu6VH>AW1jU%Qc>D$l34O8KBCl3+vc&n< z^D7H9ATKjQ(@F^Kd?@C&MC1`IvY3srJCY{Y=tVx#oGXLSIAy2wl%D^q+T;= z&w{~oYIyvR>g@Pjkn&^u&rcm%QmA{ zm6Xw9QL1y?>R~;QMy~#?v8KlkCDYF$leLfI_&>j`@PWm!bIoPE-13&!3f z!OF6BbfN0U^DA8z;BlL{#64WVQ;RG@A{W}qS=NS_kSkAQ>Ao7t#$#Rr>S867)nUwio|#fCY#a+0l^k{W z(G%Dny_Q+`Jc`K?)+t!F#aiLk4_E6u-xW6MwPj8}%zFG>-@~?NH+cL`3PVQTV7av9 zoigS(JdT??(O9aXC@fR<)Lgh!5g!f}9rw)0RIS!{G~8Qi&vRbov}fLD6Izk96DH{H z0-mnwVyzO=tUySj+l5#uY+=@s(Q0EiE#O3R9oSzl9j(z^W2wEk)E01uw4E0cDq0wB9P^J0lp-O{jxiSl`Ov9OP4t*R^;F+AT@Bky8 zU9PuFyxPv3?TiMZ`l1W^(4a)Cy-auS4O_OPm3{*;Wz2g#^P8)I$?singtbQfc%>QE zE0waf4`(Ztu-uqm(wMDo<_S&EIo+(Hc0(Bx#=O_#7n2q%^I9sl>}<42x7cvSA_@@c zwNKDaj|w!8laSr7G|ya9t|bxN9iDmHOrvRal?E)RbNgoAd0=J;E=Y%a{y|S18<9^$ zyP;G9lZA3y@yx%L@UB(r$00<}W;X)1vg~eE=wP|EO_bZ$9i<`Y%CPfgjSsA9`T(6* znhVuBNP@VPTWUipi|r613XfOnm8Q!o)x%1&*=S;j&_z93Z;bTtbmdqH`V*Cl?gm{g zdFF_ET8;{+pNxSvN^LQ3UlFr!A%njKYt?EZhOumTt&nq?MFl$h%>{gCRLMe8@K zZN$l_XVTC{Gdxf>=749W57f(5try(f0++05;oztxxpKIcwIlT4*MM=X#YQ7oQ2SRy zT8yl;UmMiTp4l`bH+8EW_WB*leq>(%)H9u@J}g;ttW;~U3Su_x!0v2S>MdmENy$zo z%$b@4Q&Lmb>(O?{4ehX5!S!8-Z?QGuAIst@IH2IL3gxUxK_D#B0SHrzu8KItuDqZv zX5Z#nK>}99)l*9Vs3S5U5V6EwmZR4ZZP5Gd>_OITbC!>mnoTO!mTfz5Z{^rA2q2o# zsq)EEeZImZNoGpAw$~kdO2Q-T6#=5%$Uv5N)a1-aS=Wf03jR}Vj%5s4T$|1 zi4h2>u5!Fo>qBuXPYVltsj{=`)xH>U$DwN7E;@OF)iZf$j^!v4lLhI?YD-qwHqYFC zuyI^6n4wkYwPPai)}9w4#Z!$s+%9E`T4K0tRfPLWc)7ya>SIW!PkQF$fbLG91z@jb z1SC9*>gm;iADA14J+HbDH{V#QBU@Ui2dnPHb=GItcOg2H9YjO~ zbpRCv7G|0iXciRQ@0ooE>aBKjNzx(e>d9&ux`b76e5$mN5{WFzG{B6l&B80vga8+o z?J)Wy-HV5?0{-Vb^T1#X@WfjDhDU8lCz}{T>13%|6Yf^)CZt^38PGIm755T-*&AQpWwDks?1HgGDKYeX35$r`TosoGRbwr&wuk8lEDLvAp4Fh;z@wo(SZSfg+TmWr zI7<4yTIo1LyT>!TqDGIpjAP1xJb+nSTBxJx+c=sLc8m)HMKG*f72C`;E0EG$73@y6 z!o3YPmP>VG_IPIZ&`2P|Xg*x=1BV|zaCmO(UE882S`t72X855}vuerE znKsvgTRCM%wxr@Gd&M=)rNzZcsoBF7h7aH+FAj+2BGk?Hj4thC_MxN5TQ0-04Qwci zf_18cs_%28f8SEwI#^oagI1u1&KJQ88=iSj1dy2no>ueWv3_w7+7?E{kgZ4>OTR+P zT!1cRH3MF_yUGwgSUncWxteE=&p`_4=jsAG{iOx*)U}m9dN`MTVORwyRt>kOsM))OZNxr^R=aNWk%AtS$9kl z-Ov@J36c&SB|BQ;M-rXHs8eqae*dY};I$h1XM?Xd+T!*hrgS5)4uB`|%JZ&}HmM7G zWi?p&W`5-_?VAR{%JX^{f)sg#xwGL5+NfUnCSPs);Z1l+?*06V9)cs|2X~}5;P`(W zhru_3l}o|tWn$0U3YUYG@5+zi9%r$TFXuMnGV2#tIf~m-oAcVX_+veM*!#<{uC65PZXe1My84I)>m&&JenIdn`@sVGhkKFG(q@C}+jL6;@ZFj(z(O&Bu z+|<%DB`hCn%UZ0JdzpKesx|aNI(nW&?$?++MsiUuyJD7ZaXf7| zX

    s%Hx#pel1qyg*!F|L9({SNWuwJtdC!bS0R1W=MxPE$ zIlwU!_E2i_Za1nmN3M>xXpyhOexd`p$T8+?iBAF4qfdh)FRq4Ewq+(c{?PsH;y4R% zz=0f_sjK5&bhMq0nX^CpVf4cu4{+q*Vba8`NQ|7QU9q_gmJ8N;X+t!(4 zCUj7Wem~e9y7FfAgy;3oItJD;u#SOs46I{d9RuqaSjWIR2G%jKj)DIx82EU?=+T8m u4#HahtYcsu1M3)A$G|!U)-kY-fprY5V_+Qv>lj$az&ZxjG4TH<2L3mh;K!-} From 4ebb985b46385b8c03ac572648a1c628262f65d0 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 5 May 2010 22:34:41 +0300 Subject: [PATCH 145/260] Added MySqlMigrations.cs (supports stored proc/funcs) Uses MySqlScript class to correctly run proc/func definitions that need delimiter change. Requires MySql.Data.dll 6.2 or later. --- OpenSim/Data/MySQL/MySQLMigrations.cs | 85 +++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 OpenSim/Data/MySQL/MySQLMigrations.cs diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs new file mode 100644 index 0000000000..b16655d1cb --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -0,0 +1,85 @@ +/* + * 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 OpenSimulator 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.Data; +using System.Data.Common; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using log4net; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + ///

    This is a MySQL-customized migration processor. The only difference is in how + /// it executes SQL scripts (using MySqlScript instead of MyCommand) + /// + /// + public class MySqlMigration : Migration + { + public MySqlMigration() + : base() + { + } + + public MySqlMigration(DbConnection conn, Assembly assem, string subtype, string type) : + base(conn, assem, subtype, type) + { + } + + public MySqlMigration(DbConnection conn, Assembly assem, string type) : + base(conn, assem, type) + { + } + + protected override void ExecuteScript(DbConnection conn, string[] script) + { + if (!(conn is MySqlConnection)) + { + base.ExecuteScript(conn, script); + return; + } + + MySqlScript scr = new MySqlScript((MySqlConnection)conn); + { + foreach (string sql in script) + { + scr.Query = sql; + scr.Error += delegate(object sender, MySqlScriptErrorEventArgs args) + { + m_log.ErrorFormat("[MySQL MIGRATION]: Error {0}", args.Exception.Message); + m_log.ErrorFormat("[MySQL MIGRATION]: In SQL: {0}", args.StatementText); + throw args.Exception; + }; + scr.Execute(); + } + } + } + } +} From e4419c34c30e1cf6529ced125f0c05aed24644a4 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sat, 1 May 2010 17:43:10 +0300 Subject: [PATCH 146/260] Converted MySQL migration history to the new format Replaced all NNN_StoreName.sql migration resources with a more readable, single-file-per-store --- OpenSim/Data/MSSQL/Resources/002_Presence.sql | 6 - .../Data/MySQL/Resources/001_AssetStore.sql | 15 - OpenSim/Data/MySQL/Resources/001_Avatar.sql | 5 - OpenSim/Data/MySQL/Resources/001_Friends.sql | 9 - .../Data/MySQL/Resources/001_FriendsStore.sql | 5 - .../MySQL/Resources/001_InventoryStore.sql | 40 - OpenSim/Data/MySQL/Resources/001_Presence.sql | 13 - .../Data/MySQL/Resources/001_RegionStore.sql | 154 ---- .../Data/MySQL/Resources/001_UserAccount.sql | 13 - .../Data/MySQL/Resources/002_AssetStore.sql | 9 - .../Data/MySQL/Resources/002_AuthStore.sql | 5 - OpenSim/Data/MySQL/Resources/002_Friends.sql | 5 - .../Data/MySQL/Resources/002_FriendsStore.sql | 5 - .../Data/MySQL/Resources/002_GridStore.sql | 5 - .../MySQL/Resources/002_InventoryStore.sql | 31 - .../Data/MySQL/Resources/002_RegionStore.sql | 6 - .../Data/MySQL/Resources/002_UserAccount.sql | 5 - .../Data/MySQL/Resources/002_UserStore.sql | 5 - .../Data/MySQL/Resources/003_AssetStore.sql | 9 - .../Data/MySQL/Resources/003_AuthStore.sql | 5 - .../Data/MySQL/Resources/003_GridStore.sql | 7 - .../MySQL/Resources/003_InventoryStore.sql | 5 - .../Data/MySQL/Resources/003_RegionStore.sql | 5 - .../Data/MySQL/Resources/003_UserAccount.sql | 9 - .../Data/MySQL/Resources/003_UserStore.sql | 6 - .../Data/MySQL/Resources/004_AssetStore.sql | 5 - .../Data/MySQL/Resources/004_GridStore.sql | 6 - .../MySQL/Resources/004_InventoryStore.sql | 7 - .../Data/MySQL/Resources/004_RegionStore.sql | 5 - .../Data/MySQL/Resources/004_UserAccount.sql | 8 - .../Data/MySQL/Resources/004_UserStore.sql | 6 - .../Data/MySQL/Resources/005_AssetStore.sql | 6 - .../Data/MySQL/Resources/005_GridStore.sql | 6 - .../Data/MySQL/Resources/005_RegionStore.sql | 40 - .../Data/MySQL/Resources/005_UserStore.sql | 5 - .../Data/MySQL/Resources/006_AssetStore.sql | 1 - .../Data/MySQL/Resources/006_GridStore.sql | 5 - .../Data/MySQL/Resources/006_RegionStore.sql | 12 - .../Data/MySQL/Resources/006_UserStore.sql | 5 - .../Data/MySQL/Resources/007_GridStore.sql | 7 - .../Data/MySQL/Resources/007_RegionStore.sql | 25 - .../Data/MySQL/Resources/007_UserStore.sql | 5 - .../Data/MySQL/Resources/008_RegionStore.sql | 9 - .../Data/MySQL/Resources/008_UserStore.sql | 5 - .../Data/MySQL/Resources/009_RegionStore.sql | 31 - .../Data/MySQL/Resources/010_RegionStore.sql | 9 - .../Data/MySQL/Resources/011_RegionStore.sql | 9 - .../Data/MySQL/Resources/012_RegionStore.sql | 5 - .../Data/MySQL/Resources/013_RegionStore.sql | 103 --- .../Data/MySQL/Resources/014_RegionStore.sql | 8 - .../Data/MySQL/Resources/015_RegionStore.sql | 6 - .../Data/MySQL/Resources/016_RegionStore.sql | 27 - .../Data/MySQL/Resources/017_RegionStore.sql | 9 - .../Data/MySQL/Resources/018_RegionStore.sql | 6 - .../Data/MySQL/Resources/019_RegionStore.sql | 6 - .../Data/MySQL/Resources/020_RegionStore.sql | 7 - .../Data/MySQL/Resources/021_RegionStore.sql | 8 - .../Data/MySQL/Resources/022_RegionStore.sql | 6 - .../Data/MySQL/Resources/023_RegionStore.sql | 6 - .../Data/MySQL/Resources/024_RegionStore.sql | 18 - .../Data/MySQL/Resources/025_RegionStore.sql | 46 - .../Data/MySQL/Resources/026_RegionStore.sql | 41 - .../Data/MySQL/Resources/027_RegionStore.sql | 5 - .../Data/MySQL/Resources/028_RegionStore.sql | 79 -- .../Data/MySQL/Resources/029_RegionStore.sql | 5 - .../Data/MySQL/Resources/030_RegionStore.sql | 7 - .../Data/MySQL/Resources/031_RegionStore.sql | 7 - .../Data/MySQL/Resources/032_RegionStore.sql | 3 - .../MySQL/Resources/AssetStore.migrations | 69 ++ ...001_AuthStore.sql => AuthStore.migrations} | 18 + .../Data/MySQL/Resources/Avatar.migrations | 12 + .../MySQL/Resources/FriendsStore.migrations | 25 + ...001_GridStore.sql => GridStore.migrations} | 57 ++ .../MySQL/Resources/InventoryStore.migrations | 93 ++ .../{001_LogStore.sql => LogStore.migrations} | 3 + .../Data/MySQL/Resources/Presence.migrations | 36 + .../MySQL/Resources/RegionStore.migrations | 806 ++++++++++++++++++ .../MySQL/Resources/UserAccount.migrations | 47 + ...001_UserStore.sql => UserStore.migrations} | 63 +- 79 files changed, 1228 insertions(+), 1008 deletions(-) delete mode 100644 OpenSim/Data/MSSQL/Resources/002_Presence.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_Avatar.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_Friends.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_FriendsStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_Presence.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/001_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_AuthStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_Friends.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_FriendsStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/002_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_AuthStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/003_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_InventoryStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_UserAccount.sql delete mode 100644 OpenSim/Data/MySQL/Resources/004_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/005_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/006_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/007_GridStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/007_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/007_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/008_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/008_UserStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/009_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/010_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/011_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/012_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/013_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/014_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/015_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/016_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/017_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/018_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/019_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/020_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/021_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/022_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/023_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/024_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/025_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/026_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/027_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/028_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/029_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/030_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/031_RegionStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/032_RegionStore.sql create mode 100644 OpenSim/Data/MySQL/Resources/AssetStore.migrations rename OpenSim/Data/MySQL/Resources/{001_AuthStore.sql => AuthStore.migrations} (51%) create mode 100644 OpenSim/Data/MySQL/Resources/Avatar.migrations create mode 100644 OpenSim/Data/MySQL/Resources/FriendsStore.migrations rename OpenSim/Data/MySQL/Resources/{001_GridStore.sql => GridStore.migrations} (64%) create mode 100644 OpenSim/Data/MySQL/Resources/InventoryStore.migrations rename OpenSim/Data/MySQL/Resources/{001_LogStore.sql => LogStore.migrations} (95%) create mode 100644 OpenSim/Data/MySQL/Resources/Presence.migrations create mode 100644 OpenSim/Data/MySQL/Resources/RegionStore.migrations create mode 100644 OpenSim/Data/MySQL/Resources/UserAccount.migrations rename OpenSim/Data/MySQL/Resources/{001_UserStore.sql => UserStore.migrations} (72%) diff --git a/OpenSim/Data/MSSQL/Resources/002_Presence.sql b/OpenSim/Data/MSSQL/Resources/002_Presence.sql deleted file mode 100644 index a67671ddf6..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_Presence.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -CREATE UNIQUE INDEX SessionID ON Presence(SessionID); -CREATE INDEX UserID ON Presence(UserID); - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_AssetStore.sql b/OpenSim/Data/MySQL/Resources/001_AssetStore.sql deleted file mode 100644 index 6a9a127b8f..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_AssetStore.sql +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN; - -CREATE TABLE `assets` ( - `id` binary(16) NOT NULL, - `name` varchar(64) NOT NULL, - `description` varchar(64) NOT NULL, - `assetType` tinyint(4) NOT NULL, - `invType` tinyint(4) NOT NULL, - `local` tinyint(1) NOT NULL, - `temporary` tinyint(1) NOT NULL, - `data` longblob NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_Avatar.sql b/OpenSim/Data/MySQL/Resources/001_Avatar.sql deleted file mode 100644 index 27a307244b..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_Avatar.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE Avatars (PrincipalID CHAR(36) NOT NULL, Name VARCHAR(32) NOT NULL, Value VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY(PrincipalID, Name), KEY(PrincipalID)); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_Friends.sql b/OpenSim/Data/MySQL/Resources/001_Friends.sql deleted file mode 100644 index e158a2c802..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_Friends.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -CREATE TABLE `Friends` ( - `PrincipalID` CHAR(36) NOT NULL, - `FriendID` VARCHAR(255) NOT NULL, - `Flags` CHAR(16) NOT NULL DEFAULT '0' -) ENGINE=InnoDB; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_FriendsStore.sql b/OpenSim/Data/MySQL/Resources/001_FriendsStore.sql deleted file mode 100644 index da2c59c6d0..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_FriendsStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE `Friends` (`PrincipalID` CHAR(36) NOT NULL, `Friend` VARCHAR(255) NOT NULL, `Flags` VARCHAR(16) NOT NULL DEFAULT 0, `Offered` VARCHAR(32) NOT NULL DEFAULT 0, PRIMARY KEY(`PrincipalID`, `Friend`), KEY(`PrincipalID`)); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/001_InventoryStore.sql deleted file mode 100644 index 40dc91cdc6..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_InventoryStore.sql +++ /dev/null @@ -1,40 +0,0 @@ -BEGIN; - -CREATE TABLE `inventoryfolders` ( - `folderID` varchar(36) NOT NULL default '', - `agentID` varchar(36) default NULL, - `parentFolderID` varchar(36) default NULL, - `folderName` varchar(64) default NULL, - `type` smallint NOT NULL default 0, - `version` int NOT NULL default 0, - PRIMARY KEY (`folderID`), - KEY `owner` (`agentID`), - KEY `parent` (`parentFolderID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `inventoryitems` ( - `inventoryID` varchar(36) NOT NULL default '', - `assetID` varchar(36) default NULL, - `assetType` int(11) default NULL, - `parentFolderID` varchar(36) default NULL, - `avatarID` varchar(36) default NULL, - `inventoryName` varchar(64) default NULL, - `inventoryDescription` varchar(128) default NULL, - `inventoryNextPermissions` int(10) unsigned default NULL, - `inventoryCurrentPermissions` int(10) unsigned default NULL, - `invType` int(11) default NULL, - `creatorID` varchar(36) default NULL, - `inventoryBasePermissions` int(10) unsigned NOT NULL default 0, - `inventoryEveryOnePermissions` int(10) unsigned NOT NULL default 0, - `salePrice` int(11) NOT NULL default 0, - `saleType` tinyint(4) NOT NULL default 0, - `creationDate` int(11) NOT NULL default 0, - `groupID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - `groupOwned` tinyint(4) NOT NULL default 0, - `flags` int(11) unsigned NOT NULL default 0, - PRIMARY KEY (`inventoryID`), - KEY `owner` (`avatarID`), - KEY `folder` (`parentFolderID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_Presence.sql b/OpenSim/Data/MySQL/Resources/001_Presence.sql deleted file mode 100644 index 84fa05794c..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_Presence.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE `Presence` ( - `UserID` VARCHAR(255) NOT NULL, - `RegionID` CHAR(36) NOT NULL, - `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', - `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' -) ENGINE=InnoDB; - -CREATE UNIQUE INDEX SessionID ON Presence(SessionID); -CREATE INDEX UserID ON Presence(UserID); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_RegionStore.sql b/OpenSim/Data/MySQL/Resources/001_RegionStore.sql deleted file mode 100644 index 31164b35ed..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_RegionStore.sql +++ /dev/null @@ -1,154 +0,0 @@ -BEGIN; - -CREATE TABLE `prims` ( - `UUID` varchar(255) NOT NULL, - `RegionUUID` varchar(255) default NULL, - `ParentID` int(11) default NULL, - `CreationDate` int(11) default NULL, - `Name` varchar(255) default NULL, - `SceneGroupID` varchar(255) default NULL, - `Text` varchar(255) default NULL, - `Description` varchar(255) default NULL, - `SitName` varchar(255) default NULL, - `TouchName` varchar(255) default NULL, - `ObjectFlags` int(11) default NULL, - `CreatorID` varchar(255) default NULL, - `OwnerID` varchar(255) default NULL, - `GroupID` varchar(255) default NULL, - `LastOwnerID` varchar(255) default NULL, - `OwnerMask` int(11) default NULL, - `NextOwnerMask` int(11) default NULL, - `GroupMask` int(11) default NULL, - `EveryoneMask` int(11) default NULL, - `BaseMask` int(11) default NULL, - `PositionX` float default NULL, - `PositionY` float default NULL, - `PositionZ` float default NULL, - `GroupPositionX` float default NULL, - `GroupPositionY` float default NULL, - `GroupPositionZ` float default NULL, - `VelocityX` float default NULL, - `VelocityY` float default NULL, - `VelocityZ` float default NULL, - `AngularVelocityX` float default NULL, - `AngularVelocityY` float default NULL, - `AngularVelocityZ` float default NULL, - `AccelerationX` float default NULL, - `AccelerationY` float default NULL, - `AccelerationZ` float default NULL, - `RotationX` float default NULL, - `RotationY` float default NULL, - `RotationZ` float default NULL, - `RotationW` float default NULL, - `SitTargetOffsetX` float default NULL, - `SitTargetOffsetY` float default NULL, - `SitTargetOffsetZ` float default NULL, - `SitTargetOrientW` float default NULL, - `SitTargetOrientX` float default NULL, - `SitTargetOrientY` float default NULL, - `SitTargetOrientZ` float default NULL, - PRIMARY KEY (`UUID`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `primshapes` ( - `UUID` varchar(255) NOT NULL, - `Shape` int(11) default NULL, - `ScaleX` float default NULL, - `ScaleY` float default NULL, - `ScaleZ` float default NULL, - `PCode` int(11) default NULL, - `PathBegin` int(11) default NULL, - `PathEnd` int(11) default NULL, - `PathScaleX` int(11) default NULL, - `PathScaleY` int(11) default NULL, - `PathShearX` int(11) default NULL, - `PathShearY` int(11) default NULL, - `PathSkew` int(11) default NULL, - `PathCurve` int(11) default NULL, - `PathRadiusOffset` int(11) default NULL, - `PathRevolutions` int(11) default NULL, - `PathTaperX` int(11) default NULL, - `PathTaperY` int(11) default NULL, - `PathTwist` int(11) default NULL, - `PathTwistBegin` int(11) default NULL, - `ProfileBegin` int(11) default NULL, - `ProfileEnd` int(11) default NULL, - `ProfileCurve` int(11) default NULL, - `ProfileHollow` int(11) default NULL, - `State` int(11) default NULL, - `Texture` longblob, - `ExtraParams` longblob, - PRIMARY KEY (`UUID`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `primitems` ( - `itemID` varchar(255) NOT NULL, - `primID` varchar(255) default NULL, - `assetID` varchar(255) default NULL, - `parentFolderID` varchar(255) default NULL, - `invType` int(11) default NULL, - `assetType` int(11) default NULL, - `name` varchar(255) default NULL, - `description` varchar(255) default NULL, - `creationDate` bigint(20) default NULL, - `creatorID` varchar(255) default NULL, - `ownerID` varchar(255) default NULL, - `lastOwnerID` varchar(255) default NULL, - `groupID` varchar(255) default NULL, - `nextPermissions` int(11) default NULL, - `currentPermissions` int(11) default NULL, - `basePermissions` int(11) default NULL, - `everyonePermissions` int(11) default NULL, - `groupPermissions` int(11) default NULL, - PRIMARY KEY (`itemID`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `terrain` ( - `RegionUUID` varchar(255) default NULL, - `Revision` int(11) default NULL, - `Heightfield` longblob -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -CREATE TABLE `land` ( - `UUID` varchar(255) NOT NULL, - `RegionUUID` varchar(255) default NULL, - `LocalLandID` int(11) default NULL, - `Bitmap` longblob, - `Name` varchar(255) default NULL, - `Description` varchar(255) default NULL, - `OwnerUUID` varchar(255) default NULL, - `IsGroupOwned` int(11) default NULL, - `Area` int(11) default NULL, - `AuctionID` int(11) default NULL, - `Category` int(11) default NULL, - `ClaimDate` int(11) default NULL, - `ClaimPrice` int(11) default NULL, - `GroupUUID` varchar(255) default NULL, - `SalePrice` int(11) default NULL, - `LandStatus` int(11) default NULL, - `LandFlags` int(11) default NULL, - `LandingType` int(11) default NULL, - `MediaAutoScale` int(11) default NULL, - `MediaTextureUUID` varchar(255) default NULL, - `MediaURL` varchar(255) default NULL, - `MusicURL` varchar(255) default NULL, - `PassHours` float default NULL, - `PassPrice` int(11) default NULL, - `SnapshotUUID` varchar(255) default NULL, - `UserLocationX` float default NULL, - `UserLocationY` float default NULL, - `UserLocationZ` float default NULL, - `UserLookAtX` float default NULL, - `UserLookAtY` float default NULL, - `UserLookAtZ` float default NULL, - `AuthbuyerID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - PRIMARY KEY (`UUID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `landaccesslist` ( - `LandUUID` varchar(255) default NULL, - `AccessUUID` varchar(255) default NULL, - `Flags` int(11) default NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/001_UserAccount.sql b/OpenSim/Data/MySQL/Resources/001_UserAccount.sql deleted file mode 100644 index 07da57118d..0000000000 --- a/OpenSim/Data/MySQL/Resources/001_UserAccount.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE `UserAccounts` ( - `PrincipalID` CHAR(36) NOT NULL, - `ScopeID` CHAR(36) NOT NULL, - `FirstName` VARCHAR(64) NOT NULL, - `LastName` VARCHAR(64) NOT NULL, - `Email` VARCHAR(64), - `ServiceURLs` TEXT, - `Created` INT(11) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_AssetStore.sql b/OpenSim/Data/MySQL/Resources/002_AssetStore.sql deleted file mode 100644 index a7d7fca8c5..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_AssetStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE assets change id oldid binary(16); -ALTER TABLE assets add id varchar(36) not null default ''; -UPDATE assets set id = concat(substr(hex(oldid),1,8),"-",substr(hex(oldid),9,4),"-",substr(hex(oldid),13,4),"-",substr(hex(oldid),17,4),"-",substr(hex(oldid),21,12)); -ALTER TABLE assets drop oldid; -ALTER TABLE assets add constraint primary key(id); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/002_AuthStore.sql b/OpenSim/Data/MySQL/Resources/002_AuthStore.sql deleted file mode 100644 index dc7dfe0115..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_AuthStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_Friends.sql b/OpenSim/Data/MySQL/Resources/002_Friends.sql deleted file mode 100644 index 5ff64389f1..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_Friends.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO Friends (PrincipalID, FriendID, Flags) SELECT ownerID, friendID, friendPerms FROM userfriends; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_FriendsStore.sql b/OpenSim/Data/MySQL/Resources/002_FriendsStore.sql deleted file mode 100644 index a3638678c8..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_FriendsStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_GridStore.sql b/OpenSim/Data/MySQL/Resources/002_GridStore.sql deleted file mode 100644 index 35b9be122e..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_GridStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE regions add column access integer unsigned default 1; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/002_InventoryStore.sql deleted file mode 100644 index c161a687e2..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_InventoryStore.sql +++ /dev/null @@ -1,31 +0,0 @@ -BEGIN; - -ALTER TABLE inventoryfolders change folderID folderIDold varchar(36); -ALTER TABLE inventoryfolders change agentID agentIDold varchar(36); -ALTER TABLE inventoryfolders change parentFolderID parentFolderIDold varchar(36); -ALTER TABLE inventoryfolders add folderID char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE inventoryfolders add agentID char(36) default NULL; -ALTER TABLE inventoryfolders add parentFolderID char(36) default NULL; -UPDATE inventoryfolders set folderID = folderIDold, agentID = agentIDold, parentFolderID = parentFolderIDold; -ALTER TABLE inventoryfolders drop folderIDold; -ALTER TABLE inventoryfolders drop agentIDold; -ALTER TABLE inventoryfolders drop parentFolderIDold; -ALTER TABLE inventoryfolders add constraint primary key(folderID); -ALTER TABLE inventoryfolders add index inventoryfolders_agentid(agentID); -ALTER TABLE inventoryfolders add index inventoryfolders_parentFolderid(parentFolderID); - -ALTER TABLE inventoryitems change inventoryID inventoryIDold varchar(36); -ALTER TABLE inventoryitems change avatarID avatarIDold varchar(36); -ALTER TABLE inventoryitems change parentFolderID parentFolderIDold varchar(36); -ALTER TABLE inventoryitems add inventoryID char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE inventoryitems add avatarID char(36) default NULL; -ALTER TABLE inventoryitems add parentFolderID char(36) default NULL; -UPDATE inventoryitems set inventoryID = inventoryIDold, avatarID = avatarIDold, parentFolderID = parentFolderIDold; -ALTER TABLE inventoryitems drop inventoryIDold; -ALTER TABLE inventoryitems drop avatarIDold; -ALTER TABLE inventoryitems drop parentFolderIDold; -ALTER TABLE inventoryitems add constraint primary key(inventoryID); -ALTER TABLE inventoryitems add index inventoryitems_avatarid(avatarID); -ALTER TABLE inventoryitems add index inventoryitems_parentFolderid(parentFolderID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/002_RegionStore.sql b/OpenSim/Data/MySQL/Resources/002_RegionStore.sql deleted file mode 100644 index 45bf959d40..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -CREATE index prims_regionuuid on prims(RegionUUID); -CREATE index primitems_primid on primitems(primID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/002_UserAccount.sql b/OpenSim/Data/MySQL/Resources/002_UserAccount.sql deleted file mode 100644 index ad2ddda239..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_UserAccount.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, lastname AS LastName, email as Email, CONCAT('AssetServerURI=', userAssetURI, ' InventoryServerURI=', userInventoryURI, ' GatewayURI= HomeURI=') AS ServiceURLs, created as Created FROM users; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/002_UserStore.sql b/OpenSim/Data/MySQL/Resources/002_UserStore.sql deleted file mode 100644 index 393cea0f12..0000000000 --- a/OpenSim/Data/MySQL/Resources/002_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add homeRegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_AssetStore.sql b/OpenSim/Data/MySQL/Resources/003_AssetStore.sql deleted file mode 100644 index d489278f13..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_AssetStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE assets change id oldid varchar(36); -ALTER TABLE assets add id char(36) not null default '00000000-0000-0000-0000-000000000000'; -UPDATE assets set id = oldid; -ALTER TABLE assets drop oldid; -ALTER TABLE assets add constraint primary key(id); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/003_AuthStore.sql b/OpenSim/Data/MySQL/Resources/003_AuthStore.sql deleted file mode 100644 index af9ffe6f1d..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_AuthStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE `auth` ADD COLUMN `accountType` VARCHAR(32) NOT NULL DEFAULT 'UserAccount'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_GridStore.sql b/OpenSim/Data/MySQL/Resources/003_GridStore.sql deleted file mode 100644 index bc3fe7df7a..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_GridStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE regions add column ScopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; - -create index ScopeID on regions(ScopeID); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/003_InventoryStore.sql deleted file mode 100644 index 4c6da91aab..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_InventoryStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_RegionStore.sql b/OpenSim/Data/MySQL/Resources/003_RegionStore.sql deleted file mode 100644 index cb0a6141cc..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - - CREATE TABLE regionban (regionUUID VARCHAR(36) NOT NULL, bannedUUID VARCHAR(36) NOT NULL, bannedIp VARCHAR(16) NOT NULL, bannedIpHostMask VARCHAR(16) NOT NULL) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/003_UserAccount.sql b/OpenSim/Data/MySQL/Resources/003_UserAccount.sql deleted file mode 100644 index e42d93b92c..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_UserAccount.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); -CREATE INDEX Email ON UserAccounts(Email); -CREATE INDEX FirstName ON UserAccounts(FirstName); -CREATE INDEX LastName ON UserAccounts(LastName); -CREATE INDEX Name ON UserAccounts(FirstName,LastName); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/003_UserStore.sql b/OpenSim/Data/MySQL/Resources/003_UserStore.sql deleted file mode 100644 index 6f890eeec1..0000000000 --- a/OpenSim/Data/MySQL/Resources/003_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add userFlags integer NOT NULL default 0; -ALTER TABLE users add godLevel integer NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_AssetStore.sql b/OpenSim/Data/MySQL/Resources/004_AssetStore.sql deleted file mode 100644 index ae1951df39..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_AssetStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE assets drop InvType; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_GridStore.sql b/OpenSim/Data/MySQL/Resources/004_GridStore.sql deleted file mode 100644 index 2238a888ea..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_GridStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE regions add column sizeX integer not null default 0; -ALTER TABLE regions add column sizeY integer not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_InventoryStore.sql b/OpenSim/Data/MySQL/Resources/004_InventoryStore.sql deleted file mode 100644 index c45f773904..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_InventoryStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID is NULL; -update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID = ''; -alter table inventoryitems modify column creatorID varchar(36) not NULL default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/004_RegionStore.sql b/OpenSim/Data/MySQL/Resources/004_RegionStore.sql deleted file mode 100644 index 4db2f7587d..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE primitems add flags integer not null default 0; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/004_UserAccount.sql b/OpenSim/Data/MySQL/Resources/004_UserAccount.sql deleted file mode 100644 index 8abcd53a16..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_UserAccount.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -ALTER TABLE UserAccounts ADD COLUMN UserLevel integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD COLUMN UserFlags integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD COLUMN UserTitle varchar(64) NOT NULL DEFAULT ''; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/004_UserStore.sql b/OpenSim/Data/MySQL/Resources/004_UserStore.sql deleted file mode 100644 index 03142afa37..0000000000 --- a/OpenSim/Data/MySQL/Resources/004_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add customType varchar(32) not null default ''; -ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_AssetStore.sql b/OpenSim/Data/MySQL/Resources/005_AssetStore.sql deleted file mode 100644 index bfeb6525af..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_AssetStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE assets add create_time integer default 0; -ALTER TABLE assets add access_time integer default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_GridStore.sql b/OpenSim/Data/MySQL/Resources/005_GridStore.sql deleted file mode 100644 index 835ba89369..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_GridStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE `regions` ADD COLUMN `flags` integer NOT NULL DEFAULT 0; -CREATE INDEX flags ON regions(flags); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_RegionStore.sql b/OpenSim/Data/MySQL/Resources/005_RegionStore.sql deleted file mode 100644 index c4a9527403..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_RegionStore.sql +++ /dev/null @@ -1,40 +0,0 @@ -BEGIN; - -create table regionsettings ( - regionUUID char(36) not null, - block_terraform integer not null, - block_fly integer not null, - allow_damage integer not null, - restrict_pushing integer not null, - allow_land_resell integer not null, - allow_land_join_divide integer not null, - block_show_in_search integer not null, - agent_limit integer not null, - object_bonus float not null, - maturity integer not null, - disable_scripts integer not null, - disable_collisions integer not null, - disable_physics integer not null, - terrain_texture_1 char(36) not null, - terrain_texture_2 char(36) not null, - terrain_texture_3 char(36) not null, - terrain_texture_4 char(36) not null, - elevation_1_nw float not null, - elevation_2_nw float not null, - elevation_1_ne float not null, - elevation_2_ne float not null, - elevation_1_se float not null, - elevation_2_se float not null, - elevation_1_sw float not null, - elevation_2_sw float not null, - water_height float not null, - terrain_raise_limit float not null, - terrain_lower_limit float not null, - use_estate_sun integer not null, - fixed_sun integer not null, - sun_position float not null, - covenant char(36), - primary key(regionUUID) -); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/005_UserStore.sql b/OpenSim/Data/MySQL/Resources/005_UserStore.sql deleted file mode 100644 index 55896bc9a0..0000000000 --- a/OpenSim/Data/MySQL/Resources/005_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL, `attachpoint` int(11) NOT NULL, `item` char(36) NOT NULL, `asset` char(36) NOT NULL) ENGINE=InnoDB; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/006_AssetStore.sql b/OpenSim/Data/MySQL/Resources/006_AssetStore.sql deleted file mode 100644 index 31043537a3..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_AssetStore.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621' diff --git a/OpenSim/Data/MySQL/Resources/006_GridStore.sql b/OpenSim/Data/MySQL/Resources/006_GridStore.sql deleted file mode 100644 index 91322d6431..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_GridStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE `regions` ADD COLUMN `last_seen` integer NOT NULL DEFAULT 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/006_RegionStore.sql b/OpenSim/Data/MySQL/Resources/006_RegionStore.sql deleted file mode 100644 index c1ba5b808d..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_RegionStore.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN; - -alter table landaccesslist ENGINE = InnoDB; -alter table migrations ENGINE = InnoDB; -alter table primitems ENGINE = InnoDB; -alter table prims ENGINE = InnoDB; -alter table primshapes ENGINE = InnoDB; -alter table regionsettings ENGINE = InnoDB; -alter table terrain ENGINE = InnoDB; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/006_UserStore.sql b/OpenSim/Data/MySQL/Resources/006_UserStore.sql deleted file mode 100644 index 10b321e601..0000000000 --- a/OpenSim/Data/MySQL/Resources/006_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE agents add currentLookAt varchar(36) not null default ''; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/007_GridStore.sql b/OpenSim/Data/MySQL/Resources/007_GridStore.sql deleted file mode 100644 index dbec58432e..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_GridStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE `regions` ADD COLUMN `PrincipalID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; -ALTER TABLE `regions` ADD COLUMN `Token` varchar(255) NOT NULL; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/007_RegionStore.sql b/OpenSim/Data/MySQL/Resources/007_RegionStore.sql deleted file mode 100644 index 404d248e6b..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_RegionStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN; - -ALTER TABLE prims change UUID UUIDold varchar(255); -ALTER TABLE prims change RegionUUID RegionUUIDold varchar(255); -ALTER TABLE prims change CreatorID CreatorIDold varchar(255); -ALTER TABLE prims change OwnerID OwnerIDold varchar(255); -ALTER TABLE prims change GroupID GroupIDold varchar(255); -ALTER TABLE prims change LastOwnerID LastOwnerIDold varchar(255); -ALTER TABLE prims add UUID char(36); -ALTER TABLE prims add RegionUUID char(36); -ALTER TABLE prims add CreatorID char(36); -ALTER TABLE prims add OwnerID char(36); -ALTER TABLE prims add GroupID char(36); -ALTER TABLE prims add LastOwnerID char(36); -UPDATE prims set UUID = UUIDold, RegionUUID = RegionUUIDold, CreatorID = CreatorIDold, OwnerID = OwnerIDold, GroupID = GroupIDold, LastOwnerID = LastOwnerIDold; -ALTER TABLE prims drop UUIDold; -ALTER TABLE prims drop RegionUUIDold; -ALTER TABLE prims drop CreatorIDold; -ALTER TABLE prims drop OwnerIDold; -ALTER TABLE prims drop GroupIDold; -ALTER TABLE prims drop LastOwnerIDold; -ALTER TABLE prims add constraint primary key(UUID); -ALTER TABLE prims add index prims_regionuuid(RegionUUID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/007_UserStore.sql b/OpenSim/Data/MySQL/Resources/007_UserStore.sql deleted file mode 100644 index 3ab5261373..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add email varchar(250); - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/008_RegionStore.sql b/OpenSim/Data/MySQL/Resources/008_RegionStore.sql deleted file mode 100644 index 7bc61c04e4..0000000000 --- a/OpenSim/Data/MySQL/Resources/008_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE primshapes change UUID UUIDold varchar(255); -ALTER TABLE primshapes add UUID char(36); -UPDATE primshapes set UUID = UUIDold; -ALTER TABLE primshapes drop UUIDold; -ALTER TABLE primshapes add constraint primary key(UUID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/008_UserStore.sql b/OpenSim/Data/MySQL/Resources/008_UserStore.sql deleted file mode 100644 index 4500bd5d7b..0000000000 --- a/OpenSim/Data/MySQL/Resources/008_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add scopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/009_RegionStore.sql b/OpenSim/Data/MySQL/Resources/009_RegionStore.sql deleted file mode 100644 index 284732aa6f..0000000000 --- a/OpenSim/Data/MySQL/Resources/009_RegionStore.sql +++ /dev/null @@ -1,31 +0,0 @@ -BEGIN; - -ALTER TABLE primitems change itemID itemIDold varchar(255); -ALTER TABLE primitems change primID primIDold varchar(255); -ALTER TABLE primitems change assetID assetIDold varchar(255); -ALTER TABLE primitems change parentFolderID parentFolderIDold varchar(255); -ALTER TABLE primitems change creatorID creatorIDold varchar(255); -ALTER TABLE primitems change ownerID ownerIDold varchar(255); -ALTER TABLE primitems change groupID groupIDold varchar(255); -ALTER TABLE primitems change lastOwnerID lastOwnerIDold varchar(255); -ALTER TABLE primitems add itemID char(36); -ALTER TABLE primitems add primID char(36); -ALTER TABLE primitems add assetID char(36); -ALTER TABLE primitems add parentFolderID char(36); -ALTER TABLE primitems add creatorID char(36); -ALTER TABLE primitems add ownerID char(36); -ALTER TABLE primitems add groupID char(36); -ALTER TABLE primitems add lastOwnerID char(36); -UPDATE primitems set itemID = itemIDold, primID = primIDold, assetID = assetIDold, parentFolderID = parentFolderIDold, creatorID = creatorIDold, ownerID = ownerIDold, groupID = groupIDold, lastOwnerID = lastOwnerIDold; -ALTER TABLE primitems drop itemIDold; -ALTER TABLE primitems drop primIDold; -ALTER TABLE primitems drop assetIDold; -ALTER TABLE primitems drop parentFolderIDold; -ALTER TABLE primitems drop creatorIDold; -ALTER TABLE primitems drop ownerIDold; -ALTER TABLE primitems drop groupIDold; -ALTER TABLE primitems drop lastOwnerIDold; -ALTER TABLE primitems add constraint primary key(itemID); -ALTER TABLE primitems add index primitems_primid(primID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/010_RegionStore.sql b/OpenSim/Data/MySQL/Resources/010_RegionStore.sql deleted file mode 100644 index 031a746aeb..0000000000 --- a/OpenSim/Data/MySQL/Resources/010_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -# 1 "010_RegionStore.sql" -# 1 "" -# 1 "" -# 1 "010_RegionStore.sql" -BEGIN; - -DELETE FROM regionsettings; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/011_RegionStore.sql b/OpenSim/Data/MySQL/Resources/011_RegionStore.sql deleted file mode 100644 index ab0196934a..0000000000 --- a/OpenSim/Data/MySQL/Resources/011_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE prims change SceneGroupID SceneGroupIDold varchar(255); -ALTER TABLE prims add SceneGroupID char(36); -UPDATE prims set SceneGroupID = SceneGroupIDold; -ALTER TABLE prims drop SceneGroupIDold; -ALTER TABLE prims add index prims_scenegroupid(SceneGroupID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/012_RegionStore.sql b/OpenSim/Data/MySQL/Resources/012_RegionStore.sql deleted file mode 100644 index 95c27573de..0000000000 --- a/OpenSim/Data/MySQL/Resources/012_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims add index prims_parentid(ParentID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/013_RegionStore.sql b/OpenSim/Data/MySQL/Resources/013_RegionStore.sql deleted file mode 100644 index a6bd30da50..0000000000 --- a/OpenSim/Data/MySQL/Resources/013_RegionStore.sql +++ /dev/null @@ -1,103 +0,0 @@ -begin; - -drop table regionsettings; - -CREATE TABLE `regionsettings` ( - `regionUUID` char(36) NOT NULL, - `block_terraform` int(11) NOT NULL, - `block_fly` int(11) NOT NULL, - `allow_damage` int(11) NOT NULL, - `restrict_pushing` int(11) NOT NULL, - `allow_land_resell` int(11) NOT NULL, - `allow_land_join_divide` int(11) NOT NULL, - `block_show_in_search` int(11) NOT NULL, - `agent_limit` int(11) NOT NULL, - `object_bonus` float NOT NULL, - `maturity` int(11) NOT NULL, - `disable_scripts` int(11) NOT NULL, - `disable_collisions` int(11) NOT NULL, - `disable_physics` int(11) NOT NULL, - `terrain_texture_1` char(36) NOT NULL, - `terrain_texture_2` char(36) NOT NULL, - `terrain_texture_3` char(36) NOT NULL, - `terrain_texture_4` char(36) NOT NULL, - `elevation_1_nw` float NOT NULL, - `elevation_2_nw` float NOT NULL, - `elevation_1_ne` float NOT NULL, - `elevation_2_ne` float NOT NULL, - `elevation_1_se` float NOT NULL, - `elevation_2_se` float NOT NULL, - `elevation_1_sw` float NOT NULL, - `elevation_2_sw` float NOT NULL, - `water_height` float NOT NULL, - `terrain_raise_limit` float NOT NULL, - `terrain_lower_limit` float NOT NULL, - `use_estate_sun` int(11) NOT NULL, - `fixed_sun` int(11) NOT NULL, - `sun_position` float NOT NULL, - `covenant` char(36) default NULL, - `Sandbox` tinyint(4) NOT NULL, - PRIMARY KEY (`regionUUID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_managers` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_groups` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_users` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estateban` ( - `EstateID` int(10) unsigned NOT NULL, - `bannedUUID` varchar(36) NOT NULL, - `bannedIp` varchar(16) NOT NULL, - `bannedIpHostMask` varchar(16) NOT NULL, - `bannedNameMask` varchar(64) default NULL, - KEY `estateban_EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_settings` ( - `EstateID` int(10) unsigned NOT NULL auto_increment, - `EstateName` varchar(64) default NULL, - `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, - `DenyAnonymous` tinyint(4) NOT NULL, - `ResetHomeOnTeleport` tinyint(4) NOT NULL, - `FixedSun` tinyint(4) NOT NULL, - `DenyTransacted` tinyint(4) NOT NULL, - `BlockDwell` tinyint(4) NOT NULL, - `DenyIdentified` tinyint(4) NOT NULL, - `AllowVoice` tinyint(4) NOT NULL, - `UseGlobalTime` tinyint(4) NOT NULL, - `PricePerMeter` int(11) NOT NULL, - `TaxFree` tinyint(4) NOT NULL, - `AllowDirectTeleport` tinyint(4) NOT NULL, - `RedirectGridX` int(11) NOT NULL, - `RedirectGridY` int(11) NOT NULL, - `ParentEstateID` int(10) unsigned NOT NULL, - `SunPosition` double NOT NULL, - `EstateSkipScripts` tinyint(4) NOT NULL, - `BillableFactor` float NOT NULL, - `PublicAccess` tinyint(4) NOT NULL, - PRIMARY KEY (`EstateID`) -) ENGINE=InnoDB AUTO_INCREMENT=100; - -CREATE TABLE `estate_map` ( - `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - `EstateID` int(11) NOT NULL, - PRIMARY KEY (`RegionID`), - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/014_RegionStore.sql b/OpenSim/Data/MySQL/Resources/014_RegionStore.sql deleted file mode 100644 index 788fd63c42..0000000000 --- a/OpenSim/Data/MySQL/Resources/014_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -alter table estate_settings add column AbuseEmail varchar(255) not null; - -alter table estate_settings add column EstateOwner varchar(36) not null; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/015_RegionStore.sql b/OpenSim/Data/MySQL/Resources/015_RegionStore.sql deleted file mode 100644 index 6d4f9f332f..0000000000 --- a/OpenSim/Data/MySQL/Resources/015_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -alter table estate_settings add column DenyMinors tinyint not null; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/016_RegionStore.sql b/OpenSim/Data/MySQL/Resources/016_RegionStore.sql deleted file mode 100644 index 9bc265e5ff..0000000000 --- a/OpenSim/Data/MySQL/Resources/016_RegionStore.sql +++ /dev/null @@ -1,27 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN PayPrice integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton1 integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton2 integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton3 integer not null default 0; -ALTER TABLE prims ADD COLUMN PayButton4 integer not null default 0; -ALTER TABLE prims ADD COLUMN LoopedSound char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN LoopedSoundGain float not null default 0.0; -ALTER TABLE prims ADD COLUMN TextureAnimation blob; -ALTER TABLE prims ADD COLUMN OmegaX float not null default 0.0; -ALTER TABLE prims ADD COLUMN OmegaY float not null default 0.0; -ALTER TABLE prims ADD COLUMN OmegaZ float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetX float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetY float not null default 0.0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float not null default 0.0; -ALTER TABLE prims ADD COLUMN ForceMouselook tinyint not null default 0; -ALTER TABLE prims ADD COLUMN ScriptAccessPin integer not null default 0; -ALTER TABLE prims ADD COLUMN AllowedDrop tinyint not null default 0; -ALTER TABLE prims ADD COLUMN DieAtEdge tinyint not null default 0; -ALTER TABLE prims ADD COLUMN SalePrice integer not null default 10; -ALTER TABLE prims ADD COLUMN SaleType tinyint not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/017_RegionStore.sql b/OpenSim/Data/MySQL/Resources/017_RegionStore.sql deleted file mode 100644 index 0304f30b72..0000000000 --- a/OpenSim/Data/MySQL/Resources/017_RegionStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; -ALTER TABLE prims ADD COLUMN ParticleSystem blob; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/018_RegionStore.sql b/OpenSim/Data/MySQL/Resources/018_RegionStore.sql deleted file mode 100644 index 68c489c7bf..0000000000 --- a/OpenSim/Data/MySQL/Resources/018_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -ALTER TABLE prims ADD COLUMN ClickAction tinyint NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/019_RegionStore.sql b/OpenSim/Data/MySQL/Resources/019_RegionStore.sql deleted file mode 100644 index 4c14f2a5f4..0000000000 --- a/OpenSim/Data/MySQL/Resources/019_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -ALTER TABLE prims ADD COLUMN Material tinyint NOT NULL default 3; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/020_RegionStore.sql b/OpenSim/Data/MySQL/Resources/020_RegionStore.sql deleted file mode 100644 index 814ef48bb6..0000000000 --- a/OpenSim/Data/MySQL/Resources/020_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -begin; - -ALTER TABLE land ADD COLUMN OtherCleanTime integer NOT NULL default 0; -ALTER TABLE land ADD COLUMN Dwell integer NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/021_RegionStore.sql b/OpenSim/Data/MySQL/Resources/021_RegionStore.sql deleted file mode 100644 index c59b27e745..0000000000 --- a/OpenSim/Data/MySQL/Resources/021_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/MySQL/Resources/022_RegionStore.sql b/OpenSim/Data/MySQL/Resources/022_RegionStore.sql deleted file mode 100644 index df0bb7dac9..0000000000 --- a/OpenSim/Data/MySQL/Resources/022_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN CollisionSoundVolume float not null default 0.0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/023_RegionStore.sql b/OpenSim/Data/MySQL/Resources/023_RegionStore.sql deleted file mode 100644 index 559591fba5..0000000000 --- a/OpenSim/Data/MySQL/Resources/023_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN LinkNumber integer not null default 0; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/024_RegionStore.sql b/OpenSim/Data/MySQL/Resources/024_RegionStore.sql deleted file mode 100644 index defbf5cbb1..0000000000 --- a/OpenSim/Data/MySQL/Resources/024_RegionStore.sql +++ /dev/null @@ -1,18 +0,0 @@ -BEGIN; - -alter table regionsettings change column `object_bonus` `object_bonus` double NOT NULL; -alter table regionsettings change column `elevation_1_nw` `elevation_1_nw` double NOT NULL; -alter table regionsettings change column `elevation_2_nw` `elevation_2_nw` double NOT NULL; -alter table regionsettings change column `elevation_1_ne` `elevation_1_ne` double NOT NULL; -alter table regionsettings change column `elevation_2_ne` `elevation_2_ne` double NOT NULL; -alter table regionsettings change column `elevation_1_se` `elevation_1_se` double NOT NULL; -alter table regionsettings change column `elevation_2_se` `elevation_2_se` double NOT NULL; -alter table regionsettings change column `elevation_1_sw` `elevation_1_sw` double NOT NULL; -alter table regionsettings change column `elevation_2_sw` `elevation_2_sw` double NOT NULL; -alter table regionsettings change column `water_height` `water_height` double NOT NULL; -alter table regionsettings change column `terrain_raise_limit` `terrain_raise_limit` double NOT NULL; -alter table regionsettings change column `terrain_lower_limit` `terrain_lower_limit` double NOT NULL; -alter table regionsettings change column `sun_position` `sun_position` double NOT NULL; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/025_RegionStore.sql b/OpenSim/Data/MySQL/Resources/025_RegionStore.sql deleted file mode 100644 index e8f5d70e47..0000000000 --- a/OpenSim/Data/MySQL/Resources/025_RegionStore.sql +++ /dev/null @@ -1,46 +0,0 @@ -BEGIN; - -alter table prims change column `PositionX` `PositionX` double default NULL; -alter table prims change column `PositionY` `PositionY` double default NULL; -alter table prims change column `PositionZ` `PositionZ` double default NULL; -alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; -alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; -alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; -alter table prims change column `VelocityX` `VelocityX` double default NULL; -alter table prims change column `VelocityY` `VelocityY` double default NULL; -alter table prims change column `VelocityZ` `VelocityZ` double default NULL; -alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; -alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; -alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; -alter table prims change column `AccelerationX` `AccelerationX` double default NULL; -alter table prims change column `AccelerationY` `AccelerationY` double default NULL; -alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; -alter table prims change column `RotationX` `RotationX` double default NULL; -alter table prims change column `RotationY` `RotationY` double default NULL; -alter table prims change column `RotationZ` `RotationZ` double default NULL; -alter table prims change column `RotationW` `RotationW` double default NULL; -alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; -alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; -alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; -alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; -alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; -alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; -alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; -alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; -alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; -alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; -alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; -alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; - -alter table primshapes change column `ScaleX` `ScaleX` double NOT NULL default '0'; -alter table primshapes change column `ScaleY` `ScaleY` double NOT NULL default '0'; -alter table primshapes change column `ScaleZ` `ScaleZ` double NOT NULL default '0'; - -COMMIT; - diff --git a/OpenSim/Data/MySQL/Resources/026_RegionStore.sql b/OpenSim/Data/MySQL/Resources/026_RegionStore.sql deleted file mode 100644 index 91af8a84c8..0000000000 --- a/OpenSim/Data/MySQL/Resources/026_RegionStore.sql +++ /dev/null @@ -1,41 +0,0 @@ -begin; - -alter table prims change column `PositionX` `PositionX` double default NULL; -alter table prims change column `PositionY` `PositionY` double default NULL; -alter table prims change column `PositionZ` `PositionZ` double default NULL; -alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; -alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; -alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; -alter table prims change column `VelocityX` `VelocityX` double default NULL; -alter table prims change column `VelocityY` `VelocityY` double default NULL; -alter table prims change column `VelocityZ` `VelocityZ` double default NULL; -alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; -alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; -alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; -alter table prims change column `AccelerationX` `AccelerationX` double default NULL; -alter table prims change column `AccelerationY` `AccelerationY` double default NULL; -alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; -alter table prims change column `RotationX` `RotationX` double default NULL; -alter table prims change column `RotationY` `RotationY` double default NULL; -alter table prims change column `RotationZ` `RotationZ` double default NULL; -alter table prims change column `RotationW` `RotationW` double default NULL; -alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; -alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; -alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; -alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; -alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; -alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; -alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; -alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; -alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; -alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; -alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; -alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; -alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; - -commit; diff --git a/OpenSim/Data/MySQL/Resources/027_RegionStore.sql b/OpenSim/Data/MySQL/Resources/027_RegionStore.sql deleted file mode 100644 index e1efab31c1..0000000000 --- a/OpenSim/Data/MySQL/Resources/027_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims DROP COLUMN ParentID; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/028_RegionStore.sql b/OpenSim/Data/MySQL/Resources/028_RegionStore.sql deleted file mode 100644 index 078394f80d..0000000000 --- a/OpenSim/Data/MySQL/Resources/028_RegionStore.sql +++ /dev/null @@ -1,79 +0,0 @@ -BEGIN; - -update terrain - set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) - where RegionUUID not like '%-%'; - - -update landaccesslist - set LandUUID = concat(substr(LandUUID, 1, 8), "-", substr(LandUUID, 9, 4), "-", substr(LandUUID, 13, 4), "-", substr(LandUUID, 17, 4), "-", substr(LandUUID, 21, 12)) - where LandUUID not like '%-%'; - -update landaccesslist - set AccessUUID = concat(substr(AccessUUID, 1, 8), "-", substr(AccessUUID, 9, 4), "-", substr(AccessUUID, 13, 4), "-", substr(AccessUUID, 17, 4), "-", substr(AccessUUID, 21, 12)) - where AccessUUID not like '%-%'; - - -update prims - set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) - where UUID not like '%-%'; - -update prims - set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) - where RegionUUID not like '%-%'; - -update prims - set SceneGroupID = concat(substr(SceneGroupID, 1, 8), "-", substr(SceneGroupID, 9, 4), "-", substr(SceneGroupID, 13, 4), "-", substr(SceneGroupID, 17, 4), "-", substr(SceneGroupID, 21, 12)) - where SceneGroupID not like '%-%'; - -update prims - set CreatorID = concat(substr(CreatorID, 1, 8), "-", substr(CreatorID, 9, 4), "-", substr(CreatorID, 13, 4), "-", substr(CreatorID, 17, 4), "-", substr(CreatorID, 21, 12)) - where CreatorID not like '%-%'; - -update prims - set OwnerID = concat(substr(OwnerID, 1, 8), "-", substr(OwnerID, 9, 4), "-", substr(OwnerID, 13, 4), "-", substr(OwnerID, 17, 4), "-", substr(OwnerID, 21, 12)) - where OwnerID not like '%-%'; - -update prims - set GroupID = concat(substr(GroupID, 1, 8), "-", substr(GroupID, 9, 4), "-", substr(GroupID, 13, 4), "-", substr(GroupID, 17, 4), "-", substr(GroupID, 21, 12)) - where GroupID not like '%-%'; - -update prims - set LastOwnerID = concat(substr(LastOwnerID, 1, 8), "-", substr(LastOwnerID, 9, 4), "-", substr(LastOwnerID, 13, 4), "-", substr(LastOwnerID, 17, 4), "-", substr(LastOwnerID, 21, 12)) - where LastOwnerID not like '%-%'; - - -update primshapes - set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) - where UUID not like '%-%'; - - -update land - set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) - where UUID not like '%-%'; - -update land - set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) - where RegionUUID not like '%-%'; - -update land - set OwnerUUID = concat(substr(OwnerUUID, 1, 8), "-", substr(OwnerUUID, 9, 4), "-", substr(OwnerUUID, 13, 4), "-", substr(OwnerUUID, 17, 4), "-", substr(OwnerUUID, 21, 12)) - where OwnerUUID not like '%-%'; - -update land - set GroupUUID = concat(substr(GroupUUID, 1, 8), "-", substr(GroupUUID, 9, 4), "-", substr(GroupUUID, 13, 4), "-", substr(GroupUUID, 17, 4), "-", substr(GroupUUID, 21, 12)) - where GroupUUID not like '%-%'; - -update land - set MediaTextureUUID = concat(substr(MediaTextureUUID, 1, 8), "-", substr(MediaTextureUUID, 9, 4), "-", substr(MediaTextureUUID, 13, 4), "-", substr(MediaTextureUUID, 17, 4), "-", substr(MediaTextureUUID, 21, 12)) - where MediaTextureUUID not like '%-%'; - -update land - set SnapshotUUID = concat(substr(SnapshotUUID, 1, 8), "-", substr(SnapshotUUID, 9, 4), "-", substr(SnapshotUUID, 13, 4), "-", substr(SnapshotUUID, 17, 4), "-", substr(SnapshotUUID, 21, 12)) - where SnapshotUUID not like '%-%'; - -update land - set AuthbuyerID = concat(substr(AuthbuyerID, 1, 8), "-", substr(AuthbuyerID, 9, 4), "-", substr(AuthbuyerID, 13, 4), "-", substr(AuthbuyerID, 17, 4), "-", substr(AuthbuyerID, 21, 12)) - where AuthbuyerID not like '%-%'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/029_RegionStore.sql b/OpenSim/Data/MySQL/Resources/029_RegionStore.sql deleted file mode 100644 index b5962a2e10..0000000000 --- a/OpenSim/Data/MySQL/Resources/029_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN PassTouches tinyint not null default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/030_RegionStore.sql b/OpenSim/Data/MySQL/Resources/030_RegionStore.sql deleted file mode 100644 index dfdcf6d710..0000000000 --- a/OpenSim/Data/MySQL/Resources/030_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE regionsettings ADD COLUMN loaded_creation_date varchar(20) default NULL; -ALTER TABLE regionsettings ADD COLUMN loaded_creation_time varchar(20) default NULL; -ALTER TABLE regionsettings ADD COLUMN loaded_creation_id varchar(64) default NULL; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/031_RegionStore.sql b/OpenSim/Data/MySQL/Resources/031_RegionStore.sql deleted file mode 100644 index d069296039..0000000000 --- a/OpenSim/Data/MySQL/Resources/031_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE regionsettings DROP COLUMN loaded_creation_date; -ALTER TABLE regionsettings DROP COLUMN loaded_creation_time; -ALTER TABLE regionsettings ADD COLUMN loaded_creation_datetime int unsigned NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/032_RegionStore.sql b/OpenSim/Data/MySQL/Resources/032_RegionStore.sql deleted file mode 100644 index dca5de7144..0000000000 --- a/OpenSim/Data/MySQL/Resources/032_RegionStore.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -ALTER TABLE estate_settings AUTO_INCREMENT = 100; -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/AssetStore.migrations b/OpenSim/Data/MySQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..b9595f0f47 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/AssetStore.migrations @@ -0,0 +1,69 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE `assets` ( + `id` binary(16) NOT NULL, + `name` varchar(64) NOT NULL, + `description` varchar(64) NOT NULL, + `assetType` tinyint(4) NOT NULL, + `invType` tinyint(4) NOT NULL, + `local` tinyint(1) NOT NULL, + `temporary` tinyint(1) NOT NULL, + `data` longblob NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; + +COMMIT; + +# ----------------- +:VERSION 2 + +BEGIN; + +ALTER TABLE assets change id oldid binary(16); +ALTER TABLE assets add id varchar(36) not null default ''; +UPDATE assets set id = concat(substr(hex(oldid),1,8),"-",substr(hex(oldid),9,4),"-",substr(hex(oldid),13,4),"-",substr(hex(oldid),17,4),"-",substr(hex(oldid),21,12)); +ALTER TABLE assets drop oldid; +ALTER TABLE assets add constraint primary key(id); + +COMMIT; + +# ----------------- +:VERSION 3 + +BEGIN; + +ALTER TABLE assets change id oldid varchar(36); +ALTER TABLE assets add id char(36) not null default '00000000-0000-0000-0000-000000000000'; +UPDATE assets set id = oldid; +ALTER TABLE assets drop oldid; +ALTER TABLE assets add constraint primary key(id); + +COMMIT; + +# ----------------- +:VERSION 4 + +BEGIN; + +ALTER TABLE assets drop InvType; + +COMMIT; + +# ----------------- +:VERSION 5 + +BEGIN; + +ALTER TABLE assets add create_time integer default 0; +ALTER TABLE assets add access_time integer default 0; + +COMMIT; + +# ----------------- +:VERSION 6 + +DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621' + diff --git a/OpenSim/Data/MySQL/Resources/001_AuthStore.sql b/OpenSim/Data/MySQL/Resources/AuthStore.migrations similarity index 51% rename from OpenSim/Data/MySQL/Resources/001_AuthStore.sql rename to OpenSim/Data/MySQL/Resources/AuthStore.migrations index c7e16fbdfb..023c786aa5 100644 --- a/OpenSim/Data/MySQL/Resources/001_AuthStore.sql +++ b/OpenSim/Data/MySQL/Resources/AuthStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 # ------------------------------- + begin; CREATE TABLE `auth` ( @@ -19,3 +21,19 @@ CREATE TABLE `tokens` ( ) ENGINE=InnoDB; commit; + +:VERSION 2 # ------------------------------- + +BEGIN; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; + +:VERSION 3 # ------------------------------- + +BEGIN; + +ALTER TABLE `auth` ADD COLUMN `accountType` VARCHAR(32) NOT NULL DEFAULT 'UserAccount'; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/Avatar.migrations b/OpenSim/Data/MySQL/Resources/Avatar.migrations new file mode 100644 index 0000000000..8d0eee68a1 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/Avatar.migrations @@ -0,0 +1,12 @@ +:VERSION 1 + +BEGIN; + +CREATE TABLE Avatars ( + PrincipalID CHAR(36) NOT NULL, + Name VARCHAR(32) NOT NULL, + Value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY(PrincipalID, Name), + KEY(PrincipalID)); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations new file mode 100644 index 0000000000..ce713bdb89 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations @@ -0,0 +1,25 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `Friends` ( + `PrincipalID` CHAR(36) NOT NULL, + `Friend` VARCHAR(255) NOT NULL, + `Flags` VARCHAR(16) NOT NULL DEFAULT 0, + `Offered` VARCHAR(32) NOT NULL DEFAULT 0, + PRIMARY KEY(`PrincipalID`, `Friend`), + KEY(`PrincipalID`) +); + +COMMIT; + +:VERSION 2 # ------------------------- + +BEGIN; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; + + + diff --git a/OpenSim/Data/MySQL/Resources/001_GridStore.sql b/OpenSim/Data/MySQL/Resources/GridStore.migrations similarity index 64% rename from OpenSim/Data/MySQL/Resources/001_GridStore.sql rename to OpenSim/Data/MySQL/Resources/GridStore.migrations index cb0f9bd2cd..523a8ac7dc 100644 --- a/OpenSim/Data/MySQL/Resources/001_GridStore.sql +++ b/OpenSim/Data/MySQL/Resources/GridStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + CREATE TABLE `regions` ( `uuid` varchar(36) NOT NULL, `regionHandle` bigint(20) unsigned NOT NULL, @@ -30,3 +32,58 @@ CREATE TABLE `regions` ( KEY `regionHandle` (`regionHandle`), KEY `overrideHandles` (`eastOverrideHandle`,`westOverrideHandle`,`southOverrideHandle`,`northOverrideHandle`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Rev. 3'; + +:VERSION 2 + +BEGIN; + +ALTER TABLE regions add column access integer unsigned default 1; + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE regions add column ScopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; + +create index ScopeID on regions(ScopeID); + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE regions add column sizeX integer not null default 0; +ALTER TABLE regions add column sizeY integer not null default 0; + +COMMIT; + +:VERSION 5 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `flags` integer NOT NULL DEFAULT 0; +CREATE INDEX flags ON regions(flags); + +COMMIT; + +:VERSION 6 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `last_seen` integer NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 7 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `PrincipalID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE `regions` ADD COLUMN `Token` varchar(255) NOT NULL; + +COMMIT; + + diff --git a/OpenSim/Data/MySQL/Resources/InventoryStore.migrations b/OpenSim/Data/MySQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..8c5864e97d --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/InventoryStore.migrations @@ -0,0 +1,93 @@ +:VERSION 1 # ------------ +BEGIN; + +CREATE TABLE `inventoryfolders` ( + `folderID` varchar(36) NOT NULL default '', + `agentID` varchar(36) default NULL, + `parentFolderID` varchar(36) default NULL, + `folderName` varchar(64) default NULL, + `type` smallint NOT NULL default 0, + `version` int NOT NULL default 0, + PRIMARY KEY (`folderID`), + KEY `owner` (`agentID`), + KEY `parent` (`parentFolderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `inventoryitems` ( + `inventoryID` varchar(36) NOT NULL default '', + `assetID` varchar(36) default NULL, + `assetType` int(11) default NULL, + `parentFolderID` varchar(36) default NULL, + `avatarID` varchar(36) default NULL, + `inventoryName` varchar(64) default NULL, + `inventoryDescription` varchar(128) default NULL, + `inventoryNextPermissions` int(10) unsigned default NULL, + `inventoryCurrentPermissions` int(10) unsigned default NULL, + `invType` int(11) default NULL, + `creatorID` varchar(36) default NULL, + `inventoryBasePermissions` int(10) unsigned NOT NULL default 0, + `inventoryEveryOnePermissions` int(10) unsigned NOT NULL default 0, + `salePrice` int(11) NOT NULL default 0, + `saleType` tinyint(4) NOT NULL default 0, + `creationDate` int(11) NOT NULL default 0, + `groupID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `groupOwned` tinyint(4) NOT NULL default 0, + `flags` int(11) unsigned NOT NULL default 0, + PRIMARY KEY (`inventoryID`), + KEY `owner` (`avatarID`), + KEY `folder` (`parentFolderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 2 # ------------ + +BEGIN; + +ALTER TABLE inventoryfolders change folderID folderIDold varchar(36); +ALTER TABLE inventoryfolders change agentID agentIDold varchar(36); +ALTER TABLE inventoryfolders change parentFolderID parentFolderIDold varchar(36); +ALTER TABLE inventoryfolders add folderID char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE inventoryfolders add agentID char(36) default NULL; +ALTER TABLE inventoryfolders add parentFolderID char(36) default NULL; +UPDATE inventoryfolders set folderID = folderIDold, agentID = agentIDold, parentFolderID = parentFolderIDold; +ALTER TABLE inventoryfolders drop folderIDold; +ALTER TABLE inventoryfolders drop agentIDold; +ALTER TABLE inventoryfolders drop parentFolderIDold; +ALTER TABLE inventoryfolders add constraint primary key(folderID); +ALTER TABLE inventoryfolders add index inventoryfolders_agentid(agentID); +ALTER TABLE inventoryfolders add index inventoryfolders_parentFolderid(parentFolderID); + +ALTER TABLE inventoryitems change inventoryID inventoryIDold varchar(36); +ALTER TABLE inventoryitems change avatarID avatarIDold varchar(36); +ALTER TABLE inventoryitems change parentFolderID parentFolderIDold varchar(36); +ALTER TABLE inventoryitems add inventoryID char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE inventoryitems add avatarID char(36) default NULL; +ALTER TABLE inventoryitems add parentFolderID char(36) default NULL; +UPDATE inventoryitems set inventoryID = inventoryIDold, avatarID = avatarIDold, parentFolderID = parentFolderIDold; +ALTER TABLE inventoryitems drop inventoryIDold; +ALTER TABLE inventoryitems drop avatarIDold; +ALTER TABLE inventoryitems drop parentFolderIDold; +ALTER TABLE inventoryitems add constraint primary key(inventoryID); +ALTER TABLE inventoryitems add index inventoryitems_avatarid(avatarID); +ALTER TABLE inventoryitems add index inventoryitems_parentFolderid(parentFolderID); + +COMMIT; + +:VERSION 3 # ------------ + +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; + +:VERSION 4 # ------------ + +BEGIN; + +update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID is NULL; +update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID = ''; +alter table inventoryitems modify column creatorID varchar(36) not NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_LogStore.sql b/OpenSim/Data/MySQL/Resources/LogStore.migrations similarity index 95% rename from OpenSim/Data/MySQL/Resources/001_LogStore.sql rename to OpenSim/Data/MySQL/Resources/LogStore.migrations index b4c29fbef5..9ac26ac722 100644 --- a/OpenSim/Data/MySQL/Resources/001_LogStore.sql +++ b/OpenSim/Data/MySQL/Resources/LogStore.migrations @@ -1,3 +1,6 @@ + +:VERSION 1 + CREATE TABLE `logs` ( `logID` int(10) unsigned NOT NULL auto_increment, `target` varchar(36) default NULL, diff --git a/OpenSim/Data/MySQL/Resources/Presence.migrations b/OpenSim/Data/MySQL/Resources/Presence.migrations new file mode 100644 index 0000000000..d513024987 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/Presence.migrations @@ -0,0 +1,36 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `Presence` ( + `UserID` VARCHAR(255) NOT NULL, + `RegionID` CHAR(36) NOT NULL, + `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `Online` CHAR(5) NOT NULL DEFAULT 'false', + `Login` CHAR(16) NOT NULL DEFAULT '0', + `Logout` CHAR(16) NOT NULL DEFAULT '0', + `Position` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `LookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>' +) ENGINE=InnoDB; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +ALTER TABLE Presence ADD COLUMN `HomeRegionID` CHAR(36) NOT NULL; +ALTER TABLE Presence ADD COLUMN `HomePosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; +ALTER TABLE Presence ADD COLUMN `HomeLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; + +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +CREATE UNIQUE INDEX SessionID ON Presence(SessionID); +CREATE INDEX UserID ON Presence(UserID); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..3dab67e58e --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -0,0 +1,806 @@ + +:VERSION 1 #--------------------- + +BEGIN; + +CREATE TABLE `prims` ( + `UUID` varchar(255) NOT NULL, + `RegionUUID` varchar(255) default NULL, + `ParentID` int(11) default NULL, + `CreationDate` int(11) default NULL, + `Name` varchar(255) default NULL, + `SceneGroupID` varchar(255) default NULL, + `Text` varchar(255) default NULL, + `Description` varchar(255) default NULL, + `SitName` varchar(255) default NULL, + `TouchName` varchar(255) default NULL, + `ObjectFlags` int(11) default NULL, + `CreatorID` varchar(255) default NULL, + `OwnerID` varchar(255) default NULL, + `GroupID` varchar(255) default NULL, + `LastOwnerID` varchar(255) default NULL, + `OwnerMask` int(11) default NULL, + `NextOwnerMask` int(11) default NULL, + `GroupMask` int(11) default NULL, + `EveryoneMask` int(11) default NULL, + `BaseMask` int(11) default NULL, + `PositionX` float default NULL, + `PositionY` float default NULL, + `PositionZ` float default NULL, + `GroupPositionX` float default NULL, + `GroupPositionY` float default NULL, + `GroupPositionZ` float default NULL, + `VelocityX` float default NULL, + `VelocityY` float default NULL, + `VelocityZ` float default NULL, + `AngularVelocityX` float default NULL, + `AngularVelocityY` float default NULL, + `AngularVelocityZ` float default NULL, + `AccelerationX` float default NULL, + `AccelerationY` float default NULL, + `AccelerationZ` float default NULL, + `RotationX` float default NULL, + `RotationY` float default NULL, + `RotationZ` float default NULL, + `RotationW` float default NULL, + `SitTargetOffsetX` float default NULL, + `SitTargetOffsetY` float default NULL, + `SitTargetOffsetZ` float default NULL, + `SitTargetOrientW` float default NULL, + `SitTargetOrientX` float default NULL, + `SitTargetOrientY` float default NULL, + `SitTargetOrientZ` float default NULL, + PRIMARY KEY (`UUID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `primshapes` ( + `UUID` varchar(255) NOT NULL, + `Shape` int(11) default NULL, + `ScaleX` float default NULL, + `ScaleY` float default NULL, + `ScaleZ` float default NULL, + `PCode` int(11) default NULL, + `PathBegin` int(11) default NULL, + `PathEnd` int(11) default NULL, + `PathScaleX` int(11) default NULL, + `PathScaleY` int(11) default NULL, + `PathShearX` int(11) default NULL, + `PathShearY` int(11) default NULL, + `PathSkew` int(11) default NULL, + `PathCurve` int(11) default NULL, + `PathRadiusOffset` int(11) default NULL, + `PathRevolutions` int(11) default NULL, + `PathTaperX` int(11) default NULL, + `PathTaperY` int(11) default NULL, + `PathTwist` int(11) default NULL, + `PathTwistBegin` int(11) default NULL, + `ProfileBegin` int(11) default NULL, + `ProfileEnd` int(11) default NULL, + `ProfileCurve` int(11) default NULL, + `ProfileHollow` int(11) default NULL, + `State` int(11) default NULL, + `Texture` longblob, + `ExtraParams` longblob, + PRIMARY KEY (`UUID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `primitems` ( + `itemID` varchar(255) NOT NULL, + `primID` varchar(255) default NULL, + `assetID` varchar(255) default NULL, + `parentFolderID` varchar(255) default NULL, + `invType` int(11) default NULL, + `assetType` int(11) default NULL, + `name` varchar(255) default NULL, + `description` varchar(255) default NULL, + `creationDate` bigint(20) default NULL, + `creatorID` varchar(255) default NULL, + `ownerID` varchar(255) default NULL, + `lastOwnerID` varchar(255) default NULL, + `groupID` varchar(255) default NULL, + `nextPermissions` int(11) default NULL, + `currentPermissions` int(11) default NULL, + `basePermissions` int(11) default NULL, + `everyonePermissions` int(11) default NULL, + `groupPermissions` int(11) default NULL, + PRIMARY KEY (`itemID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `terrain` ( + `RegionUUID` varchar(255) default NULL, + `Revision` int(11) default NULL, + `Heightfield` longblob +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `land` ( + `UUID` varchar(255) NOT NULL, + `RegionUUID` varchar(255) default NULL, + `LocalLandID` int(11) default NULL, + `Bitmap` longblob, + `Name` varchar(255) default NULL, + `Description` varchar(255) default NULL, + `OwnerUUID` varchar(255) default NULL, + `IsGroupOwned` int(11) default NULL, + `Area` int(11) default NULL, + `AuctionID` int(11) default NULL, + `Category` int(11) default NULL, + `ClaimDate` int(11) default NULL, + `ClaimPrice` int(11) default NULL, + `GroupUUID` varchar(255) default NULL, + `SalePrice` int(11) default NULL, + `LandStatus` int(11) default NULL, + `LandFlags` int(11) default NULL, + `LandingType` int(11) default NULL, + `MediaAutoScale` int(11) default NULL, + `MediaTextureUUID` varchar(255) default NULL, + `MediaURL` varchar(255) default NULL, + `MusicURL` varchar(255) default NULL, + `PassHours` float default NULL, + `PassPrice` int(11) default NULL, + `SnapshotUUID` varchar(255) default NULL, + `UserLocationX` float default NULL, + `UserLocationY` float default NULL, + `UserLocationZ` float default NULL, + `UserLookAtX` float default NULL, + `UserLookAtY` float default NULL, + `UserLookAtZ` float default NULL, + `AuthbuyerID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + PRIMARY KEY (`UUID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `landaccesslist` ( + `LandUUID` varchar(255) default NULL, + `AccessUUID` varchar(255) default NULL, + `Flags` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +COMMIT; + +:VERSION 2 #--------------------- + +BEGIN; + +CREATE index prims_regionuuid on prims(RegionUUID); +CREATE index primitems_primid on primitems(primID); + +COMMIT; + +:VERSION 3 #--------------------- + +BEGIN; + CREATE TABLE regionban (regionUUID VARCHAR(36) NOT NULL, bannedUUID VARCHAR(36) NOT NULL, bannedIp VARCHAR(16) NOT NULL, bannedIpHostMask VARCHAR(16) NOT NULL) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; +COMMIT; + +:VERSION 4 #--------------------- + +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; + +:VERSION 5 #--------------------- +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36), + primary key(regionUUID) +); + +COMMIT; + + +:VERSION 6 #--------------------- + +BEGIN; + +alter table landaccesslist ENGINE = InnoDB; +alter table migrations ENGINE = InnoDB; +alter table primitems ENGINE = InnoDB; +alter table prims ENGINE = InnoDB; +alter table primshapes ENGINE = InnoDB; +alter table regionsettings ENGINE = InnoDB; +alter table terrain ENGINE = InnoDB; + +COMMIT; + +:VERSION 7 #--------------------- + +BEGIN; + +ALTER TABLE prims change UUID UUIDold varchar(255); +ALTER TABLE prims change RegionUUID RegionUUIDold varchar(255); +ALTER TABLE prims change CreatorID CreatorIDold varchar(255); +ALTER TABLE prims change OwnerID OwnerIDold varchar(255); +ALTER TABLE prims change GroupID GroupIDold varchar(255); +ALTER TABLE prims change LastOwnerID LastOwnerIDold varchar(255); +ALTER TABLE prims add UUID char(36); +ALTER TABLE prims add RegionUUID char(36); +ALTER TABLE prims add CreatorID char(36); +ALTER TABLE prims add OwnerID char(36); +ALTER TABLE prims add GroupID char(36); +ALTER TABLE prims add LastOwnerID char(36); +UPDATE prims set UUID = UUIDold, RegionUUID = RegionUUIDold, CreatorID = CreatorIDold, OwnerID = OwnerIDold, GroupID = GroupIDold, LastOwnerID = LastOwnerIDold; +ALTER TABLE prims drop UUIDold; +ALTER TABLE prims drop RegionUUIDold; +ALTER TABLE prims drop CreatorIDold; +ALTER TABLE prims drop OwnerIDold; +ALTER TABLE prims drop GroupIDold; +ALTER TABLE prims drop LastOwnerIDold; +ALTER TABLE prims add constraint primary key(UUID); +ALTER TABLE prims add index prims_regionuuid(RegionUUID); + +COMMIT; + +:VERSION 8 #--------------------- + +BEGIN; + +ALTER TABLE primshapes change UUID UUIDold varchar(255); +ALTER TABLE primshapes add UUID char(36); +UPDATE primshapes set UUID = UUIDold; +ALTER TABLE primshapes drop UUIDold; +ALTER TABLE primshapes add constraint primary key(UUID); + +COMMIT; + +:VERSION 9 #--------------------- + +BEGIN; + +ALTER TABLE primitems change itemID itemIDold varchar(255); +ALTER TABLE primitems change primID primIDold varchar(255); +ALTER TABLE primitems change assetID assetIDold varchar(255); +ALTER TABLE primitems change parentFolderID parentFolderIDold varchar(255); +ALTER TABLE primitems change creatorID creatorIDold varchar(255); +ALTER TABLE primitems change ownerID ownerIDold varchar(255); +ALTER TABLE primitems change groupID groupIDold varchar(255); +ALTER TABLE primitems change lastOwnerID lastOwnerIDold varchar(255); +ALTER TABLE primitems add itemID char(36); +ALTER TABLE primitems add primID char(36); +ALTER TABLE primitems add assetID char(36); +ALTER TABLE primitems add parentFolderID char(36); +ALTER TABLE primitems add creatorID char(36); +ALTER TABLE primitems add ownerID char(36); +ALTER TABLE primitems add groupID char(36); +ALTER TABLE primitems add lastOwnerID char(36); +UPDATE primitems set itemID = itemIDold, primID = primIDold, assetID = assetIDold, parentFolderID = parentFolderIDold, creatorID = creatorIDold, ownerID = ownerIDold, groupID = groupIDold, lastOwnerID = lastOwnerIDold; +ALTER TABLE primitems drop itemIDold; +ALTER TABLE primitems drop primIDold; +ALTER TABLE primitems drop assetIDold; +ALTER TABLE primitems drop parentFolderIDold; +ALTER TABLE primitems drop creatorIDold; +ALTER TABLE primitems drop ownerIDold; +ALTER TABLE primitems drop groupIDold; +ALTER TABLE primitems drop lastOwnerIDold; +ALTER TABLE primitems add constraint primary key(itemID); +ALTER TABLE primitems add index primitems_primid(primID); + +COMMIT; + +:VERSION 10 #--------------------- + +# 1 "010_RegionStore.sql" +# 1 "" +# 1 "" +# 1 "010_RegionStore.sql" +BEGIN; + +DELETE FROM regionsettings; + +COMMIT; + + +:VERSION 11 #--------------------- + +BEGIN; + +ALTER TABLE prims change SceneGroupID SceneGroupIDold varchar(255); +ALTER TABLE prims add SceneGroupID char(36); +UPDATE prims set SceneGroupID = SceneGroupIDold; +ALTER TABLE prims drop SceneGroupIDold; +ALTER TABLE prims add index prims_scenegroupid(SceneGroupID); + +COMMIT; + +:VERSION 12 #--------------------- + +BEGIN; + +ALTER TABLE prims add index prims_parentid(ParentID); + +COMMIT; + +:VERSION 13 #--------------------- +begin; + +drop table regionsettings; + +CREATE TABLE `regionsettings` ( + `regionUUID` char(36) NOT NULL, + `block_terraform` int(11) NOT NULL, + `block_fly` int(11) NOT NULL, + `allow_damage` int(11) NOT NULL, + `restrict_pushing` int(11) NOT NULL, + `allow_land_resell` int(11) NOT NULL, + `allow_land_join_divide` int(11) NOT NULL, + `block_show_in_search` int(11) NOT NULL, + `agent_limit` int(11) NOT NULL, + `object_bonus` float NOT NULL, + `maturity` int(11) NOT NULL, + `disable_scripts` int(11) NOT NULL, + `disable_collisions` int(11) NOT NULL, + `disable_physics` int(11) NOT NULL, + `terrain_texture_1` char(36) NOT NULL, + `terrain_texture_2` char(36) NOT NULL, + `terrain_texture_3` char(36) NOT NULL, + `terrain_texture_4` char(36) NOT NULL, + `elevation_1_nw` float NOT NULL, + `elevation_2_nw` float NOT NULL, + `elevation_1_ne` float NOT NULL, + `elevation_2_ne` float NOT NULL, + `elevation_1_se` float NOT NULL, + `elevation_2_se` float NOT NULL, + `elevation_1_sw` float NOT NULL, + `elevation_2_sw` float NOT NULL, + `water_height` float NOT NULL, + `terrain_raise_limit` float NOT NULL, + `terrain_lower_limit` float NOT NULL, + `use_estate_sun` int(11) NOT NULL, + `fixed_sun` int(11) NOT NULL, + `sun_position` float NOT NULL, + `covenant` char(36) default NULL, + `Sandbox` tinyint(4) NOT NULL, + PRIMARY KEY (`regionUUID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_managers` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_groups` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_users` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estateban` ( + `EstateID` int(10) unsigned NOT NULL, + `bannedUUID` varchar(36) NOT NULL, + `bannedIp` varchar(16) NOT NULL, + `bannedIpHostMask` varchar(16) NOT NULL, + `bannedNameMask` varchar(64) default NULL, + KEY `estateban_EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE `estate_settings` ( + `EstateID` int(10) unsigned NOT NULL auto_increment, + `EstateName` varchar(64) default NULL, + `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, + `DenyAnonymous` tinyint(4) NOT NULL, + `ResetHomeOnTeleport` tinyint(4) NOT NULL, + `FixedSun` tinyint(4) NOT NULL, + `DenyTransacted` tinyint(4) NOT NULL, + `BlockDwell` tinyint(4) NOT NULL, + `DenyIdentified` tinyint(4) NOT NULL, + `AllowVoice` tinyint(4) NOT NULL, + `UseGlobalTime` tinyint(4) NOT NULL, + `PricePerMeter` int(11) NOT NULL, + `TaxFree` tinyint(4) NOT NULL, + `AllowDirectTeleport` tinyint(4) NOT NULL, + `RedirectGridX` int(11) NOT NULL, + `RedirectGridY` int(11) NOT NULL, + `ParentEstateID` int(10) unsigned NOT NULL, + `SunPosition` double NOT NULL, + `EstateSkipScripts` tinyint(4) NOT NULL, + `BillableFactor` float NOT NULL, + `PublicAccess` tinyint(4) NOT NULL, + PRIMARY KEY (`EstateID`) +) ENGINE=InnoDB AUTO_INCREMENT=100; + +CREATE TABLE `estate_map` ( + `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `EstateID` int(11) NOT NULL, + PRIMARY KEY (`RegionID`), + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +commit; + +:VERSION 14 #--------------------- + +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null; + +alter table estate_settings add column EstateOwner varchar(36) not null; + +commit; + + +:VERSION 15 #--------------------- + +begin; + +alter table estate_settings add column DenyMinors tinyint not null; + +commit; + +:VERSION 16 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton1 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton2 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton3 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton4 integer not null default 0; +ALTER TABLE prims ADD COLUMN LoopedSound char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float not null default 0.0; +ALTER TABLE prims ADD COLUMN TextureAnimation blob; +ALTER TABLE prims ADD COLUMN OmegaX float not null default 0.0; +ALTER TABLE prims ADD COLUMN OmegaY float not null default 0.0; +ALTER TABLE prims ADD COLUMN OmegaZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN ForceMouselook tinyint not null default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin integer not null default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop tinyint not null default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge tinyint not null default 0; +ALTER TABLE prims ADD COLUMN SalePrice integer not null default 10; +ALTER TABLE prims ADD COLUMN SaleType tinyint not null default 0; + +COMMIT; + + +:VERSION 17 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; +ALTER TABLE prims ADD COLUMN ParticleSystem blob; + +COMMIT; + + +:VERSION 18 #--------------------- + +begin; + +ALTER TABLE prims ADD COLUMN ClickAction tinyint NOT NULL default 0; + +commit; + +:VERSION 19 #--------------------- + +begin; + +ALTER TABLE prims ADD COLUMN Material tinyint NOT NULL default 3; + +commit; + + +:VERSION 20 #--------------------- + +begin; + +ALTER TABLE land ADD COLUMN OtherCleanTime integer NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell integer NOT NULL default 0; + +commit; + +:VERSION 21 #--------------------- + +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + + +:VERSION 22 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float not null default 0.0; + +COMMIT; + +:VERSION 23 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN LinkNumber integer not null default 0; + +COMMIT; + +:VERSION 24 #--------------------- + +BEGIN; + +alter table regionsettings change column `object_bonus` `object_bonus` double NOT NULL; +alter table regionsettings change column `elevation_1_nw` `elevation_1_nw` double NOT NULL; +alter table regionsettings change column `elevation_2_nw` `elevation_2_nw` double NOT NULL; +alter table regionsettings change column `elevation_1_ne` `elevation_1_ne` double NOT NULL; +alter table regionsettings change column `elevation_2_ne` `elevation_2_ne` double NOT NULL; +alter table regionsettings change column `elevation_1_se` `elevation_1_se` double NOT NULL; +alter table regionsettings change column `elevation_2_se` `elevation_2_se` double NOT NULL; +alter table regionsettings change column `elevation_1_sw` `elevation_1_sw` double NOT NULL; +alter table regionsettings change column `elevation_2_sw` `elevation_2_sw` double NOT NULL; +alter table regionsettings change column `water_height` `water_height` double NOT NULL; +alter table regionsettings change column `terrain_raise_limit` `terrain_raise_limit` double NOT NULL; +alter table regionsettings change column `terrain_lower_limit` `terrain_lower_limit` double NOT NULL; +alter table regionsettings change column `sun_position` `sun_position` double NOT NULL; + +COMMIT; + + +:VERSION 25 #--------------------- + +BEGIN; + +alter table prims change column `PositionX` `PositionX` double default NULL; +alter table prims change column `PositionY` `PositionY` double default NULL; +alter table prims change column `PositionZ` `PositionZ` double default NULL; +alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; +alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; +alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; +alter table prims change column `VelocityX` `VelocityX` double default NULL; +alter table prims change column `VelocityY` `VelocityY` double default NULL; +alter table prims change column `VelocityZ` `VelocityZ` double default NULL; +alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; +alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; +alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; +alter table prims change column `AccelerationX` `AccelerationX` double default NULL; +alter table prims change column `AccelerationY` `AccelerationY` double default NULL; +alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; +alter table prims change column `RotationX` `RotationX` double default NULL; +alter table prims change column `RotationY` `RotationY` double default NULL; +alter table prims change column `RotationZ` `RotationZ` double default NULL; +alter table prims change column `RotationW` `RotationW` double default NULL; +alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; +alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; +alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; +alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; +alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; +alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; +alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; +alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; +alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; +alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; +alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; +alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; + +alter table primshapes change column `ScaleX` `ScaleX` double NOT NULL default '0'; +alter table primshapes change column `ScaleY` `ScaleY` double NOT NULL default '0'; +alter table primshapes change column `ScaleZ` `ScaleZ` double NOT NULL default '0'; + +COMMIT; + +:VERSION 26 #--------------------- + +begin; + +alter table prims change column `PositionX` `PositionX` double default NULL; +alter table prims change column `PositionY` `PositionY` double default NULL; +alter table prims change column `PositionZ` `PositionZ` double default NULL; +alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; +alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; +alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; +alter table prims change column `VelocityX` `VelocityX` double default NULL; +alter table prims change column `VelocityY` `VelocityY` double default NULL; +alter table prims change column `VelocityZ` `VelocityZ` double default NULL; +alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; +alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; +alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; +alter table prims change column `AccelerationX` `AccelerationX` double default NULL; +alter table prims change column `AccelerationY` `AccelerationY` double default NULL; +alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; +alter table prims change column `RotationX` `RotationX` double default NULL; +alter table prims change column `RotationY` `RotationY` double default NULL; +alter table prims change column `RotationZ` `RotationZ` double default NULL; +alter table prims change column `RotationW` `RotationW` double default NULL; +alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; +alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; +alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; +alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; +alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; +alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; +alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; +alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; +alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; +alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; +alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; +alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; + +commit; + +:VERSION 27 #--------------------- + +BEGIN; + +ALTER TABLE prims DROP COLUMN ParentID; + +COMMIT; + +:VERSION 28 #--------------------- + +BEGIN; + +update terrain + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = concat(substr(LandUUID, 1, 8), "-", substr(LandUUID, 9, 4), "-", substr(LandUUID, 13, 4), "-", substr(LandUUID, 17, 4), "-", substr(LandUUID, 21, 12)) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = concat(substr(AccessUUID, 1, 8), "-", substr(AccessUUID, 9, 4), "-", substr(AccessUUID, 13, 4), "-", substr(AccessUUID, 17, 4), "-", substr(AccessUUID, 21, 12)) + where AccessUUID not like '%-%'; + + +update prims + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + +update prims + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = concat(substr(SceneGroupID, 1, 8), "-", substr(SceneGroupID, 9, 4), "-", substr(SceneGroupID, 13, 4), "-", substr(SceneGroupID, 17, 4), "-", substr(SceneGroupID, 21, 12)) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = concat(substr(CreatorID, 1, 8), "-", substr(CreatorID, 9, 4), "-", substr(CreatorID, 13, 4), "-", substr(CreatorID, 17, 4), "-", substr(CreatorID, 21, 12)) + where CreatorID not like '%-%'; + +update prims + set OwnerID = concat(substr(OwnerID, 1, 8), "-", substr(OwnerID, 9, 4), "-", substr(OwnerID, 13, 4), "-", substr(OwnerID, 17, 4), "-", substr(OwnerID, 21, 12)) + where OwnerID not like '%-%'; + +update prims + set GroupID = concat(substr(GroupID, 1, 8), "-", substr(GroupID, 9, 4), "-", substr(GroupID, 13, 4), "-", substr(GroupID, 17, 4), "-", substr(GroupID, 21, 12)) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = concat(substr(LastOwnerID, 1, 8), "-", substr(LastOwnerID, 9, 4), "-", substr(LastOwnerID, 13, 4), "-", substr(LastOwnerID, 17, 4), "-", substr(LastOwnerID, 21, 12)) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + + +update land + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + +update land + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = concat(substr(OwnerUUID, 1, 8), "-", substr(OwnerUUID, 9, 4), "-", substr(OwnerUUID, 13, 4), "-", substr(OwnerUUID, 17, 4), "-", substr(OwnerUUID, 21, 12)) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = concat(substr(GroupUUID, 1, 8), "-", substr(GroupUUID, 9, 4), "-", substr(GroupUUID, 13, 4), "-", substr(GroupUUID, 17, 4), "-", substr(GroupUUID, 21, 12)) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = concat(substr(MediaTextureUUID, 1, 8), "-", substr(MediaTextureUUID, 9, 4), "-", substr(MediaTextureUUID, 13, 4), "-", substr(MediaTextureUUID, 17, 4), "-", substr(MediaTextureUUID, 21, 12)) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = concat(substr(SnapshotUUID, 1, 8), "-", substr(SnapshotUUID, 9, 4), "-", substr(SnapshotUUID, 13, 4), "-", substr(SnapshotUUID, 17, 4), "-", substr(SnapshotUUID, 21, 12)) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = concat(substr(AuthbuyerID, 1, 8), "-", substr(AuthbuyerID, 9, 4), "-", substr(AuthbuyerID, 13, 4), "-", substr(AuthbuyerID, 17, 4), "-", substr(AuthbuyerID, 21, 12)) + where AuthbuyerID not like '%-%'; + +COMMIT; + +:VERSION 29 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN PassTouches tinyint not null default 0; + +COMMIT; + +:VERSION 30 #--------------------- + +BEGIN; + +ALTER TABLE regionsettings ADD COLUMN loaded_creation_date varchar(20) default NULL; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_time varchar(20) default NULL; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_id varchar(64) default NULL; + +COMMIT; + +:VERSION 31 #--------------------- + +BEGIN; + +ALTER TABLE regionsettings DROP COLUMN loaded_creation_date; +ALTER TABLE regionsettings DROP COLUMN loaded_creation_time; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_datetime int unsigned NOT NULL default 0; + +COMMIT; + +:VERSION 32 #--------------------- + +BEGIN; +ALTER TABLE estate_settings AUTO_INCREMENT = 100; +COMMIT; + + + + diff --git a/OpenSim/Data/MySQL/Resources/UserAccount.migrations b/OpenSim/Data/MySQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..84011e6e49 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserAccount.migrations @@ -0,0 +1,47 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `UserAccounts` ( + `PrincipalID` CHAR(36) NOT NULL, + `ScopeID` CHAR(36) NOT NULL, + `FirstName` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(64) NOT NULL, + `Email` VARCHAR(64), + `ServiceURLs` TEXT, + `Created` INT(11) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 2 # ------------------------- + +BEGIN; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, lastname AS LastName, email as Email, CONCAT('AssetServerURI=', userAssetURI, ' InventoryServerURI=', userInventoryURI, ' GatewayURI= HomeURI=') AS ServiceURLs, created as Created FROM users; + +COMMIT; + +:VERSION 3 # ------------------------- + +BEGIN; + +CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); +CREATE INDEX Email ON UserAccounts(Email); +CREATE INDEX FirstName ON UserAccounts(FirstName); +CREATE INDEX LastName ON UserAccounts(LastName); +CREATE INDEX Name ON UserAccounts(FirstName,LastName); + +COMMIT; + +:VERSION 4 # ------------------------- + +BEGIN; + +ALTER TABLE UserAccounts ADD COLUMN UserLevel integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD COLUMN UserFlags integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD COLUMN UserTitle varchar(64) NOT NULL DEFAULT ''; + +COMMIT; + + diff --git a/OpenSim/Data/MySQL/Resources/001_UserStore.sql b/OpenSim/Data/MySQL/Resources/UserStore.migrations similarity index 72% rename from OpenSim/Data/MySQL/Resources/001_UserStore.sql rename to OpenSim/Data/MySQL/Resources/UserStore.migrations index 29ebc7d88b..f054611f04 100644 --- a/OpenSim/Data/MySQL/Resources/001_UserStore.sql +++ b/OpenSim/Data/MySQL/Resources/UserStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 # ----------------------------- + BEGIN; SET FOREIGN_KEY_CHECKS=0; @@ -104,4 +106,63 @@ CREATE TABLE `users` ( -- ---------------------------- -- Records -- ---------------------------- -COMMIT; \ No newline at end of file +COMMIT; + +:VERSION 2 # ----------------------------- + +BEGIN; + +ALTER TABLE users add homeRegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 3 # ----------------------------- + +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; + +:VERSION 4 # ----------------------------- + +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 # ----------------------------- + +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL, `attachpoint` int(11) NOT NULL, `item` char(36) NOT NULL, `asset` char(36) NOT NULL) ENGINE=InnoDB; + +COMMIT; + +:VERSION 6 # ----------------------------- + +BEGIN; + +ALTER TABLE agents add currentLookAt varchar(36) not null default ''; + +COMMIT; + +:VERSION 7 # ----------------------------- + +BEGIN; + +ALTER TABLE users add email varchar(250); + +COMMIT; + +:VERSION 8 # ----------------------------- + +BEGIN; + +ALTER TABLE users add scopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + From 6e7b3950d70b0dbe3b765d596adc534a6ca3a2a9 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 6 May 2010 22:44:57 +0300 Subject: [PATCH 147/260] Migrations for SQLite converted to new format --- .../Data/SQLite/Resources/001_AssetStore.sql | 12 - .../SQLite/Resources/001_InventoryStore.sql | 32 -- .../Data/SQLite/Resources/001_RegionStore.sql | 144 ----- .../Data/SQLite/Resources/001_UserStore.sql | 39 -- .../Data/SQLite/Resources/002_AssetStore.sql | 10 - .../Data/SQLite/Resources/002_AuthStore.sql | 5 - .../SQLite/Resources/002_FriendsStore.sql | 5 - .../SQLite/Resources/002_InventoryStore.sql | 8 - .../Data/SQLite/Resources/002_RegionStore.sql | 10 - .../Data/SQLite/Resources/002_UserAccount.sql | 5 - .../Data/SQLite/Resources/002_UserStore.sql | 5 - .../Data/SQLite/Resources/003_AssetStore.sql | 1 - .../SQLite/Resources/003_InventoryStore.sql | 5 - .../Data/SQLite/Resources/003_RegionStore.sql | 5 - .../Data/SQLite/Resources/003_UserStore.sql | 6 - .../Data/SQLite/Resources/004_AssetStore.sql | 7 - .../Data/SQLite/Resources/004_RegionStore.sql | 38 -- .../Data/SQLite/Resources/004_UserStore.sql | 6 - .../Data/SQLite/Resources/005_RegionStore.sql | 5 - .../Data/SQLite/Resources/005_UserStore.sql | 5 - .../Data/SQLite/Resources/006_RegionStore.sql | 102 ---- .../Data/SQLite/Resources/006_UserStore.sql | 20 - .../Data/SQLite/Resources/007_RegionStore.sql | 8 - .../Data/SQLite/Resources/007_UserStore.sql | 7 - .../Data/SQLite/Resources/008_RegionStore.sql | 6 - .../Data/SQLite/Resources/008_UserStore.sql | 5 - .../Data/SQLite/Resources/009_RegionStore.sql | 8 - .../Data/SQLite/Resources/009_UserStore.sql | 11 - .../Data/SQLite/Resources/010_RegionStore.sql | 5 - .../Data/SQLite/Resources/010_UserStore.sql | 37 -- .../Data/SQLite/Resources/011_RegionStore.sql | 28 - .../Data/SQLite/Resources/012_RegionStore.sql | 5 - .../Data/SQLite/Resources/013_RegionStore.sql | 6 - .../Data/SQLite/Resources/014_RegionStore.sql | 8 - .../Data/SQLite/Resources/015_RegionStore.sql | 6 - .../Data/SQLite/Resources/016_RegionStore.sql | 5 - .../Data/SQLite/Resources/017_RegionStore.sql | 8 - .../Data/SQLite/Resources/018_RegionStore.sql | 79 --- .../SQLite/Resources/AssetStore.migrations | 42 ++ ...001_AuthStore.sql => AuthStore.migrations} | 11 + .../{001_Avatar.sql => Avatar.migrations} | 2 + ...iendsStore.sql => FriendsStore.migrations} | 10 + ...oryStore.sql => InventoryStore.migrations} | 56 ++ .../SQLite/Resources/RegionStore.migrations | 526 ++++++++++++++++++ ...UserAccount.sql => UserAccount.migrations} | 14 +- .../SQLite/Resources/UserStore.migrations | 169 ++++++ 46 files changed, 828 insertions(+), 709 deletions(-) delete mode 100644 OpenSim/Data/SQLite/Resources/001_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/001_InventoryStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/001_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/001_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_AuthStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_FriendsStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_InventoryStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_UserAccount.sql delete mode 100644 OpenSim/Data/SQLite/Resources/002_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_InventoryStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/003_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/004_AssetStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/004_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/004_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/005_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/005_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/006_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/006_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/007_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/007_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/008_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/008_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/009_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/009_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/010_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/010_UserStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/011_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/012_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/013_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/014_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/015_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/016_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/017_RegionStore.sql delete mode 100644 OpenSim/Data/SQLite/Resources/018_RegionStore.sql create mode 100644 OpenSim/Data/SQLite/Resources/AssetStore.migrations rename OpenSim/Data/SQLite/Resources/{001_AuthStore.sql => AuthStore.migrations} (62%) rename OpenSim/Data/SQLite/Resources/{001_Avatar.sql => Avatar.migrations} (92%) rename OpenSim/Data/SQLite/Resources/{001_FriendsStore.sql => FriendsStore.migrations} (63%) rename OpenSim/Data/SQLite/Resources/{004_InventoryStore.sql => InventoryStore.migrations} (55%) create mode 100644 OpenSim/Data/SQLite/Resources/RegionStore.migrations rename OpenSim/Data/SQLite/Resources/{001_UserAccount.sql => UserAccount.migrations} (51%) create mode 100644 OpenSim/Data/SQLite/Resources/UserStore.migrations diff --git a/OpenSim/Data/SQLite/Resources/001_AssetStore.sql b/OpenSim/Data/SQLite/Resources/001_AssetStore.sql deleted file mode 100644 index 2e026cad19..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_AssetStore.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN TRANSACTION; -CREATE TABLE assets( - UUID varchar(255) primary key, - Name varchar(255), - Description varchar(255), - Type integer, - InvType integer, - Local integer, - Temporary integer, - Data blob); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/001_InventoryStore.sql deleted file mode 100644 index 554d5c2ec8..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_InventoryStore.sql +++ /dev/null @@ -1,32 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE inventoryfolders( - UUID varchar(255) primary key, - name varchar(255), - agentID varchar(255), - parentID varchar(255), - type integer, - version integer); - -CREATE TABLE inventoryitems( - UUID varchar(255) primary key, - assetID varchar(255), - assetType integer, - invType integer, - parentFolderID varchar(255), - avatarID varchar(255), - creatorsID varchar(255), - inventoryName varchar(255), - inventoryDescription varchar(255), - inventoryNextPermissions integer, - inventoryCurrentPermissions integer, - inventoryBasePermissions integer, - inventoryEveryOnePermissions integer, - salePrice integer default 99, - saleType integer default 0, - creationDate integer default 2000, - groupID varchar(255) default '00000000-0000-0000-0000-000000000000', - groupOwned integer default 0, - flags integer default 0); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_RegionStore.sql b/OpenSim/Data/SQLite/Resources/001_RegionStore.sql deleted file mode 100644 index 39e8180cdc..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_RegionStore.sql +++ /dev/null @@ -1,144 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE prims( - UUID varchar(255) primary key, - RegionUUID varchar(255), - ParentID integer, - CreationDate integer, - Name varchar(255), - SceneGroupID varchar(255), - Text varchar(255), - Description varchar(255), - SitName varchar(255), - TouchName varchar(255), - CreatorID varchar(255), - OwnerID varchar(255), - GroupID varchar(255), - LastOwnerID varchar(255), - OwnerMask integer, - NextOwnerMask integer, - GroupMask integer, - EveryoneMask integer, - BaseMask integer, - PositionX float, - PositionY float, - PositionZ float, - GroupPositionX float, - GroupPositionY float, - GroupPositionZ float, - VelocityX float, - VelocityY float, - VelocityZ float, - AngularVelocityX float, - AngularVelocityY float, - AngularVelocityZ float, - AccelerationX float, - AccelerationY float, - AccelerationZ float, - RotationX float, - RotationY float, - RotationZ float, - RotationW float, - ObjectFlags integer, - SitTargetOffsetX float NOT NULL default 0, - SitTargetOffsetY float NOT NULL default 0, - SitTargetOffsetZ float NOT NULL default 0, - SitTargetOrientW float NOT NULL default 0, - SitTargetOrientX float NOT NULL default 0, - SitTargetOrientY float NOT NULL default 0, - SitTargetOrientZ float NOT NULL default 0); - -CREATE TABLE primshapes( - UUID varchar(255) primary key, - Shape integer, - ScaleX float, - ScaleY float, - ScaleZ float, - PCode integer, - PathBegin integer, - PathEnd integer, - PathScaleX integer, - PathScaleY integer, - PathShearX integer, - PathShearY integer, - PathSkew integer, - PathCurve integer, - PathRadiusOffset integer, - PathRevolutions integer, - PathTaperX integer, - PathTaperY integer, - PathTwist integer, - PathTwistBegin integer, - ProfileBegin integer, - ProfileEnd integer, - ProfileCurve integer, - ProfileHollow integer, - Texture blob, - ExtraParams blob, - State Integer NOT NULL default 0); - -CREATE TABLE primitems( - itemID varchar(255) primary key, - primID varchar(255), - assetID varchar(255), - parentFolderID varchar(255), - invType integer, - assetType integer, - name varchar(255), - description varchar(255), - creationDate integer, - creatorID varchar(255), - ownerID varchar(255), - lastOwnerID varchar(255), - groupID varchar(255), - nextPermissions string, - currentPermissions string, - basePermissions string, - everyonePermissions string, - groupPermissions string); - -CREATE TABLE terrain( - RegionUUID varchar(255), - Revision integer, - Heightfield blob); - -CREATE TABLE land( - UUID varchar(255) primary key, - RegionUUID varchar(255), - LocalLandID string, - Bitmap blob, - Name varchar(255), - Desc varchar(255), - OwnerUUID varchar(255), - IsGroupOwned string, - Area integer, - AuctionID integer, - Category integer, - ClaimDate integer, - ClaimPrice integer, - GroupUUID varchar(255), - SalePrice integer, - LandStatus integer, - LandFlags string, - LandingType string, - MediaAutoScale string, - MediaTextureUUID varchar(255), - MediaURL varchar(255), - MusicURL varchar(255), - PassHours float, - PassPrice string, - SnapshotUUID varchar(255), - UserLocationX float, - UserLocationY float, - UserLocationZ float, - UserLookAtX float, - UserLookAtY float, - UserLookAtZ float, - AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'); - -CREATE TABLE landaccesslist( - LandUUID varchar(255), - AccessUUID varchar(255), - Flags string); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_UserStore.sql b/OpenSim/Data/SQLite/Resources/001_UserStore.sql deleted file mode 100644 index b584594ced..0000000000 --- a/OpenSim/Data/SQLite/Resources/001_UserStore.sql +++ /dev/null @@ -1,39 +0,0 @@ -BEGIN TRANSACTION; - --- users table -CREATE TABLE users( - UUID varchar(255) primary key, - username varchar(255), - surname varchar(255), - passwordHash varchar(255), - passwordSalt varchar(255), - homeRegionX integer, - homeRegionY integer, - homeLocationX float, - homeLocationY float, - homeLocationZ float, - homeLookAtX float, - homeLookAtY float, - homeLookAtZ float, - created integer, - lastLogin integer, - rootInventoryFolderID varchar(255), - userInventoryURI varchar(255), - userAssetURI varchar(255), - profileCanDoMask integer, - profileWantDoMask integer, - profileAboutText varchar(255), - profileFirstText varchar(255), - profileImage varchar(255), - profileFirstImage varchar(255), - webLoginKey text default '00000000-0000-0000-0000-000000000000'); --- friends table -CREATE TABLE userfriends( - ownerID varchar(255), - friendID varchar(255), - friendPerms integer, - ownerPerms integer, - datetimestamp integer); - -COMMIT; - diff --git a/OpenSim/Data/SQLite/Resources/002_AssetStore.sql b/OpenSim/Data/SQLite/Resources/002_AssetStore.sql deleted file mode 100644 index 5339b84dfd..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_AssetStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); -INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; -DROP TABLE assets; -CREATE TABLE assets(UUID,Name,Description,Type,Local,Temporary,Data); -INSERT INTO assets SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; -DROP TABLE assets_backup; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_AuthStore.sql b/OpenSim/Data/SQLite/Resources/002_AuthStore.sql deleted file mode 100644 index 3237b68fd6..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_AuthStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_FriendsStore.sql b/OpenSim/Data/SQLite/Resources/002_FriendsStore.sql deleted file mode 100644 index 6733502224..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_FriendsStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/002_InventoryStore.sql deleted file mode 100644 index 01951d6582..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_InventoryStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN TRANSACTION; - -create index inventoryfolders_agentid on inventoryfolders(agentid); -create index inventoryfolders_parentid on inventoryfolders(parentid); -create index inventoryitems_parentfolderid on inventoryitems(parentfolderid); -create index inventoryitems_avatarid on inventoryitems(avatarid); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/002_RegionStore.sql b/OpenSim/Data/SQLite/Resources/002_RegionStore.sql deleted file mode 100644 index c5c7c99455..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_RegionStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE regionban( - regionUUID varchar (255), - bannedUUID varchar (255), - bannedIp varchar (255), - bannedIpHostMask varchar (255) - ); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/002_UserAccount.sql b/OpenSim/Data/SQLite/Resources/002_UserAccount.sql deleted file mode 100644 index c7a62932ac..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_UserAccount.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, surname AS LastName, '' as Email, '' AS ServiceURLs, created as Created FROM users; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/002_UserStore.sql b/OpenSim/Data/SQLite/Resources/002_UserStore.sql deleted file mode 100644 index 48fc680b33..0000000000 --- a/OpenSim/Data/SQLite/Resources/002_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE users add homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/003_AssetStore.sql b/OpenSim/Data/SQLite/Resources/003_AssetStore.sql deleted file mode 100644 index f54f8d98a2..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_AssetStore.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM assets WHERE UUID = 'dc4b9f0bd00845c696a401dd947ac621' diff --git a/OpenSim/Data/SQLite/Resources/003_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/003_InventoryStore.sql deleted file mode 100644 index 4c6da91aab..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_InventoryStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/003_RegionStore.sql b/OpenSim/Data/SQLite/Resources/003_RegionStore.sql deleted file mode 100644 index 4db2f7587d..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE primitems add flags integer not null default 0; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/003_UserStore.sql b/OpenSim/Data/SQLite/Resources/003_UserStore.sql deleted file mode 100644 index 6f890eeec1..0000000000 --- a/OpenSim/Data/SQLite/Resources/003_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add userFlags integer NOT NULL default 0; -ALTER TABLE users add godLevel integer NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_AssetStore.sql b/OpenSim/Data/SQLite/Resources/004_AssetStore.sql deleted file mode 100644 index 39421c4434..0000000000 --- a/OpenSim/Data/SQLite/Resources/004_AssetStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -update assets - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_RegionStore.sql b/OpenSim/Data/SQLite/Resources/004_RegionStore.sql deleted file mode 100644 index de328cb47a..0000000000 --- a/OpenSim/Data/SQLite/Resources/004_RegionStore.sql +++ /dev/null @@ -1,38 +0,0 @@ -BEGIN; - -create table regionsettings ( - regionUUID char(36) not null, - block_terraform integer not null, - block_fly integer not null, - allow_damage integer not null, - restrict_pushing integer not null, - allow_land_resell integer not null, - allow_land_join_divide integer not null, - block_show_in_search integer not null, - agent_limit integer not null, - object_bonus float not null, - maturity integer not null, - disable_scripts integer not null, - disable_collisions integer not null, - disable_physics integer not null, - terrain_texture_1 char(36) not null, - terrain_texture_2 char(36) not null, - terrain_texture_3 char(36) not null, - terrain_texture_4 char(36) not null, - elevation_1_nw float not null, - elevation_2_nw float not null, - elevation_1_ne float not null, - elevation_2_ne float not null, - elevation_1_se float not null, - elevation_2_se float not null, - elevation_1_sw float not null, - elevation_2_sw float not null, - water_height float not null, - terrain_raise_limit float not null, - terrain_lower_limit float not null, - use_estate_sun integer not null, - fixed_sun integer not null, - sun_position float not null, - covenant char(36)); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_UserStore.sql b/OpenSim/Data/SQLite/Resources/004_UserStore.sql deleted file mode 100644 index 03142afa37..0000000000 --- a/OpenSim/Data/SQLite/Resources/004_UserStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE users add customType varchar(32) not null default ''; -ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/005_RegionStore.sql b/OpenSim/Data/SQLite/Resources/005_RegionStore.sql deleted file mode 100644 index 1f6d1bd271..0000000000 --- a/OpenSim/Data/SQLite/Resources/005_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -delete from regionsettings; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/005_UserStore.sql b/OpenSim/Data/SQLite/Resources/005_UserStore.sql deleted file mode 100644 index e45c09a493..0000000000 --- a/OpenSim/Data/SQLite/Resources/005_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `attachpoint` int(11) NOT NULL DEFAULT 0, `item` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `asset` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/006_RegionStore.sql b/OpenSim/Data/SQLite/Resources/006_RegionStore.sql deleted file mode 100644 index 94ed8181cd..0000000000 --- a/OpenSim/Data/SQLite/Resources/006_RegionStore.sql +++ /dev/null @@ -1,102 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE estate_groups ( - EstateID int(10) NOT NULL, - uuid char(36) NOT NULL -); - -CREATE TABLE estate_managers ( - EstateID int(10) NOT NULL, - uuid char(36) NOT NULL -); - -CREATE TABLE estate_map ( - RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - EstateID int(11) NOT NULL -); - -CREATE TABLE estate_settings ( - EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - EstateName varchar(64) default NULL, - AbuseEmailToEstateOwner tinyint(4) NOT NULL, - DenyAnonymous tinyint(4) NOT NULL, - ResetHomeOnTeleport tinyint(4) NOT NULL, - FixedSun tinyint(4) NOT NULL, - DenyTransacted tinyint(4) NOT NULL, - BlockDwell tinyint(4) NOT NULL, - DenyIdentified tinyint(4) NOT NULL, - AllowVoice tinyint(4) NOT NULL, - UseGlobalTime tinyint(4) NOT NULL, - PricePerMeter int(11) NOT NULL, - TaxFree tinyint(4) NOT NULL, - AllowDirectTeleport tinyint(4) NOT NULL, - RedirectGridX int(11) NOT NULL, - RedirectGridY int(11) NOT NULL, - ParentEstateID int(10) NOT NULL, - SunPosition double NOT NULL, - EstateSkipScripts tinyint(4) NOT NULL, - BillableFactor float NOT NULL, - PublicAccess tinyint(4) NOT NULL -); -insert into estate_settings (EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); -delete from estate_settings; -CREATE TABLE estate_users ( - EstateID int(10) NOT NULL, - uuid char(36) NOT NULL -); - -CREATE TABLE estateban ( - EstateID int(10) NOT NULL, - bannedUUID varchar(36) NOT NULL, - bannedIp varchar(16) NOT NULL, - bannedIpHostMask varchar(16) NOT NULL, - bannedNameMask varchar(64) default NULL -); - -drop table regionsettings; -CREATE TABLE regionsettings ( - regionUUID char(36) NOT NULL, - block_terraform int(11) NOT NULL, - block_fly int(11) NOT NULL, - allow_damage int(11) NOT NULL, - restrict_pushing int(11) NOT NULL, - allow_land_resell int(11) NOT NULL, - allow_land_join_divide int(11) NOT NULL, - block_show_in_search int(11) NOT NULL, - agent_limit int(11) NOT NULL, - object_bonus float NOT NULL, - maturity int(11) NOT NULL, - disable_scripts int(11) NOT NULL, - disable_collisions int(11) NOT NULL, - disable_physics int(11) NOT NULL, - terrain_texture_1 char(36) NOT NULL, - terrain_texture_2 char(36) NOT NULL, - terrain_texture_3 char(36) NOT NULL, - terrain_texture_4 char(36) NOT NULL, - elevation_1_nw float NOT NULL, - elevation_2_nw float NOT NULL, - elevation_1_ne float NOT NULL, - elevation_2_ne float NOT NULL, - elevation_1_se float NOT NULL, - elevation_2_se float NOT NULL, - elevation_1_sw float NOT NULL, - elevation_2_sw float NOT NULL, - water_height float NOT NULL, - terrain_raise_limit float NOT NULL, - terrain_lower_limit float NOT NULL, - use_estate_sun int(11) NOT NULL, - fixed_sun int(11) NOT NULL, - sun_position float NOT NULL, - covenant char(36) default NULL, - Sandbox tinyint(4) NOT NULL, - PRIMARY KEY (regionUUID) -); - -CREATE INDEX estate_ban_estate_id on estateban(EstateID); -CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); -CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); -CREATE INDEX estate_map_estate_id on estate_map(EstateID); -CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); -CREATE INDEX estate_users_estate_id on estate_users(EstateID); - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/006_UserStore.sql b/OpenSim/Data/SQLite/Resources/006_UserStore.sql deleted file mode 100644 index f9454c55cf..0000000000 --- a/OpenSim/Data/SQLite/Resources/006_UserStore.sql +++ /dev/null @@ -1,20 +0,0 @@ -BEGIN TRANSACTION; - --- usersagents table -CREATE TABLE IF NOT EXISTS useragents( - UUID varchar(255) primary key, - agentIP varchar(255), - agentPort integer, - agentOnline boolean, - sessionID varchar(255), - secureSessionID varchar(255), - regionID varchar(255), - loginTime integer, - logoutTime integer, - currentRegion varchar(255), - currentHandle varchar(255), - currentPosX float, - currentPosY float, - currentPosZ float); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/007_RegionStore.sql b/OpenSim/Data/SQLite/Resources/007_RegionStore.sql deleted file mode 100644 index 1c813a0d40..0000000000 --- a/OpenSim/Data/SQLite/Resources/007_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -alter table estate_settings add column AbuseEmail varchar(255) not null default ''; - -alter table estate_settings add column EstateOwner varchar(36) not null default ''; - -commit; - diff --git a/OpenSim/Data/SQLite/Resources/007_UserStore.sql b/OpenSim/Data/SQLite/Resources/007_UserStore.sql deleted file mode 100644 index 8b0cd285c7..0000000000 --- a/OpenSim/Data/SQLite/Resources/007_UserStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION; - -ALTER TABLE useragents add currentLookAtX float not null default 128; -ALTER TABLE useragents add currentLookAtY float not null default 128; -ALTER TABLE useragents add currentLookAtZ float not null default 70; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/008_RegionStore.sql b/OpenSim/Data/SQLite/Resources/008_RegionStore.sql deleted file mode 100644 index 28bfbf59c3..0000000000 --- a/OpenSim/Data/SQLite/Resources/008_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -begin; - -alter table estate_settings add column DenyMinors tinyint not null default 0; - -commit; - diff --git a/OpenSim/Data/SQLite/Resources/008_UserStore.sql b/OpenSim/Data/SQLite/Resources/008_UserStore.sql deleted file mode 100644 index 97da81848c..0000000000 --- a/OpenSim/Data/SQLite/Resources/008_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION; - -ALTER TABLE users add email varchar(250); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/009_RegionStore.sql b/OpenSim/Data/SQLite/Resources/009_RegionStore.sql deleted file mode 100644 index 1f40548f36..0000000000 --- a/OpenSim/Data/SQLite/Resources/009_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; -ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/009_UserStore.sql b/OpenSim/Data/SQLite/Resources/009_UserStore.sql deleted file mode 100644 index 8ab03ef897..0000000000 --- a/OpenSim/Data/SQLite/Resources/009_UserStore.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - -update users - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -update useragents - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/010_RegionStore.sql b/OpenSim/Data/SQLite/Resources/010_RegionStore.sql deleted file mode 100644 index b91ccf0a8d..0000000000 --- a/OpenSim/Data/SQLite/Resources/010_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN ClickAction INTEGER NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/010_UserStore.sql b/OpenSim/Data/SQLite/Resources/010_UserStore.sql deleted file mode 100644 index 5f956dadfd..0000000000 --- a/OpenSim/Data/SQLite/Resources/010_UserStore.sql +++ /dev/null @@ -1,37 +0,0 @@ -BEGIN TRANSACTION; - -CREATE TABLE IF NOT EXISTS avatarappearance( - Owner varchar(36) NOT NULL primary key, - BodyItem varchar(36) DEFAULT NULL, - BodyAsset varchar(36) DEFAULT NULL, - SkinItem varchar(36) DEFAULT NULL, - SkinAsset varchar(36) DEFAULT NULL, - HairItem varchar(36) DEFAULT NULL, - HairAsset varchar(36) DEFAULT NULL, - EyesItem varchar(36) DEFAULT NULL, - EyesAsset varchar(36) DEFAULT NULL, - ShirtItem varchar(36) DEFAULT NULL, - ShirtAsset varchar(36) DEFAULT NULL, - PantsItem varchar(36) DEFAULT NULL, - PantsAsset varchar(36) DEFAULT NULL, - ShoesItem varchar(36) DEFAULT NULL, - ShoesAsset varchar(36) DEFAULT NULL, - SocksItem varchar(36) DEFAULT NULL, - SocksAsset varchar(36) DEFAULT NULL, - JacketItem varchar(36) DEFAULT NULL, - JacketAsset varchar(36) DEFAULT NULL, - GlovesItem varchar(36) DEFAULT NULL, - GlovesAsset varchar(36) DEFAULT NULL, - UnderShirtItem varchar(36) DEFAULT NULL, - UnderShirtAsset varchar(36) DEFAULT NULL, - UnderPantsItem varchar(36) DEFAULT NULL, - UnderPantsAsset varchar(36) DEFAULT NULL, - SkirtItem varchar(36) DEFAULT NULL, - SkirtAsset varchar(36) DEFAULT NULL, - Texture blob, - VisualParams blob, - Serial int DEFAULT NULL, - AvatarHeight float DEFAULT NULL -); - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/011_RegionStore.sql b/OpenSim/Data/SQLite/Resources/011_RegionStore.sql deleted file mode 100644 index 42bef89616..0000000000 --- a/OpenSim/Data/SQLite/Resources/011_RegionStore.sql +++ /dev/null @@ -1,28 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN PayPrice INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton1 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton2 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton3 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN PayButton4 INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN LoopedSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN LoopedSoundGain float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN TextureAnimation string; -ALTER TABLE prims ADD COLUMN ParticleSystem string; -ALTER TABLE prims ADD COLUMN OmegaX float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN OmegaY float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN OmegaZ float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetX float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetY float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float NOT NULL default 0; -ALTER TABLE prims ADD COLUMN ForceMouselook string NOT NULL default 0; -ALTER TABLE prims ADD COLUMN ScriptAccessPin INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN AllowedDrop INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN DieAtEdge string NOT NULL default 0; -ALTER TABLE prims ADD COLUMN SalePrice INTEGER NOT NULL default 0; -ALTER TABLE prims ADD COLUMN SaleType string NOT NULL default 0; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/012_RegionStore.sql b/OpenSim/Data/SQLite/Resources/012_RegionStore.sql deleted file mode 100644 index d952b78bd2..0000000000 --- a/OpenSim/Data/SQLite/Resources/012_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN Material INTEGER NOT NULL default 3; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/013_RegionStore.sql b/OpenSim/Data/SQLite/Resources/013_RegionStore.sql deleted file mode 100644 index 11529cd3f1..0000000000 --- a/OpenSim/Data/SQLite/Resources/013_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE land ADD COLUMN OtherCleanTime INTEGER NOT NULL default 0; -ALTER TABLE land ADD COLUMN Dwell INTEGER NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/014_RegionStore.sql b/OpenSim/Data/SQLite/Resources/014_RegionStore.sql deleted file mode 100644 index c59b27e745..0000000000 --- a/OpenSim/Data/SQLite/Resources/014_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -begin; - -ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; -ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; - -commit; - diff --git a/OpenSim/Data/SQLite/Resources/015_RegionStore.sql b/OpenSim/Data/SQLite/Resources/015_RegionStore.sql deleted file mode 100644 index c43f356be3..0000000000 --- a/OpenSim/Data/SQLite/Resources/015_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN CollisionSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD COLUMN CollisionSoundVolume float NOT NULL default 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/016_RegionStore.sql b/OpenSim/Data/SQLite/Resources/016_RegionStore.sql deleted file mode 100644 index 52f160cdbc..0000000000 --- a/OpenSim/Data/SQLite/Resources/016_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE prims ADD COLUMN VolumeDetect INTEGER NOT NULL DEFAULT 0; - -COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/017_RegionStore.sql b/OpenSim/Data/SQLite/Resources/017_RegionStore.sql deleted file mode 100644 index 6c6b7b5d40..0000000000 --- a/OpenSim/Data/SQLite/Resources/017_RegionStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; -CREATE TEMPORARY TABLE prims_backup(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); -INSERT INTO prims_backup SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims; -DROP TABLE prims; -CREATE TABLE prims(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); -INSERT INTO prims SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims_backup; -DROP TABLE prims_backup; -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/018_RegionStore.sql b/OpenSim/Data/SQLite/Resources/018_RegionStore.sql deleted file mode 100644 index 6a390c2161..0000000000 --- a/OpenSim/Data/SQLite/Resources/018_RegionStore.sql +++ /dev/null @@ -1,79 +0,0 @@ -BEGIN; - -update terrain - set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) - where RegionUUID not like '%-%'; - - -update landaccesslist - set LandUUID = substr(LandUUID, 1, 8) || "-" || substr(LandUUID, 9, 4) || "-" || substr(LandUUID, 13, 4) || "-" || substr(LandUUID, 17, 4) || "-" || substr(LandUUID, 21, 12) - where LandUUID not like '%-%'; - -update landaccesslist - set AccessUUID = substr(AccessUUID, 1, 8) || "-" || substr(AccessUUID, 9, 4) || "-" || substr(AccessUUID, 13, 4) || "-" || substr(AccessUUID, 17, 4) || "-" || substr(AccessUUID, 21, 12) - where AccessUUID not like '%-%'; - - -update prims - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -update prims - set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) - where RegionUUID not like '%-%'; - -update prims - set SceneGroupID = substr(SceneGroupID, 1, 8) || "-" || substr(SceneGroupID, 9, 4) || "-" || substr(SceneGroupID, 13, 4) || "-" || substr(SceneGroupID, 17, 4) || "-" || substr(SceneGroupID, 21, 12) - where SceneGroupID not like '%-%'; - -update prims - set CreatorID = substr(CreatorID, 1, 8) || "-" || substr(CreatorID, 9, 4) || "-" || substr(CreatorID, 13, 4) || "-" || substr(CreatorID, 17, 4) || "-" || substr(CreatorID, 21, 12) - where CreatorID not like '%-%'; - -update prims - set OwnerID = substr(OwnerID, 1, 8) || "-" || substr(OwnerID, 9, 4) || "-" || substr(OwnerID, 13, 4) || "-" || substr(OwnerID, 17, 4) || "-" || substr(OwnerID, 21, 12) - where OwnerID not like '%-%'; - -update prims - set GroupID = substr(GroupID, 1, 8) || "-" || substr(GroupID, 9, 4) || "-" || substr(GroupID, 13, 4) || "-" || substr(GroupID, 17, 4) || "-" || substr(GroupID, 21, 12) - where GroupID not like '%-%'; - -update prims - set LastOwnerID = substr(LastOwnerID, 1, 8) || "-" || substr(LastOwnerID, 9, 4) || "-" || substr(LastOwnerID, 13, 4) || "-" || substr(LastOwnerID, 17, 4) || "-" || substr(LastOwnerID, 21, 12) - where LastOwnerID not like '%-%'; - - -update primshapes - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - - -update land - set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) - where UUID not like '%-%'; - -update land - set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) - where RegionUUID not like '%-%'; - -update land - set OwnerUUID = substr(OwnerUUID, 1, 8) || "-" || substr(OwnerUUID, 9, 4) || "-" || substr(OwnerUUID, 13, 4) || "-" || substr(OwnerUUID, 17, 4) || "-" || substr(OwnerUUID, 21, 12) - where OwnerUUID not like '%-%'; - -update land - set GroupUUID = substr(GroupUUID, 1, 8) || "-" || substr(GroupUUID, 9, 4) || "-" || substr(GroupUUID, 13, 4) || "-" || substr(GroupUUID, 17, 4) || "-" || substr(GroupUUID, 21, 12) - where GroupUUID not like '%-%'; - -update land - set MediaTextureUUID = substr(MediaTextureUUID, 1, 8) || "-" || substr(MediaTextureUUID, 9, 4) || "-" || substr(MediaTextureUUID, 13, 4) || "-" || substr(MediaTextureUUID, 17, 4) || "-" || substr(MediaTextureUUID, 21, 12) - where MediaTextureUUID not like '%-%'; - -update land - set SnapshotUUID = substr(SnapshotUUID, 1, 8) || "-" || substr(SnapshotUUID, 9, 4) || "-" || substr(SnapshotUUID, 13, 4) || "-" || substr(SnapshotUUID, 17, 4) || "-" || substr(SnapshotUUID, 21, 12) - where SnapshotUUID not like '%-%'; - -update land - set AuthbuyerID = substr(AuthbuyerID, 1, 8) || "-" || substr(AuthbuyerID, 9, 4) || "-" || substr(AuthbuyerID, 13, 4) || "-" || substr(AuthbuyerID, 17, 4) || "-" || substr(AuthbuyerID, 21, 12) - where AuthbuyerID not like '%-%'; - -COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/AssetStore.migrations b/OpenSim/Data/SQLite/Resources/AssetStore.migrations new file mode 100644 index 0000000000..fb72b91af9 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/AssetStore.migrations @@ -0,0 +1,42 @@ +:VERSION 1 + +BEGIN TRANSACTION; +CREATE TABLE assets( + UUID varchar(255) primary key, + Name varchar(255), + Description varchar(255), + Type integer, + InvType integer, + Local integer, + Temporary integer, + Data blob); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; +DROP TABLE assets; +CREATE TABLE assets(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; +DROP TABLE assets_backup; + +COMMIT; + +:VERSION 3 + +DELETE FROM assets WHERE UUID = 'dc4b9f0bd00845c696a401dd947ac621' + +:VERSION 4 + +BEGIN; + +update assets + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/001_AuthStore.sql b/OpenSim/Data/SQLite/Resources/AuthStore.migrations similarity index 62% rename from OpenSim/Data/SQLite/Resources/001_AuthStore.sql rename to OpenSim/Data/SQLite/Resources/AuthStore.migrations index 468567dcc2..ca6bdf55a5 100644 --- a/OpenSim/Data/SQLite/Resources/001_AuthStore.sql +++ b/OpenSim/Data/SQLite/Resources/AuthStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION; CREATE TABLE auth ( @@ -16,3 +18,12 @@ CREATE TABLE tokens ( ); COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/001_Avatar.sql b/OpenSim/Data/SQLite/Resources/Avatar.migrations similarity index 92% rename from OpenSim/Data/SQLite/Resources/001_Avatar.sql rename to OpenSim/Data/SQLite/Resources/Avatar.migrations index 7ec906b48a..13b41969ec 100644 --- a/OpenSim/Data/SQLite/Resources/001_Avatar.sql +++ b/OpenSim/Data/SQLite/Resources/Avatar.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION; CREATE TABLE Avatars ( diff --git a/OpenSim/Data/SQLite/Resources/001_FriendsStore.sql b/OpenSim/Data/SQLite/Resources/FriendsStore.migrations similarity index 63% rename from OpenSim/Data/SQLite/Resources/001_FriendsStore.sql rename to OpenSim/Data/SQLite/Resources/FriendsStore.migrations index f1b9ab9902..3eb4352a0c 100644 --- a/OpenSim/Data/SQLite/Resources/001_FriendsStore.sql +++ b/OpenSim/Data/SQLite/Resources/FriendsStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION; CREATE TABLE `Friends` ( @@ -8,3 +10,11 @@ CREATE TABLE `Friends` ( PRIMARY KEY(`PrincipalID`, `Friend`)); COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/004_InventoryStore.sql b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations similarity index 55% rename from OpenSim/Data/SQLite/Resources/004_InventoryStore.sql rename to OpenSim/Data/SQLite/Resources/InventoryStore.migrations index e8f4d46333..585ac498e6 100644 --- a/OpenSim/Data/SQLite/Resources/004_InventoryStore.sql +++ b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations @@ -1,3 +1,59 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders( + UUID varchar(255) primary key, + name varchar(255), + agentID varchar(255), + parentID varchar(255), + type integer, + version integer); + +CREATE TABLE inventoryitems( + UUID varchar(255) primary key, + assetID varchar(255), + assetType integer, + invType integer, + parentFolderID varchar(255), + avatarID varchar(255), + creatorsID varchar(255), + inventoryName varchar(255), + inventoryDescription varchar(255), + inventoryNextPermissions integer, + inventoryCurrentPermissions integer, + inventoryBasePermissions integer, + inventoryEveryOnePermissions integer, + salePrice integer default 99, + saleType integer default 0, + creationDate integer default 2000, + groupID varchar(255) default '00000000-0000-0000-0000-000000000000', + groupOwned integer default 0, + flags integer default 0); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +create index inventoryfolders_agentid on inventoryfolders(agentid); +create index inventoryfolders_parentid on inventoryfolders(parentid); +create index inventoryitems_parentfolderid on inventoryitems(parentfolderid); +create index inventoryitems_avatarid on inventoryitems(avatarid); + +COMMIT; + +:VERSION 3 + +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; + +:VERSION 4 + BEGIN; update inventoryitems diff --git a/OpenSim/Data/SQLite/Resources/RegionStore.migrations b/OpenSim/Data/SQLite/Resources/RegionStore.migrations new file mode 100644 index 0000000000..7b27378139 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/RegionStore.migrations @@ -0,0 +1,526 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE prims( + UUID varchar(255) primary key, + RegionUUID varchar(255), + ParentID integer, + CreationDate integer, + Name varchar(255), + SceneGroupID varchar(255), + Text varchar(255), + Description varchar(255), + SitName varchar(255), + TouchName varchar(255), + CreatorID varchar(255), + OwnerID varchar(255), + GroupID varchar(255), + LastOwnerID varchar(255), + OwnerMask integer, + NextOwnerMask integer, + GroupMask integer, + EveryoneMask integer, + BaseMask integer, + PositionX float, + PositionY float, + PositionZ float, + GroupPositionX float, + GroupPositionY float, + GroupPositionZ float, + VelocityX float, + VelocityY float, + VelocityZ float, + AngularVelocityX float, + AngularVelocityY float, + AngularVelocityZ float, + AccelerationX float, + AccelerationY float, + AccelerationZ float, + RotationX float, + RotationY float, + RotationZ float, + RotationW float, + ObjectFlags integer, + SitTargetOffsetX float NOT NULL default 0, + SitTargetOffsetY float NOT NULL default 0, + SitTargetOffsetZ float NOT NULL default 0, + SitTargetOrientW float NOT NULL default 0, + SitTargetOrientX float NOT NULL default 0, + SitTargetOrientY float NOT NULL default 0, + SitTargetOrientZ float NOT NULL default 0); + +CREATE TABLE primshapes( + UUID varchar(255) primary key, + Shape integer, + ScaleX float, + ScaleY float, + ScaleZ float, + PCode integer, + PathBegin integer, + PathEnd integer, + PathScaleX integer, + PathScaleY integer, + PathShearX integer, + PathShearY integer, + PathSkew integer, + PathCurve integer, + PathRadiusOffset integer, + PathRevolutions integer, + PathTaperX integer, + PathTaperY integer, + PathTwist integer, + PathTwistBegin integer, + ProfileBegin integer, + ProfileEnd integer, + ProfileCurve integer, + ProfileHollow integer, + Texture blob, + ExtraParams blob, + State Integer NOT NULL default 0); + +CREATE TABLE primitems( + itemID varchar(255) primary key, + primID varchar(255), + assetID varchar(255), + parentFolderID varchar(255), + invType integer, + assetType integer, + name varchar(255), + description varchar(255), + creationDate integer, + creatorID varchar(255), + ownerID varchar(255), + lastOwnerID varchar(255), + groupID varchar(255), + nextPermissions string, + currentPermissions string, + basePermissions string, + everyonePermissions string, + groupPermissions string); + +CREATE TABLE terrain( + RegionUUID varchar(255), + Revision integer, + Heightfield blob); + +CREATE TABLE land( + UUID varchar(255) primary key, + RegionUUID varchar(255), + LocalLandID string, + Bitmap blob, + Name varchar(255), + Desc varchar(255), + OwnerUUID varchar(255), + IsGroupOwned string, + Area integer, + AuctionID integer, + Category integer, + ClaimDate integer, + ClaimPrice integer, + GroupUUID varchar(255), + SalePrice integer, + LandStatus integer, + LandFlags string, + LandingType string, + MediaAutoScale string, + MediaTextureUUID varchar(255), + MediaURL varchar(255), + MusicURL varchar(255), + PassHours float, + PassPrice string, + SnapshotUUID varchar(255), + UserLocationX float, + UserLocationY float, + UserLocationZ float, + UserLookAtX float, + UserLookAtY float, + UserLookAtZ float, + AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'); + +CREATE TABLE landaccesslist( + LandUUID varchar(255), + AccessUUID varchar(255), + Flags string); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE regionban( + regionUUID varchar (255), + bannedUUID varchar (255), + bannedIp varchar (255), + bannedIpHostMask varchar (255) + ); + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36)); + +COMMIT; + +:VERSION 5 + +BEGIN; + +delete from regionsettings; + +COMMIT; + +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE estate_groups ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_managers ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_map ( + RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + EstateID int(11) NOT NULL +); + +CREATE TABLE estate_settings ( + EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + EstateName varchar(64) default NULL, + AbuseEmailToEstateOwner tinyint(4) NOT NULL, + DenyAnonymous tinyint(4) NOT NULL, + ResetHomeOnTeleport tinyint(4) NOT NULL, + FixedSun tinyint(4) NOT NULL, + DenyTransacted tinyint(4) NOT NULL, + BlockDwell tinyint(4) NOT NULL, + DenyIdentified tinyint(4) NOT NULL, + AllowVoice tinyint(4) NOT NULL, + UseGlobalTime tinyint(4) NOT NULL, + PricePerMeter int(11) NOT NULL, + TaxFree tinyint(4) NOT NULL, + AllowDirectTeleport tinyint(4) NOT NULL, + RedirectGridX int(11) NOT NULL, + RedirectGridY int(11) NOT NULL, + ParentEstateID int(10) NOT NULL, + SunPosition double NOT NULL, + EstateSkipScripts tinyint(4) NOT NULL, + BillableFactor float NOT NULL, + PublicAccess tinyint(4) NOT NULL +); +insert into estate_settings (EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); +delete from estate_settings; +CREATE TABLE estate_users ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estateban ( + EstateID int(10) NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL, + bannedNameMask varchar(64) default NULL +); + +drop table regionsettings; +CREATE TABLE regionsettings ( + regionUUID char(36) NOT NULL, + block_terraform int(11) NOT NULL, + block_fly int(11) NOT NULL, + allow_damage int(11) NOT NULL, + restrict_pushing int(11) NOT NULL, + allow_land_resell int(11) NOT NULL, + allow_land_join_divide int(11) NOT NULL, + block_show_in_search int(11) NOT NULL, + agent_limit int(11) NOT NULL, + object_bonus float NOT NULL, + maturity int(11) NOT NULL, + disable_scripts int(11) NOT NULL, + disable_collisions int(11) NOT NULL, + disable_physics int(11) NOT NULL, + terrain_texture_1 char(36) NOT NULL, + terrain_texture_2 char(36) NOT NULL, + terrain_texture_3 char(36) NOT NULL, + terrain_texture_4 char(36) NOT NULL, + elevation_1_nw float NOT NULL, + elevation_2_nw float NOT NULL, + elevation_1_ne float NOT NULL, + elevation_2_ne float NOT NULL, + elevation_1_se float NOT NULL, + elevation_2_se float NOT NULL, + elevation_1_sw float NOT NULL, + elevation_2_sw float NOT NULL, + water_height float NOT NULL, + terrain_raise_limit float NOT NULL, + terrain_lower_limit float NOT NULL, + use_estate_sun int(11) NOT NULL, + fixed_sun int(11) NOT NULL, + sun_position float NOT NULL, + covenant char(36) default NULL, + Sandbox tinyint(4) NOT NULL, + PRIMARY KEY (regionUUID) +); + +CREATE INDEX estate_ban_estate_id on estateban(EstateID); +CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); +CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); +CREATE INDEX estate_map_estate_id on estate_map(EstateID); +CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); +CREATE INDEX estate_users_estate_id on estate_users(EstateID); + +COMMIT; + +:VERSION 7 + +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null default ''; + +alter table estate_settings add column EstateOwner varchar(36) not null default ''; + +commit; + +:VERSION 8 + +begin; + +alter table estate_settings add column DenyMinors tinyint not null default 0; + +commit; + +:VERSION 9 + +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; + +COMMIT; + +:VERSION 10 + +BEGIN; + +ALTER TABLE prims ADD COLUMN ClickAction INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 11 + +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton1 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton2 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton3 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton4 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN LoopedSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN TextureAnimation string; +ALTER TABLE prims ADD COLUMN ParticleSystem string; +ALTER TABLE prims ADD COLUMN OmegaX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ForceMouselook string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SalePrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SaleType string NOT NULL default 0; + +COMMIT; + +:VERSION 12 + +BEGIN; + +ALTER TABLE prims ADD COLUMN Material INTEGER NOT NULL default 3; + +COMMIT; + +:VERSION 13 + +BEGIN; + +ALTER TABLE land ADD COLUMN OtherCleanTime INTEGER NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 14 + +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + +:VERSION 15 + +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float NOT NULL default 0; + +COMMIT; + +:VERSION 16 + +BEGIN; + +ALTER TABLE prims ADD COLUMN VolumeDetect INTEGER NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 17 + +BEGIN; +CREATE TEMPORARY TABLE prims_backup(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims_backup SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims; +DROP TABLE prims; +CREATE TABLE prims(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims_backup; +DROP TABLE prims_backup; +COMMIT; + +:VERSION 18 + +BEGIN; + +update terrain + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = substr(LandUUID, 1, 8) || "-" || substr(LandUUID, 9, 4) || "-" || substr(LandUUID, 13, 4) || "-" || substr(LandUUID, 17, 4) || "-" || substr(LandUUID, 21, 12) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = substr(AccessUUID, 1, 8) || "-" || substr(AccessUUID, 9, 4) || "-" || substr(AccessUUID, 13, 4) || "-" || substr(AccessUUID, 17, 4) || "-" || substr(AccessUUID, 21, 12) + where AccessUUID not like '%-%'; + + +update prims + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update prims + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = substr(SceneGroupID, 1, 8) || "-" || substr(SceneGroupID, 9, 4) || "-" || substr(SceneGroupID, 13, 4) || "-" || substr(SceneGroupID, 17, 4) || "-" || substr(SceneGroupID, 21, 12) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = substr(CreatorID, 1, 8) || "-" || substr(CreatorID, 9, 4) || "-" || substr(CreatorID, 13, 4) || "-" || substr(CreatorID, 17, 4) || "-" || substr(CreatorID, 21, 12) + where CreatorID not like '%-%'; + +update prims + set OwnerID = substr(OwnerID, 1, 8) || "-" || substr(OwnerID, 9, 4) || "-" || substr(OwnerID, 13, 4) || "-" || substr(OwnerID, 17, 4) || "-" || substr(OwnerID, 21, 12) + where OwnerID not like '%-%'; + +update prims + set GroupID = substr(GroupID, 1, 8) || "-" || substr(GroupID, 9, 4) || "-" || substr(GroupID, 13, 4) || "-" || substr(GroupID, 17, 4) || "-" || substr(GroupID, 21, 12) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = substr(LastOwnerID, 1, 8) || "-" || substr(LastOwnerID, 9, 4) || "-" || substr(LastOwnerID, 13, 4) || "-" || substr(LastOwnerID, 17, 4) || "-" || substr(LastOwnerID, 21, 12) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + + +update land + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update land + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = substr(OwnerUUID, 1, 8) || "-" || substr(OwnerUUID, 9, 4) || "-" || substr(OwnerUUID, 13, 4) || "-" || substr(OwnerUUID, 17, 4) || "-" || substr(OwnerUUID, 21, 12) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = substr(GroupUUID, 1, 8) || "-" || substr(GroupUUID, 9, 4) || "-" || substr(GroupUUID, 13, 4) || "-" || substr(GroupUUID, 17, 4) || "-" || substr(GroupUUID, 21, 12) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = substr(MediaTextureUUID, 1, 8) || "-" || substr(MediaTextureUUID, 9, 4) || "-" || substr(MediaTextureUUID, 13, 4) || "-" || substr(MediaTextureUUID, 17, 4) || "-" || substr(MediaTextureUUID, 21, 12) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = substr(SnapshotUUID, 1, 8) || "-" || substr(SnapshotUUID, 9, 4) || "-" || substr(SnapshotUUID, 13, 4) || "-" || substr(SnapshotUUID, 17, 4) || "-" || substr(SnapshotUUID, 21, 12) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = substr(AuthbuyerID, 1, 8) || "-" || substr(AuthbuyerID, 9, 4) || "-" || substr(AuthbuyerID, 13, 4) || "-" || substr(AuthbuyerID, 17, 4) || "-" || substr(AuthbuyerID, 21, 12) + where AuthbuyerID not like '%-%'; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/001_UserAccount.sql b/OpenSim/Data/SQLite/Resources/UserAccount.migrations similarity index 51% rename from OpenSim/Data/SQLite/Resources/001_UserAccount.sql rename to OpenSim/Data/SQLite/Resources/UserAccount.migrations index c38d9a762f..854fe694c2 100644 --- a/OpenSim/Data/SQLite/Resources/001_UserAccount.sql +++ b/OpenSim/Data/SQLite/Resources/UserAccount.migrations @@ -1,4 +1,6 @@ -BEGIN TRANSACTION; +:VERSION 1 + +BEGIN TRANSACTION; -- useraccounts table CREATE TABLE UserAccounts ( @@ -14,4 +16,12 @@ CREATE TABLE UserAccounts ( UserTitle varchar(64) NOT NULL DEFAULT '' ); -COMMIT; \ No newline at end of file +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, surname AS LastName, '' as Email, '' AS ServiceURLs, created as Created FROM users; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/UserStore.migrations b/OpenSim/Data/SQLite/Resources/UserStore.migrations new file mode 100644 index 0000000000..73d35e83c3 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/UserStore.migrations @@ -0,0 +1,169 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +-- users table +CREATE TABLE users( + UUID varchar(255) primary key, + username varchar(255), + surname varchar(255), + passwordHash varchar(255), + passwordSalt varchar(255), + homeRegionX integer, + homeRegionY integer, + homeLocationX float, + homeLocationY float, + homeLocationZ float, + homeLookAtX float, + homeLookAtY float, + homeLookAtZ float, + created integer, + lastLogin integer, + rootInventoryFolderID varchar(255), + userInventoryURI varchar(255), + userAssetURI varchar(255), + profileCanDoMask integer, + profileWantDoMask integer, + profileAboutText varchar(255), + profileFirstText varchar(255), + profileImage varchar(255), + profileFirstImage varchar(255), + webLoginKey text default '00000000-0000-0000-0000-000000000000'); +-- friends table +CREATE TABLE userfriends( + ownerID varchar(255), + friendID varchar(255), + friendPerms integer, + ownerPerms integer, + datetimestamp integer); + +COMMIT; + +:VERSION 2 + +BEGIN; + +ALTER TABLE users add homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 + +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `attachpoint` int(11) NOT NULL DEFAULT 0, `item` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `asset` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'); + +COMMIT; + +:VERSION 6 + +BEGIN TRANSACTION; + +-- usersagents table +CREATE TABLE IF NOT EXISTS useragents( + UUID varchar(255) primary key, + agentIP varchar(255), + agentPort integer, + agentOnline boolean, + sessionID varchar(255), + secureSessionID varchar(255), + regionID varchar(255), + loginTime integer, + logoutTime integer, + currentRegion varchar(255), + currentHandle varchar(255), + currentPosX float, + currentPosY float, + currentPosZ float); + +COMMIT; + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE useragents add currentLookAtX float not null default 128; +ALTER TABLE useragents add currentLookAtY float not null default 128; +ALTER TABLE useragents add currentLookAtZ float not null default 70; + +COMMIT; + +:VERSION 8 + +BEGIN TRANSACTION; + +ALTER TABLE users add email varchar(250); + +COMMIT; + +:VERSION 9 + +BEGIN; + +update users + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update useragents + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; + +:VERSION 10 + +BEGIN TRANSACTION; + +CREATE TABLE IF NOT EXISTS avatarappearance( + Owner varchar(36) NOT NULL primary key, + BodyItem varchar(36) DEFAULT NULL, + BodyAsset varchar(36) DEFAULT NULL, + SkinItem varchar(36) DEFAULT NULL, + SkinAsset varchar(36) DEFAULT NULL, + HairItem varchar(36) DEFAULT NULL, + HairAsset varchar(36) DEFAULT NULL, + EyesItem varchar(36) DEFAULT NULL, + EyesAsset varchar(36) DEFAULT NULL, + ShirtItem varchar(36) DEFAULT NULL, + ShirtAsset varchar(36) DEFAULT NULL, + PantsItem varchar(36) DEFAULT NULL, + PantsAsset varchar(36) DEFAULT NULL, + ShoesItem varchar(36) DEFAULT NULL, + ShoesAsset varchar(36) DEFAULT NULL, + SocksItem varchar(36) DEFAULT NULL, + SocksAsset varchar(36) DEFAULT NULL, + JacketItem varchar(36) DEFAULT NULL, + JacketAsset varchar(36) DEFAULT NULL, + GlovesItem varchar(36) DEFAULT NULL, + GlovesAsset varchar(36) DEFAULT NULL, + UnderShirtItem varchar(36) DEFAULT NULL, + UnderShirtAsset varchar(36) DEFAULT NULL, + UnderPantsItem varchar(36) DEFAULT NULL, + UnderPantsAsset varchar(36) DEFAULT NULL, + SkirtItem varchar(36) DEFAULT NULL, + SkirtAsset varchar(36) DEFAULT NULL, + Texture blob, + VisualParams blob, + Serial int DEFAULT NULL, + AvatarHeight float DEFAULT NULL +); + +COMMIT; From 020f38774fbe8e0f53b0cb28ab49b378b9dcd325 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 6 May 2010 23:16:36 +0300 Subject: [PATCH 148/260] MS SQL migrations converted to the new format --- .../Data/MSSQL/Resources/001_AssetStore.sql | 13 - .../Data/MSSQL/Resources/001_EstateStore.sql | 85 -- .../Data/MSSQL/Resources/001_GridStore.sql | 37 - .../MSSQL/Resources/001_InventoryStore.sql | 64 -- .../Data/MSSQL/Resources/001_RegionStore.sql | 161 --- .../Data/MSSQL/Resources/001_UserAccount.sql | 14 - .../Data/MSSQL/Resources/001_UserStore.sql | 112 --- .../Data/MSSQL/Resources/002_AssetStore.sql | 29 - .../Data/MSSQL/Resources/002_AuthStore.sql | 6 - .../Data/MSSQL/Resources/002_EstateStore.sql | 25 - .../Data/MSSQL/Resources/002_FriendsStore.sql | 6 - .../Data/MSSQL/Resources/002_GridStore.sql | 49 - .../MSSQL/Resources/002_InventoryStore.sql | 5 - .../Data/MSSQL/Resources/002_RegionStore.sql | 50 - .../Data/MSSQL/Resources/002_UserAccount.sql | 12 - .../Data/MSSQL/Resources/002_UserStore.sql | 9 - .../Data/MSSQL/Resources/003_AssetStore.sql | 6 - .../Data/MSSQL/Resources/003_EstateStore.sql | 25 - .../Data/MSSQL/Resources/003_GridStore.sql | 22 - .../MSSQL/Resources/003_InventoryStore.sql | 38 - .../Data/MSSQL/Resources/003_RegionStore.sql | 67 -- .../Data/MSSQL/Resources/003_UserAccount.sql | 9 - .../Data/MSSQL/Resources/003_UserStore.sql | 15 - .../Data/MSSQL/Resources/004_AssetStore.sql | 31 - .../Data/MSSQL/Resources/004_EstateStore.sql | 22 - .../Data/MSSQL/Resources/004_GridStore.sql | 68 -- .../MSSQL/Resources/004_InventoryStore.sql | 52 - .../Data/MSSQL/Resources/004_RegionStore.sql | 40 - .../Data/MSSQL/Resources/004_UserAccount.sql | 7 - .../Data/MSSQL/Resources/004_UserStore.sql | 29 - .../Data/MSSQL/Resources/005_AssetStore.sql | 1 - .../Data/MSSQL/Resources/005_EstateStore.sql | 22 - .../Data/MSSQL/Resources/005_GridStore.sql | 5 - .../Data/MSSQL/Resources/005_RegionStore.sql | 49 - .../Data/MSSQL/Resources/005_UserStore.sql | 5 - .../Data/MSSQL/Resources/006_EstateStore.sql | 22 - .../Data/MSSQL/Resources/006_GridStore.sql | 8 - .../Data/MSSQL/Resources/006_RegionStore.sql | 36 - .../Data/MSSQL/Resources/006_UserStore.sql | 57 -- .../Data/MSSQL/Resources/007_EstateStore.sql | 25 - .../Data/MSSQL/Resources/007_GridStore.sql | 9 - .../Data/MSSQL/Resources/007_RegionStore.sql | 10 - .../Data/MSSQL/Resources/007_UserStore.sql | 42 - .../Data/MSSQL/Resources/008_EstateStore.sql | 49 - .../Data/MSSQL/Resources/008_RegionStore.sql | 7 - .../Data/MSSQL/Resources/008_UserStore.sql | 29 - .../Data/MSSQL/Resources/009_EstateStore.sql | 24 - .../Data/MSSQL/Resources/009_RegionStore.sql | 5 - .../Data/MSSQL/Resources/009_UserStore.sql | 53 - .../Data/MSSQL/Resources/010_RegionStore.sql | 7 - .../Data/MSSQL/Resources/010_UserStore.sql | 24 - .../Data/MSSQL/Resources/011_RegionStore.sql | 6 - .../Data/MSSQL/Resources/011_UserStore.sql | 5 - .../Data/MSSQL/Resources/012_RegionStore.sql | 5 - .../Data/MSSQL/Resources/013_RegionStore.sql | 112 --- .../Data/MSSQL/Resources/014_RegionStore.sql | 49 - .../Data/MSSQL/Resources/015_RegionStore.sql | 45 - .../Data/MSSQL/Resources/016_RegionStore.sql | 19 - .../Data/MSSQL/Resources/017_RegionStore.sql | 56 -- .../Data/MSSQL/Resources/018_RegionStore.sql | 18 - .../Data/MSSQL/Resources/019_RegionStore.sql | 19 - .../Data/MSSQL/Resources/020_RegionStore.sql | 58 -- .../Data/MSSQL/Resources/021_RegionStore.sql | 5 - .../Data/MSSQL/Resources/022_RegionStore.sql | 7 - .../Data/MSSQL/Resources/023_RegionStore.sql | 7 - .../MSSQL/Resources/AssetStore.migrations | 100 ++ ...001_AuthStore.sql => AuthStore.migrations} | 11 + .../{001_Avatar.sql => Avatar.migrations} | 2 + .../MSSQL/Resources/EstateStore.migrations | 334 +++++++ ...iendsStore.sql => FriendsStore.migrations} | 9 + .../Data/MSSQL/Resources/GridStore.migrations | 225 +++++ .../MSSQL/Resources/InventoryStore.migrations | 174 ++++ .../{001_LogStore.sql => LogStore.migrations} | 2 + .../{001_Presence.sql => Presence.migrations} | 11 + .../MSSQL/Resources/RegionStore.migrations | 929 ++++++++++++++++++ .../MSSQL/Resources/UserAccount.migrations | 55 ++ .../Data/MSSQL/Resources/UserStore.migrations | 421 ++++++++ 77 files changed, 2273 insertions(+), 2008 deletions(-) delete mode 100644 OpenSim/Data/MSSQL/Resources/001_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/001_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_AuthStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/002_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/003_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_UserAccount.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/004_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_AssetStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/005_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/006_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_GridStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/007_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/008_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/008_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/008_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/009_EstateStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/009_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/009_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/010_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/010_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/011_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/011_UserStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/012_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/013_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/014_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/015_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/016_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/017_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/018_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/019_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/020_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/021_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/022_RegionStore.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/023_RegionStore.sql create mode 100644 OpenSim/Data/MSSQL/Resources/AssetStore.migrations rename OpenSim/Data/MSSQL/Resources/{001_AuthStore.sql => AuthStore.migrations} (63%) rename OpenSim/Data/MSSQL/Resources/{001_Avatar.sql => Avatar.migrations} (94%) create mode 100644 OpenSim/Data/MSSQL/Resources/EstateStore.migrations rename OpenSim/Data/MSSQL/Resources/{001_FriendsStore.sql => FriendsStore.migrations} (54%) create mode 100644 OpenSim/Data/MSSQL/Resources/GridStore.migrations create mode 100644 OpenSim/Data/MSSQL/Resources/InventoryStore.migrations rename OpenSim/Data/MSSQL/Resources/{001_LogStore.sql => LogStore.migrations} (96%) rename OpenSim/Data/MSSQL/Resources/{001_Presence.sql => Presence.migrations} (81%) create mode 100644 OpenSim/Data/MSSQL/Resources/RegionStore.migrations create mode 100644 OpenSim/Data/MSSQL/Resources/UserAccount.migrations create mode 100644 OpenSim/Data/MSSQL/Resources/UserStore.migrations diff --git a/OpenSim/Data/MSSQL/Resources/001_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/001_AssetStore.sql deleted file mode 100644 index 2b293c7636..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_AssetStore.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE [assets] ( - [id] [varchar](36) NOT NULL, - [name] [varchar](64) NOT NULL, - [description] [varchar](64) NOT NULL, - [assetType] [tinyint] NOT NULL, - [local] [tinyint] NOT NULL, - [temporary] [tinyint] NOT NULL, - [data] [image] NOT NULL, -PRIMARY KEY CLUSTERED -( - [id] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] diff --git a/OpenSim/Data/MSSQL/Resources/001_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/001_EstateStore.sql deleted file mode 100644 index 9bb2f75965..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_EstateStore.sql +++ /dev/null @@ -1,85 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [dbo].[estate_managers]( - [EstateID] [int] NOT NULL, - [uuid] [varchar](36) NOT NULL, - CONSTRAINT [PK_estate_managers] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - -CREATE TABLE [dbo].[estate_groups]( - [EstateID] [int] NOT NULL, - [uuid] [varchar](36) NOT NULL, - CONSTRAINT [PK_estate_groups] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - - -CREATE TABLE [dbo].[estate_users]( - [EstateID] [int] NOT NULL, - [uuid] [varchar](36) NOT NULL, - CONSTRAINT [PK_estate_users] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - - -CREATE TABLE [dbo].[estateban]( - [EstateID] [int] NOT NULL, - [bannedUUID] [varchar](36) NOT NULL, - [bannedIp] [varchar](16) NOT NULL, - [bannedIpHostMask] [varchar](16) NOT NULL, - [bannedNameMask] [varchar](64) NULL DEFAULT (NULL), - CONSTRAINT [PK_estateban] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - -CREATE TABLE [dbo].[estate_settings]( - [EstateID] [int] IDENTITY(1,100) NOT NULL, - [EstateName] [varchar](64) NULL DEFAULT (NULL), - [AbuseEmailToEstateOwner] [bit] NOT NULL, - [DenyAnonymous] [bit] NOT NULL, - [ResetHomeOnTeleport] [bit] NOT NULL, - [FixedSun] [bit] NOT NULL, - [DenyTransacted] [bit] NOT NULL, - [BlockDwell] [bit] NOT NULL, - [DenyIdentified] [bit] NOT NULL, - [AllowVoice] [bit] NOT NULL, - [UseGlobalTime] [bit] NOT NULL, - [PricePerMeter] [int] NOT NULL, - [TaxFree] [bit] NOT NULL, - [AllowDirectTeleport] [bit] NOT NULL, - [RedirectGridX] [int] NOT NULL, - [RedirectGridY] [int] NOT NULL, - [ParentEstateID] [int] NOT NULL, - [SunPosition] [float] NOT NULL, - [EstateSkipScripts] [bit] NOT NULL, - [BillableFactor] [float] NOT NULL, - [PublicAccess] [bit] NOT NULL, - [AbuseEmail] [varchar](255) NOT NULL, - [EstateOwner] [varchar](36) NOT NULL, - [DenyMinors] [bit] NOT NULL, - CONSTRAINT [PK_estate_settings] PRIMARY KEY CLUSTERED -( - [EstateID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - - -CREATE TABLE [dbo].[estate_map]( - [RegionID] [varchar](36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - [EstateID] [int] NOT NULL, - CONSTRAINT [PK_estate_map] PRIMARY KEY CLUSTERED -( - [RegionID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY]; - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/001_GridStore.sql b/OpenSim/Data/MSSQL/Resources/001_GridStore.sql deleted file mode 100644 index ff15f54c07..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_GridStore.sql +++ /dev/null @@ -1,37 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [dbo].[regions]( - [regionHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionName] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [uuid] [varchar](255) COLLATE Latin1_General_CI_AS NOT NULL, - [regionRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionSecret] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionDataURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverIP] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [locX] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [locY] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [locZ] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [eastOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [westOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [southOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [northOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionAssetURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionAssetRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionAssetSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionUserURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionUserRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionUserSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [regionMapTexture] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverHttpPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [serverRemotingPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, - [owner_uuid] [varchar](36) COLLATE Latin1_General_CI_AS NULL, -PRIMARY KEY CLUSTERED -( - [uuid] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql deleted file mode 100644 index 836d2d1e2b..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_InventoryStore.sql +++ /dev/null @@ -1,64 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [inventoryfolders] ( - [folderID] [varchar](36) NOT NULL default '', - [agentID] [varchar](36) default NULL, - [parentFolderID] [varchar](36) default NULL, - [folderName] [varchar](64) default NULL, - [type] [smallint] NOT NULL default 0, - [version] [int] NOT NULL default 0, - PRIMARY KEY CLUSTERED -( - [folderID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [owner] ON [inventoryfolders] -( - [agentID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [parent] ON [inventoryfolders] -( - [parentFolderID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE TABLE [inventoryitems] ( - [inventoryID] [varchar](36) NOT NULL default '', - [assetID] [varchar](36) default NULL, - [assetType] [int] default NULL, - [parentFolderID] [varchar](36) default NULL, - [avatarID] [varchar](36) default NULL, - [inventoryName] [varchar](64) default NULL, - [inventoryDescription] [varchar](128) default NULL, - [inventoryNextPermissions] [int] default NULL, - [inventoryCurrentPermissions] [int] default NULL, - [invType] [int] default NULL, - [creatorID] [varchar](36) default NULL, - [inventoryBasePermissions] [int] NOT NULL default 0, - [inventoryEveryOnePermissions] [int] NOT NULL default 0, - [salePrice] [int] default NULL, - [saleType] [tinyint] default NULL, - [creationDate] [int] default NULL, - [groupID] [varchar](36) default NULL, - [groupOwned] [bit] default NULL, - [flags] [int] default NULL, - PRIMARY KEY CLUSTERED -( - [inventoryID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX [owner] ON [inventoryitems] -( - [avatarID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [folder] ON [inventoryitems] -( - [parentFolderID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/001_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/001_RegionStore.sql deleted file mode 100644 index fe7c58f8ed..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_RegionStore.sql +++ /dev/null @@ -1,161 +0,0 @@ -CREATE TABLE [dbo].[prims]( - [UUID] [varchar](255) NOT NULL, - [RegionUUID] [varchar](255) NULL, - [ParentID] [int] NULL, - [CreationDate] [int] NULL, - [Name] [varchar](255) NULL, - [SceneGroupID] [varchar](255) NULL, - [Text] [varchar](255) NULL, - [Description] [varchar](255) NULL, - [SitName] [varchar](255) NULL, - [TouchName] [varchar](255) NULL, - [ObjectFlags] [int] NULL, - [CreatorID] [varchar](255) NULL, - [OwnerID] [varchar](255) NULL, - [GroupID] [varchar](255) NULL, - [LastOwnerID] [varchar](255) NULL, - [OwnerMask] [int] NULL, - [NextOwnerMask] [int] NULL, - [GroupMask] [int] NULL, - [EveryoneMask] [int] NULL, - [BaseMask] [int] NULL, - [PositionX] [float] NULL, - [PositionY] [float] NULL, - [PositionZ] [float] NULL, - [GroupPositionX] [float] NULL, - [GroupPositionY] [float] NULL, - [GroupPositionZ] [float] NULL, - [VelocityX] [float] NULL, - [VelocityY] [float] NULL, - [VelocityZ] [float] NULL, - [AngularVelocityX] [float] NULL, - [AngularVelocityY] [float] NULL, - [AngularVelocityZ] [float] NULL, - [AccelerationX] [float] NULL, - [AccelerationY] [float] NULL, - [AccelerationZ] [float] NULL, - [RotationX] [float] NULL, - [RotationY] [float] NULL, - [RotationZ] [float] NULL, - [RotationW] [float] NULL, - [SitTargetOffsetX] [float] NULL, - [SitTargetOffsetY] [float] NULL, - [SitTargetOffsetZ] [float] NULL, - [SitTargetOrientW] [float] NULL, - [SitTargetOrientX] [float] NULL, - [SitTargetOrientY] [float] NULL, - [SitTargetOrientZ] [float] NULL, -PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -CREATE TABLE [dbo].[primshapes]( - [UUID] [varchar](255) NOT NULL, - [Shape] [int] NULL, - [ScaleX] [float] NULL, - [ScaleY] [float] NULL, - [ScaleZ] [float] NULL, - [PCode] [int] NULL, - [PathBegin] [int] NULL, - [PathEnd] [int] NULL, - [PathScaleX] [int] NULL, - [PathScaleY] [int] NULL, - [PathShearX] [int] NULL, - [PathShearY] [int] NULL, - [PathSkew] [int] NULL, - [PathCurve] [int] NULL, - [PathRadiusOffset] [int] NULL, - [PathRevolutions] [int] NULL, - [PathTaperX] [int] NULL, - [PathTaperY] [int] NULL, - [PathTwist] [int] NULL, - [PathTwistBegin] [int] NULL, - [ProfileBegin] [int] NULL, - [ProfileEnd] [int] NULL, - [ProfileCurve] [int] NULL, - [ProfileHollow] [int] NULL, - [State] [int] NULL, - [Texture] [image] NULL, - [ExtraParams] [image] NULL, -PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -CREATE TABLE [dbo].[primitems]( - [itemID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [primID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [assetID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [parentFolderID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [invType] [int] NULL, - [assetType] [int] NULL, - [name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [creationDate] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [creatorID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [ownerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [lastOwnerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [groupID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [nextPermissions] [int] NULL, - [currentPermissions] [int] NULL, - [basePermissions] [int] NULL, - [everyonePermissions] [int] NULL, - [groupPermissions] [int] NULL, -PRIMARY KEY CLUSTERED -( - [itemID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -CREATE TABLE [dbo].[terrain]( - [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [Revision] [int] NULL, - [Heightfield] [image] NULL -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -CREATE TABLE [dbo].[land]( - [UUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [LocalLandID] [int] NULL, - [Bitmap] [image] NULL, - [Name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [Description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [OwnerUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [IsGroupOwned] [int] NULL, - [Area] [int] NULL, - [AuctionID] [int] NULL, - [Category] [int] NULL, - [ClaimDate] [int] NULL, - [ClaimPrice] [int] NULL, - [GroupUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [SalePrice] [int] NULL, - [LandStatus] [int] NULL, - [LandFlags] [int] NULL, - [LandingType] [int] NULL, - [MediaAutoScale] [int] NULL, - [MediaTextureUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [MediaURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [MusicURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [PassHours] [float] NULL, - [PassPrice] [int] NULL, - [SnapshotUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [UserLocationX] [float] NULL, - [UserLocationY] [float] NULL, - [UserLocationZ] [float] NULL, - [UserLookAtX] [float] NULL, - [UserLookAtY] [float] NULL, - [UserLookAtZ] [float] NULL, -PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -CREATE TABLE [dbo].[landaccesslist]( - [LandUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [AccessUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, - [Flags] [int] NULL -) ON [PRIMARY] \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/001_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/001_UserAccount.sql deleted file mode 100644 index 3dbf8a4925..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_UserAccount.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE [UserAccounts] ( - [PrincipalID] uniqueidentifier NOT NULL, - [ScopeID] uniqueidentifier NOT NULL, - [FirstName] [varchar](64) NOT NULL, - [LastName] [varchar](64) NOT NULL, - [Email] [varchar](64) NULL, - [ServiceURLs] [text] NULL, - [Created] [int] default NULL, - - PRIMARY KEY CLUSTERED -( - [PrincipalID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] diff --git a/OpenSim/Data/MSSQL/Resources/001_UserStore.sql b/OpenSim/Data/MSSQL/Resources/001_UserStore.sql deleted file mode 100644 index 160c457dad..0000000000 --- a/OpenSim/Data/MSSQL/Resources/001_UserStore.sql +++ /dev/null @@ -1,112 +0,0 @@ -CREATE TABLE [users] ( - [UUID] [varchar](36) NOT NULL default '', - [username] [varchar](32) NOT NULL, - [lastname] [varchar](32) NOT NULL, - [passwordHash] [varchar](32) NOT NULL, - [passwordSalt] [varchar](32) NOT NULL, - [homeRegion] [bigint] default NULL, - [homeLocationX] [float] default NULL, - [homeLocationY] [float] default NULL, - [homeLocationZ] [float] default NULL, - [homeLookAtX] [float] default NULL, - [homeLookAtY] [float] default NULL, - [homeLookAtZ] [float] default NULL, - [created] [int] NOT NULL, - [lastLogin] [int] NOT NULL, - [userInventoryURI] [varchar](255) default NULL, - [userAssetURI] [varchar](255) default NULL, - [profileCanDoMask] [int] default NULL, - [profileWantDoMask] [int] default NULL, - [profileAboutText] [ntext], - [profileFirstText] [ntext], - [profileImage] [varchar](36) default NULL, - [profileFirstImage] [varchar](36) default NULL, - [webLoginKey] [varchar](36) default NULL, - PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX [usernames] ON [users] -( - [username] ASC, - [lastname] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE TABLE [agents] ( - [UUID] [varchar](36) NOT NULL, - [sessionID] [varchar](36) NOT NULL, - [secureSessionID] [varchar](36) NOT NULL, - [agentIP] [varchar](16) NOT NULL, - [agentPort] [int] NOT NULL, - [agentOnline] [tinyint] NOT NULL, - [loginTime] [int] NOT NULL, - [logoutTime] [int] NOT NULL, - [currentRegion] [varchar](36) NOT NULL, - [currentHandle] [bigint] NOT NULL, - [currentPos] [varchar](64) NOT NULL, - PRIMARY KEY CLUSTERED -( - [UUID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX [session] ON [agents] -( - [sessionID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX [ssession] ON [agents] -( - [secureSessionID] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE TABLE [dbo].[userfriends]( - [ownerID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, - [friendID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, - [friendPerms] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, - [datetimestamp] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL -) ON [PRIMARY] - -CREATE TABLE [avatarappearance] ( - [Owner] [varchar](36) NOT NULL, - [Serial] int NOT NULL, - [Visual_Params] [image] NOT NULL, - [Texture] [image] NOT NULL, - [Avatar_Height] float NOT NULL, - [Body_Item] [varchar](36) NOT NULL, - [Body_Asset] [varchar](36) NOT NULL, - [Skin_Item] [varchar](36) NOT NULL, - [Skin_Asset] [varchar](36) NOT NULL, - [Hair_Item] [varchar](36) NOT NULL, - [Hair_Asset] [varchar](36) NOT NULL, - [Eyes_Item] [varchar](36) NOT NULL, - [Eyes_Asset] [varchar](36) NOT NULL, - [Shirt_Item] [varchar](36) NOT NULL, - [Shirt_Asset] [varchar](36) NOT NULL, - [Pants_Item] [varchar](36) NOT NULL, - [Pants_Asset] [varchar](36) NOT NULL, - [Shoes_Item] [varchar](36) NOT NULL, - [Shoes_Asset] [varchar](36) NOT NULL, - [Socks_Item] [varchar](36) NOT NULL, - [Socks_Asset] [varchar](36) NOT NULL, - [Jacket_Item] [varchar](36) NOT NULL, - [Jacket_Asset] [varchar](36) NOT NULL, - [Gloves_Item] [varchar](36) NOT NULL, - [Gloves_Asset] [varchar](36) NOT NULL, - [Undershirt_Item] [varchar](36) NOT NULL, - [Undershirt_Asset] [varchar](36) NOT NULL, - [Underpants_Item] [varchar](36) NOT NULL, - [Underpants_Asset] [varchar](36) NOT NULL, - [Skirt_Item] [varchar](36) NOT NULL, - [Skirt_Asset] [varchar](36) NOT NULL, - - PRIMARY KEY CLUSTERED ( - [Owner] - ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] -) ON [PRIMARY] diff --git a/OpenSim/Data/MSSQL/Resources/002_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/002_AssetStore.sql deleted file mode 100644 index 3e245432bf..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_AssetStore.sql +++ /dev/null @@ -1,29 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_assets - ( - id varchar(36) NOT NULL, - name varchar(64) NOT NULL, - description varchar(64) NOT NULL, - assetType tinyint NOT NULL, - local bit NOT NULL, - temporary bit NOT NULL, - data image NOT NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM assets) - EXEC('INSERT INTO Tmp_assets (id, name, description, assetType, local, temporary, data) - SELECT id, name, description, assetType, CONVERT(bit, local), CONVERT(bit, temporary), data FROM assets WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE assets - -EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' - -ALTER TABLE dbo.assets ADD CONSTRAINT - PK__assets__id PRIMARY KEY CLUSTERED - ( - id - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/002_AuthStore.sql b/OpenSim/Data/MSSQL/Resources/002_AuthStore.sql deleted file mode 100644 index daed955932..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_AuthStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey, accountType) SELECT [UUID] AS UUID, [passwordHash] AS passwordHash, [passwordSalt] AS passwordSalt, [webLoginKey] AS webLoginKey, 'UserAccount' as [accountType] FROM users; - - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/002_EstateStore.sql deleted file mode 100644 index 18c12c097c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_EstateStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE dbo.estate_managers DROP CONSTRAINT PK_estate_managers - -CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -ALTER TABLE dbo.estate_groups DROP CONSTRAINT PK_estate_groups - -CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -ALTER TABLE dbo.estate_users DROP CONSTRAINT PK_estate_users - -CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql b/OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql deleted file mode 100644 index e67d20e4b7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_FriendsStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -INSERT INTO Friends (PrincipalID, Friend, Flags, Offered) SELECT [ownerID], [friendID], [friendPerms], 0 FROM userfriends; - - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_GridStore.sql b/OpenSim/Data/MSSQL/Resources/002_GridStore.sql deleted file mode 100644 index f5901cb527..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_GridStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_regions - ( - uuid varchar(36) COLLATE Latin1_General_CI_AS NOT NULL, - regionHandle bigint NULL, - regionName varchar(20) NULL, - regionRecvKey varchar(128) NULL, - regionSendKey varchar(128) NULL, - regionSecret varchar(128) NULL, - regionDataURI varchar(128) NULL, - serverIP varchar(64) NULL, - serverPort int NULL, - serverURI varchar(255) NULL, - locX int NULL, - locY int NULL, - locZ int NULL, - eastOverrideHandle bigint NULL, - westOverrideHandle bigint NULL, - southOverrideHandle bigint NULL, - northOverrideHandle bigint NULL, - regionAssetURI varchar(255) NULL, - regionAssetRecvKey varchar(128) NULL, - regionAssetSendKey varchar(128) NULL, - regionUserURI varchar(255) NULL, - regionUserRecvKey varchar(128) NULL, - regionUserSendKey varchar(128) NULL, - regionMapTexture varchar(36) NULL, - serverHttpPort int NULL, - serverRemotingPort int NULL, - owner_uuid varchar(36) NULL, - originUUID varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM regions) - EXEC('INSERT INTO Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid) - SELECT CONVERT(varchar(36), uuid), CONVERT(bigint, regionHandle), CONVERT(varchar(20), regionName), CONVERT(varchar(128), regionRecvKey), CONVERT(varchar(128), regionSendKey), CONVERT(varchar(128), regionSecret), CONVERT(varchar(128), regionDataURI), CONVERT(varchar(64), serverIP), CONVERT(int, serverPort), serverURI, CONVERT(int, locX), CONVERT(int, locY), CONVERT(int, locZ), CONVERT(bigint, eastOverrideHandle), CONVERT(bigint, westOverrideHandle), CONVERT(bigint, southOverrideHandle), CONVERT(bigint, northOverrideHandle), regionAssetURI, CONVERT(varchar(128), regionAssetRecvKey), CONVERT(varchar(128), regionAssetSendKey), regionUserURI, CONVERT(varchar(128), regionUserRecvKey), CONVERT(varchar(128), regionUserSendKey), CONVERT(varchar(36), regionMapTexture), CONVERT(int, serverHttpPort), CONVERT(int, serverRemotingPort), owner_uuid FROM regions') - -DROP TABLE regions - -EXECUTE sp_rename N'Tmp_regions', N'regions', 'OBJECT' - -ALTER TABLE regions ADD CONSTRAINT - PK__regions__uuid PRIMARY KEY CLUSTERED - ( - uuid - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql deleted file mode 100644 index bcc26b88c8..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_InventoryStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE inventoryitems ADD inventoryGroupPermissions INTEGER NOT NULL default 0 - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/002_RegionStore.sql deleted file mode 100644 index 1801035206..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_RegionStore.sql +++ /dev/null @@ -1,50 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE regionban ( - [regionUUID] VARCHAR(36) NOT NULL, - [bannedUUID] VARCHAR(36) NOT NULL, - [bannedIp] VARCHAR(16) NOT NULL, - [bannedIpHostMask] VARCHAR(16) NOT NULL) - -create table [dbo].[regionsettings] ( - [regionUUID] [varchar](36) not null, - [block_terraform] [bit] not null, - [block_fly] [bit] not null, - [allow_damage] [bit] not null, - [restrict_pushing] [bit] not null, - [allow_land_resell] [bit] not null, - [allow_land_join_divide] [bit] not null, - [block_show_in_search] [bit] not null, - [agent_limit] [int] not null, - [object_bonus] [float] not null, - [maturity] [int] not null, - [disable_scripts] [bit] not null, - [disable_collisions] [bit] not null, - [disable_physics] [bit] not null, - [terrain_texture_1] [varchar](36) not null, - [terrain_texture_2] [varchar](36) not null, - [terrain_texture_3] [varchar](36) not null, - [terrain_texture_4] [varchar](36) not null, - [elevation_1_nw] [float] not null, - [elevation_2_nw] [float] not null, - [elevation_1_ne] [float] not null, - [elevation_2_ne] [float] not null, - [elevation_1_se] [float] not null, - [elevation_2_se] [float] not null, - [elevation_1_sw] [float] not null, - [elevation_2_sw] [float] not null, - [water_height] [float] not null, - [terrain_raise_limit] [float] not null, - [terrain_lower_limit] [float] not null, - [use_estate_sun] [bit] not null, - [fixed_sun] [bit] not null, - [sun_position] [float] not null, - [covenant] [varchar](36) default NULL, - [Sandbox] [bit] NOT NULL, -PRIMARY KEY CLUSTERED -( - [regionUUID] ASC -)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] -) ON [PRIMARY] - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/002_UserAccount.sql deleted file mode 100644 index 89d1f3495a..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_UserAccount.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN TRANSACTION - -INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT [UUID] AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, -username AS FirstName, -lastname AS LastName, -email as Email, ( -'AssetServerURI=' + -userAssetURI + ' InventoryServerURI=' + userInventoryURI + ' GatewayURI= HomeURI=') AS ServiceURLs, -created as Created FROM users; - - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/002_UserStore.sql b/OpenSim/Data/MSSQL/Resources/002_UserStore.sql deleted file mode 100644 index 402eddf15c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/002_UserStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE users ADD homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE users ADD userFlags int NOT NULL default 0; -ALTER TABLE users ADD godLevel int NOT NULL default 0; -ALTER TABLE users ADD customType varchar(32) not null default ''; -ALTER TABLE users ADD partner varchar(36) not null default '00000000-0000-0000-0000-000000000000'; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/003_AssetStore.sql deleted file mode 100644 index 1434330739..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_AssetStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE assets add create_time integer default 0 -ALTER TABLE assets add access_time integer default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/003_EstateStore.sql deleted file mode 100644 index 120966ae1d..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_EstateStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estateban - ( - EstateID int NOT NULL, - bannedUUID varchar(36) NOT NULL, - bannedIp varchar(16) NULL, - bannedIpHostMask varchar(16) NULL, - bannedNameMask varchar(64) NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estateban) - EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) - SELECT EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban') - -DROP TABLE dbo.estateban - -EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_GridStore.sql b/OpenSim/Data/MSSQL/Resources/003_GridStore.sql deleted file mode 100644 index e080947665..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_GridStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions - ( - regionName - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions - ( - regionHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions - ( - eastOverrideHandle, - westOverrideHandle, - southOverrideHandle, - northOverrideHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql deleted file mode 100644 index 2f623ec911..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_InventoryStore.sql +++ /dev/null @@ -1,38 +0,0 @@ -/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_inventoryfolders - ( - folderID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - agentID uniqueidentifier NULL DEFAULT (NULL), - parentFolderID uniqueidentifier NULL DEFAULT (NULL), - folderName varchar(64) NULL DEFAULT (NULL), - type smallint NOT NULL DEFAULT ((0)), - version int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.inventoryfolders) - EXEC('INSERT INTO dbo.Tmp_inventoryfolders (folderID, agentID, parentFolderID, folderName, type, version) - SELECT CONVERT(uniqueidentifier, folderID), CONVERT(uniqueidentifier, agentID), CONVERT(uniqueidentifier, parentFolderID), folderName, type, version FROM dbo.inventoryfolders WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.inventoryfolders - -EXECUTE sp_rename N'dbo.Tmp_inventoryfolders', N'inventoryfolders', 'OBJECT' - -ALTER TABLE dbo.inventoryfolders ADD CONSTRAINT - PK__inventor__C2FABFB3173876EA PRIMARY KEY CLUSTERED - ( - folderID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX owner ON dbo.inventoryfolders - ( - agentID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX parent ON dbo.inventoryfolders - ( - parentFolderID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/003_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/003_RegionStore.sql deleted file mode 100644 index a8f40c2a86..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_RegionStore.sql +++ /dev/null @@ -1,67 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_prims - ( - UUID varchar(36) NOT NULL, - RegionUUID varchar(36) NULL, - ParentID int NULL, - CreationDate int NULL, - Name varchar(255) NULL, - SceneGroupID varchar(36) NULL, - Text varchar(255) NULL, - Description varchar(255) NULL, - SitName varchar(255) NULL, - TouchName varchar(255) NULL, - ObjectFlags int NULL, - CreatorID varchar(36) NULL, - OwnerID varchar(36) NULL, - GroupID varchar(36) NULL, - LastOwnerID varchar(36) NULL, - OwnerMask int NULL, - NextOwnerMask int NULL, - GroupMask int NULL, - EveryoneMask int NULL, - BaseMask int NULL, - PositionX float(53) NULL, - PositionY float(53) NULL, - PositionZ float(53) NULL, - GroupPositionX float(53) NULL, - GroupPositionY float(53) NULL, - GroupPositionZ float(53) NULL, - VelocityX float(53) NULL, - VelocityY float(53) NULL, - VelocityZ float(53) NULL, - AngularVelocityX float(53) NULL, - AngularVelocityY float(53) NULL, - AngularVelocityZ float(53) NULL, - AccelerationX float(53) NULL, - AccelerationY float(53) NULL, - AccelerationZ float(53) NULL, - RotationX float(53) NULL, - RotationY float(53) NULL, - RotationZ float(53) NULL, - RotationW float(53) NULL, - SitTargetOffsetX float(53) NULL, - SitTargetOffsetY float(53) NULL, - SitTargetOffsetZ float(53) NULL, - SitTargetOrientW float(53) NULL, - SitTargetOrientX float(53) NULL, - SitTargetOrientY float(53) NULL, - SitTargetOrientZ float(53) NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.prims) - EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ) - SELECT CONVERT(varchar(36), UUID), CONVERT(varchar(36), RegionUUID), ParentID, CreationDate, Name, CONVERT(varchar(36), SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(varchar(36), CreatorID), CONVERT(varchar(36), OwnerID), CONVERT(varchar(36), GroupID), CONVERT(varchar(36), LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.prims - -EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' - -ALTER TABLE dbo.prims ADD CONSTRAINT - PK__prims__10566F31 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/003_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/003_UserAccount.sql deleted file mode 100644 index da0395b49c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_UserAccount.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN TRANSACTION - -CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); -CREATE INDEX Email ON UserAccounts(Email); -CREATE INDEX FirstName ON UserAccounts(FirstName); -CREATE INDEX LastName ON UserAccounts(LastName); -CREATE INDEX Name ON UserAccounts(FirstName,LastName); - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/003_UserStore.sql b/OpenSim/Data/MSSQL/Resources/003_UserStore.sql deleted file mode 100644 index cb507c9630..0000000000 --- a/OpenSim/Data/MSSQL/Resources/003_UserStore.sql +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [avatarattachments] ( - [UUID] varchar(36) NOT NULL - , [attachpoint] int NOT NULL - , [item] varchar(36) NOT NULL - , [asset] varchar(36) NOT NULL) - -CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/004_AssetStore.sql deleted file mode 100644 index 215cf3a14e..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_AssetStore.sql +++ /dev/null @@ -1,31 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_assets - ( - id uniqueidentifier NOT NULL, - name varchar(64) NOT NULL, - description varchar(64) NOT NULL, - assetType tinyint NOT NULL, - local bit NOT NULL, - temporary bit NOT NULL, - data image NOT NULL, - create_time int NULL, - access_time int NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.assets) - EXEC('INSERT INTO dbo.Tmp_assets (id, name, description, assetType, local, temporary, data, create_time, access_time) - SELECT CONVERT(uniqueidentifier, id), name, description, assetType, local, temporary, data, create_time, access_time FROM dbo.assets WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE assets - -EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' - -ALTER TABLE dbo.assets ADD CONSTRAINT - PK__assets__id PRIMARY KEY CLUSTERED - ( - id - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/004_EstateStore.sql deleted file mode 100644 index 0a132c110e..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_EstateStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_managers - ( - EstateID int NOT NULL, - uuid uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_managers) - EXEC('INSERT INTO dbo.Tmp_estate_managers (EstateID, uuid) - SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_managers WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_managers - -EXECUTE sp_rename N'dbo.Tmp_estate_managers', N'estate_managers', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_GridStore.sql b/OpenSim/Data/MSSQL/Resources/004_GridStore.sql deleted file mode 100644 index 6456c95f83..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_GridStore.sql +++ /dev/null @@ -1,68 +0,0 @@ -/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_regions - ( - uuid uniqueidentifier NOT NULL, - regionHandle bigint NULL, - regionName varchar(20) NULL, - regionRecvKey varchar(128) NULL, - regionSendKey varchar(128) NULL, - regionSecret varchar(128) NULL, - regionDataURI varchar(128) NULL, - serverIP varchar(64) NULL, - serverPort int NULL, - serverURI varchar(255) NULL, - locX int NULL, - locY int NULL, - locZ int NULL, - eastOverrideHandle bigint NULL, - westOverrideHandle bigint NULL, - southOverrideHandle bigint NULL, - northOverrideHandle bigint NULL, - regionAssetURI varchar(255) NULL, - regionAssetRecvKey varchar(128) NULL, - regionAssetSendKey varchar(128) NULL, - regionUserURI varchar(255) NULL, - regionUserRecvKey varchar(128) NULL, - regionUserSendKey varchar(128) NULL, - regionMapTexture uniqueidentifier NULL, - serverHttpPort int NULL, - serverRemotingPort int NULL, - owner_uuid uniqueidentifier NOT NULL, - originUUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.regions) - EXEC('INSERT INTO dbo.Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid, originUUID) - SELECT CONVERT(uniqueidentifier, uuid), regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, CONVERT(uniqueidentifier, regionMapTexture), serverHttpPort, serverRemotingPort, CONVERT(uniqueidentifier, owner_uuid), CONVERT(uniqueidentifier, originUUID) FROM dbo.regions WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.regions - -EXECUTE sp_rename N'dbo.Tmp_regions', N'regions', 'OBJECT' - -ALTER TABLE dbo.regions ADD CONSTRAINT - PK__regions__uuid PRIMARY KEY CLUSTERED - ( - uuid - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions - ( - regionName - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions - ( - regionHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions - ( - eastOverrideHandle, - westOverrideHandle, - southOverrideHandle, - northOverrideHandle - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql b/OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql deleted file mode 100644 index 96ef1c0c90..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_InventoryStore.sql +++ /dev/null @@ -1,52 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_inventoryitems - ( - inventoryID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - assetID uniqueidentifier NULL DEFAULT (NULL), - assetType int NULL DEFAULT (NULL), - parentFolderID uniqueidentifier NULL DEFAULT (NULL), - avatarID uniqueidentifier NULL DEFAULT (NULL), - inventoryName varchar(64) NULL DEFAULT (NULL), - inventoryDescription varchar(128) NULL DEFAULT (NULL), - inventoryNextPermissions int NULL DEFAULT (NULL), - inventoryCurrentPermissions int NULL DEFAULT (NULL), - invType int NULL DEFAULT (NULL), - creatorID uniqueidentifier NULL DEFAULT (NULL), - inventoryBasePermissions int NOT NULL DEFAULT ((0)), - inventoryEveryOnePermissions int NOT NULL DEFAULT ((0)), - salePrice int NULL DEFAULT (NULL), - saleType tinyint NULL DEFAULT (NULL), - creationDate int NULL DEFAULT (NULL), - groupID uniqueidentifier NULL DEFAULT (NULL), - groupOwned bit NULL DEFAULT (NULL), - flags int NULL DEFAULT (NULL), - inventoryGroupPermissions int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.inventoryitems) - EXEC('INSERT INTO dbo.Tmp_inventoryitems (inventoryID, assetID, assetType, parentFolderID, avatarID, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryGroupPermissions) - SELECT CONVERT(uniqueidentifier, inventoryID), CONVERT(uniqueidentifier, assetID), assetType, CONVERT(uniqueidentifier, parentFolderID), CONVERT(uniqueidentifier, avatarID), inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, CONVERT(uniqueidentifier, creatorID), inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, CONVERT(uniqueidentifier, groupID), groupOwned, flags, inventoryGroupPermissions FROM dbo.inventoryitems WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.inventoryitems - -EXECUTE sp_rename N'dbo.Tmp_inventoryitems', N'inventoryitems', 'OBJECT' - -ALTER TABLE dbo.inventoryitems ADD CONSTRAINT - PK__inventor__C4B7BC2220C1E124 PRIMARY KEY CLUSTERED - ( - inventoryID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX owner ON dbo.inventoryitems - ( - avatarID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX folder ON dbo.inventoryitems - ( - parentFolderID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/004_RegionStore.sql deleted file mode 100644 index 15b39a7fcf..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_RegionStore.sql +++ /dev/null @@ -1,40 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_primitems - ( - itemID varchar(36) NOT NULL, - primID varchar(36) NULL, - assetID varchar(36) NULL, - parentFolderID varchar(36) NULL, - invType int NULL, - assetType int NULL, - name varchar(255) NULL, - description varchar(255) NULL, - creationDate varchar(255) NULL, - creatorID varchar(36) NULL, - ownerID varchar(36) NULL, - lastOwnerID varchar(36) NULL, - groupID varchar(36) NULL, - nextPermissions int NULL, - currentPermissions int NULL, - basePermissions int NULL, - everyonePermissions int NULL, - groupPermissions int NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM primitems) - EXEC('INSERT INTO Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions) - SELECT CONVERT(varchar(36), itemID), CONVERT(varchar(36), primID), CONVERT(varchar(36), assetID), CONVERT(varchar(36), parentFolderID), invType, assetType, name, description, creationDate, CONVERT(varchar(36), creatorID), CONVERT(varchar(36), ownerID), CONVERT(varchar(36), lastOwnerID), CONVERT(varchar(36), groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions') - -DROP TABLE primitems - -EXECUTE sp_rename N'Tmp_primitems', N'primitems', 'OBJECT' - -ALTER TABLE primitems ADD CONSTRAINT - PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED - ( - itemID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/004_UserAccount.sql b/OpenSim/Data/MSSQL/Resources/004_UserAccount.sql deleted file mode 100644 index a9a9021cc7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_UserAccount.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE UserAccounts ADD UserLevel integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD UserFlags integer NOT NULL DEFAULT 0; -ALTER TABLE UserAccounts ADD UserTitle varchar(64) NOT NULL DEFAULT ''; - -COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/004_UserStore.sql b/OpenSim/Data/MSSQL/Resources/004_UserStore.sql deleted file mode 100644 index 08f1a1d182..0000000000 --- a/OpenSim/Data/MSSQL/Resources/004_UserStore.sql +++ /dev/null @@ -1,29 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_userfriends - ( - ownerID varchar(36) NOT NULL, - friendID varchar(36) NOT NULL, - friendPerms int NOT NULL, - datetimestamp int NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM userfriends) - EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) - SELECT CONVERT(varchar(36), ownerID), CONVERT(varchar(36), friendID), CONVERT(int, friendPerms), CONVERT(int, datetimestamp) FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.userfriends - -EXECUTE sp_rename N'Tmp_userfriends', N'userfriends', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON userfriends - ( - ownerID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON userfriends - ( - friendID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_AssetStore.sql b/OpenSim/Data/MSSQL/Resources/005_AssetStore.sql deleted file mode 100644 index 4e95b2b693..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_AssetStore.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; diff --git a/OpenSim/Data/MSSQL/Resources/005_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/005_EstateStore.sql deleted file mode 100644 index ba93b39ff4..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_EstateStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_groups - ( - EstateID int NOT NULL, - uuid uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_groups) - EXEC('INSERT INTO dbo.Tmp_estate_groups (EstateID, uuid) - SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_groups WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_groups - -EXECUTE sp_rename N'dbo.Tmp_estate_groups', N'estate_groups', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_GridStore.sql b/OpenSim/Data/MSSQL/Resources/005_GridStore.sql deleted file mode 100644 index aa04a33019..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_GridStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regions ADD access int default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/005_RegionStore.sql deleted file mode 100644 index eb0862c9bd..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_RegionStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE Tmp_primshapes - ( - UUID varchar(36) NOT NULL, - Shape int NULL, - ScaleX float(53) NULL, - ScaleY float(53) NULL, - ScaleZ float(53) NULL, - PCode int NULL, - PathBegin int NULL, - PathEnd int NULL, - PathScaleX int NULL, - PathScaleY int NULL, - PathShearX int NULL, - PathShearY int NULL, - PathSkew int NULL, - PathCurve int NULL, - PathRadiusOffset int NULL, - PathRevolutions int NULL, - PathTaperX int NULL, - PathTaperY int NULL, - PathTwist int NULL, - PathTwistBegin int NULL, - ProfileBegin int NULL, - ProfileEnd int NULL, - ProfileCurve int NULL, - ProfileHollow int NULL, - State int NULL, - Texture image NULL, - ExtraParams image NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM primshapes) - EXEC('INSERT INTO Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) - SELECT CONVERT(varchar(36), UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM primshapes WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE primshapes - -EXECUTE sp_rename N'Tmp_primshapes', N'primshapes', 'OBJECT' - -ALTER TABLE primshapes ADD CONSTRAINT - PK__primshapes__0880433F PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/005_UserStore.sql b/OpenSim/Data/MSSQL/Resources/005_UserStore.sql deleted file mode 100644 index 1b6ab8f7da..0000000000 --- a/OpenSim/Data/MSSQL/Resources/005_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - - ALTER TABLE users add email varchar(250); - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/006_EstateStore.sql deleted file mode 100644 index f7df8fda18..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_EstateStore.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_users - ( - EstateID int NOT NULL, - uuid uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_users) - EXEC('INSERT INTO dbo.Tmp_estate_users (EstateID, uuid) - SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_users WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_users - -EXECUTE sp_rename N'dbo.Tmp_estate_users', N'estate_users', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_GridStore.sql b/OpenSim/Data/MSSQL/Resources/006_GridStore.sql deleted file mode 100644 index 42010ce657..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_GridStore.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regions ADD scopeid uniqueidentifier default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE regions ADD DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [owner_uuid]; -ALTER TABLE regions ADD sizeX integer not null default 0; -ALTER TABLE regions ADD sizeY integer not null default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/006_RegionStore.sql deleted file mode 100644 index 0419c0c8a6..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_RegionStore.sql +++ /dev/null @@ -1,36 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD PayPrice int not null default 0 -ALTER TABLE prims ADD PayButton1 int not null default 0 -ALTER TABLE prims ADD PayButton2 int not null default 0 -ALTER TABLE prims ADD PayButton3 int not null default 0 -ALTER TABLE prims ADD PayButton4 int not null default 0 -ALTER TABLE prims ADD LoopedSound varchar(36) not null default '00000000-0000-0000-0000-000000000000'; -ALTER TABLE prims ADD LoopedSoundGain float not null default 0.0; -ALTER TABLE prims ADD TextureAnimation image -ALTER TABLE prims ADD OmegaX float not null default 0.0 -ALTER TABLE prims ADD OmegaY float not null default 0.0 -ALTER TABLE prims ADD OmegaZ float not null default 0.0 -ALTER TABLE prims ADD CameraEyeOffsetX float not null default 0.0 -ALTER TABLE prims ADD CameraEyeOffsetY float not null default 0.0 -ALTER TABLE prims ADD CameraEyeOffsetZ float not null default 0.0 -ALTER TABLE prims ADD CameraAtOffsetX float not null default 0.0 -ALTER TABLE prims ADD CameraAtOffsetY float not null default 0.0 -ALTER TABLE prims ADD CameraAtOffsetZ float not null default 0.0 -ALTER TABLE prims ADD ForceMouselook tinyint not null default 0 -ALTER TABLE prims ADD ScriptAccessPin int not null default 0 -ALTER TABLE prims ADD AllowedDrop tinyint not null default 0 -ALTER TABLE prims ADD DieAtEdge tinyint not null default 0 -ALTER TABLE prims ADD SalePrice int not null default 10 -ALTER TABLE prims ADD SaleType tinyint not null default 0 - -ALTER TABLE primitems add flags integer not null default 0 - -ALTER TABLE land ADD AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000' - -CREATE index prims_regionuuid on prims(RegionUUID) -CREATE index prims_parentid on prims(ParentID) - -CREATE index primitems_primid on primitems(primID) - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/006_UserStore.sql b/OpenSim/Data/MSSQL/Resources/006_UserStore.sql deleted file mode 100644 index 67fe5818a8..0000000000 --- a/OpenSim/Data/MSSQL/Resources/006_UserStore.sql +++ /dev/null @@ -1,57 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_users - ( - UUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - username varchar(32) NOT NULL, - lastname varchar(32) NOT NULL, - passwordHash varchar(32) NOT NULL, - passwordSalt varchar(32) NOT NULL, - homeRegion bigint NULL DEFAULT (NULL), - homeLocationX float(53) NULL DEFAULT (NULL), - homeLocationY float(53) NULL DEFAULT (NULL), - homeLocationZ float(53) NULL DEFAULT (NULL), - homeLookAtX float(53) NULL DEFAULT (NULL), - homeLookAtY float(53) NULL DEFAULT (NULL), - homeLookAtZ float(53) NULL DEFAULT (NULL), - created int NOT NULL, - lastLogin int NOT NULL, - userInventoryURI varchar(255) NULL DEFAULT (NULL), - userAssetURI varchar(255) NULL DEFAULT (NULL), - profileCanDoMask int NULL DEFAULT (NULL), - profileWantDoMask int NULL DEFAULT (NULL), - profileAboutText ntext NULL, - profileFirstText ntext NULL, - profileImage uniqueidentifier NULL DEFAULT (NULL), - profileFirstImage uniqueidentifier NULL DEFAULT (NULL), - webLoginKey uniqueidentifier NULL DEFAULT (NULL), - homeRegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - userFlags int NOT NULL DEFAULT ((0)), - godLevel int NOT NULL DEFAULT ((0)), - customType varchar(32) NOT NULL DEFAULT (''), - partner uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - email varchar(250) NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.users) - EXEC('INSERT INTO dbo.Tmp_users (UUID, username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, profileImage, profileFirstImage, webLoginKey, homeRegionID, userFlags, godLevel, customType, partner, email) - SELECT CONVERT(uniqueidentifier, UUID), username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, CONVERT(uniqueidentifier, profileImage), CONVERT(uniqueidentifier, profileFirstImage), CONVERT(uniqueidentifier, webLoginKey), CONVERT(uniqueidentifier, homeRegionID), userFlags, godLevel, customType, CONVERT(uniqueidentifier, partner), email FROM dbo.users WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.users - -EXECUTE sp_rename N'dbo.Tmp_users', N'users', 'OBJECT' - -ALTER TABLE dbo.users ADD CONSTRAINT - PK__users__65A475E737A5467C PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX usernames ON dbo.users - ( - username, - lastname - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/007_EstateStore.sql deleted file mode 100644 index c9165b0c6d..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_EstateStore.sql +++ /dev/null @@ -1,25 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estateban - ( - EstateID int NOT NULL, - bannedUUID uniqueidentifier NOT NULL, - bannedIp varchar(16) NULL, - bannedIpHostMask varchar(16) NULL, - bannedNameMask varchar(64) NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estateban) - EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) - SELECT EstateID, CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estateban - -EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_GridStore.sql b/OpenSim/Data/MSSQL/Resources/007_GridStore.sql deleted file mode 100644 index 0b66d40b47..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_GridStore.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regions ADD [flags] integer NOT NULL DEFAULT 0; -CREATE INDEX [flags] ON regions(flags); -ALTER TABLE [regions] ADD [last_seen] integer NOT NULL DEFAULT 0; -ALTER TABLE [regions] ADD [PrincipalID] uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; -ALTER TABLE [regions] ADD [Token] varchar(255) NOT NULL DEFAULT 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/007_RegionStore.sql deleted file mode 100644 index 684f937334..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_RegionStore.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD ColorR int not null default 0; -ALTER TABLE prims ADD ColorG int not null default 0; -ALTER TABLE prims ADD ColorB int not null default 0; -ALTER TABLE prims ADD ColorA int not null default 0; -ALTER TABLE prims ADD ParticleSystem IMAGE; -ALTER TABLE prims ADD ClickAction tinyint NOT NULL default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/007_UserStore.sql b/OpenSim/Data/MSSQL/Resources/007_UserStore.sql deleted file mode 100644 index 92a8fc572a..0000000000 --- a/OpenSim/Data/MSSQL/Resources/007_UserStore.sql +++ /dev/null @@ -1,42 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_agents - ( - UUID uniqueidentifier NOT NULL, - sessionID uniqueidentifier NOT NULL, - secureSessionID uniqueidentifier NOT NULL, - agentIP varchar(16) NOT NULL, - agentPort int NOT NULL, - agentOnline tinyint NOT NULL, - loginTime int NOT NULL, - logoutTime int NOT NULL, - currentRegion uniqueidentifier NOT NULL, - currentHandle bigint NOT NULL, - currentPos varchar(64) NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.agents) - EXEC('INSERT INTO dbo.Tmp_agents (UUID, sessionID, secureSessionID, agentIP, agentPort, agentOnline, loginTime, logoutTime, currentRegion, currentHandle, currentPos) - SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, sessionID), CONVERT(uniqueidentifier, secureSessionID), agentIP, agentPort, agentOnline, loginTime, logoutTime, CONVERT(uniqueidentifier, currentRegion), currentHandle, currentPos FROM dbo.agents WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.agents - -EXECUTE sp_rename N'dbo.Tmp_agents', N'agents', 'OBJECT' - -ALTER TABLE dbo.agents ADD CONSTRAINT - PK__agents__65A475E749C3F6B7 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX session ON dbo.agents - ( - sessionID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX ssession ON dbo.agents - ( - secureSessionID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/008_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/008_EstateStore.sql deleted file mode 100644 index 9c5355eac7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/008_EstateStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_settings - ( - EstateID int NOT NULL IDENTITY (1, 100), - EstateName varchar(64) NULL DEFAULT (NULL), - AbuseEmailToEstateOwner bit NOT NULL, - DenyAnonymous bit NOT NULL, - ResetHomeOnTeleport bit NOT NULL, - FixedSun bit NOT NULL, - DenyTransacted bit NOT NULL, - BlockDwell bit NOT NULL, - DenyIdentified bit NOT NULL, - AllowVoice bit NOT NULL, - UseGlobalTime bit NOT NULL, - PricePerMeter int NOT NULL, - TaxFree bit NOT NULL, - AllowDirectTeleport bit NOT NULL, - RedirectGridX int NOT NULL, - RedirectGridY int NOT NULL, - ParentEstateID int NOT NULL, - SunPosition float(53) NOT NULL, - EstateSkipScripts bit NOT NULL, - BillableFactor float(53) NOT NULL, - PublicAccess bit NOT NULL, - AbuseEmail varchar(255) NOT NULL, - EstateOwner uniqueidentifier NOT NULL, - DenyMinors bit NOT NULL - ) ON [PRIMARY] - -SET IDENTITY_INSERT dbo.Tmp_estate_settings ON - -IF EXISTS(SELECT * FROM dbo.estate_settings) - EXEC('INSERT INTO dbo.Tmp_estate_settings (EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, EstateOwner, DenyMinors) - SELECT EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, CONVERT(uniqueidentifier, EstateOwner), DenyMinors FROM dbo.estate_settings WITH (HOLDLOCK TABLOCKX)') - -SET IDENTITY_INSERT dbo.Tmp_estate_settings OFF - -DROP TABLE dbo.estate_settings - -EXECUTE sp_rename N'dbo.Tmp_estate_settings', N'estate_settings', 'OBJECT' - -ALTER TABLE dbo.estate_settings ADD CONSTRAINT - PK_estate_settings PRIMARY KEY CLUSTERED - ( - EstateID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/008_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/008_RegionStore.sql deleted file mode 100644 index 87d6d80005..0000000000 --- a/OpenSim/Data/MSSQL/Resources/008_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE land ADD OtherCleanTime integer NOT NULL default 0; -ALTER TABLE land ADD Dwell integer NOT NULL default 0; - -COMMIT - diff --git a/OpenSim/Data/MSSQL/Resources/008_UserStore.sql b/OpenSim/Data/MSSQL/Resources/008_UserStore.sql deleted file mode 100644 index 505252ba42..0000000000 --- a/OpenSim/Data/MSSQL/Resources/008_UserStore.sql +++ /dev/null @@ -1,29 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_userfriends - ( - ownerID uniqueidentifier NOT NULL, - friendID uniqueidentifier NOT NULL, - friendPerms int NOT NULL, - datetimestamp int NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.userfriends) - EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) - SELECT CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, friendID), friendPerms, datetimestamp FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.userfriends - -EXECUTE sp_rename N'dbo.Tmp_userfriends', N'userfriends', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON dbo.userfriends - ( - ownerID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON dbo.userfriends - ( - friendID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/009_EstateStore.sql b/OpenSim/Data/MSSQL/Resources/009_EstateStore.sql deleted file mode 100644 index f91557c805..0000000000 --- a/OpenSim/Data/MSSQL/Resources/009_EstateStore.sql +++ /dev/null @@ -1,24 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_estate_map - ( - RegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - EstateID int NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.estate_map) - EXEC('INSERT INTO dbo.Tmp_estate_map (RegionID, EstateID) - SELECT CONVERT(uniqueidentifier, RegionID), EstateID FROM dbo.estate_map WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.estate_map - -EXECUTE sp_rename N'dbo.Tmp_estate_map', N'estate_map', 'OBJECT' - -ALTER TABLE dbo.estate_map ADD CONSTRAINT - PK_estate_map PRIMARY KEY CLUSTERED - ( - RegionID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/009_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/009_RegionStore.sql deleted file mode 100644 index 4ef3b3f125..0000000000 --- a/OpenSim/Data/MSSQL/Resources/009_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD Material tinyint NOT NULL default 3 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/009_UserStore.sql b/OpenSim/Data/MSSQL/Resources/009_UserStore.sql deleted file mode 100644 index b1ab8ba69b..0000000000 --- a/OpenSim/Data/MSSQL/Resources/009_UserStore.sql +++ /dev/null @@ -1,53 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_avatarappearance - ( - Owner uniqueidentifier NOT NULL, - Serial int NOT NULL, - Visual_Params image NOT NULL, - Texture image NOT NULL, - Avatar_Height float(53) NOT NULL, - Body_Item uniqueidentifier NOT NULL, - Body_Asset uniqueidentifier NOT NULL, - Skin_Item uniqueidentifier NOT NULL, - Skin_Asset uniqueidentifier NOT NULL, - Hair_Item uniqueidentifier NOT NULL, - Hair_Asset uniqueidentifier NOT NULL, - Eyes_Item uniqueidentifier NOT NULL, - Eyes_Asset uniqueidentifier NOT NULL, - Shirt_Item uniqueidentifier NOT NULL, - Shirt_Asset uniqueidentifier NOT NULL, - Pants_Item uniqueidentifier NOT NULL, - Pants_Asset uniqueidentifier NOT NULL, - Shoes_Item uniqueidentifier NOT NULL, - Shoes_Asset uniqueidentifier NOT NULL, - Socks_Item uniqueidentifier NOT NULL, - Socks_Asset uniqueidentifier NOT NULL, - Jacket_Item uniqueidentifier NOT NULL, - Jacket_Asset uniqueidentifier NOT NULL, - Gloves_Item uniqueidentifier NOT NULL, - Gloves_Asset uniqueidentifier NOT NULL, - Undershirt_Item uniqueidentifier NOT NULL, - Undershirt_Asset uniqueidentifier NOT NULL, - Underpants_Item uniqueidentifier NOT NULL, - Underpants_Asset uniqueidentifier NOT NULL, - Skirt_Item uniqueidentifier NOT NULL, - Skirt_Asset uniqueidentifier NOT NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.avatarappearance) - EXEC('INSERT INTO dbo.Tmp_avatarappearance (Owner, Serial, Visual_Params, Texture, Avatar_Height, Body_Item, Body_Asset, Skin_Item, Skin_Asset, Hair_Item, Hair_Asset, Eyes_Item, Eyes_Asset, Shirt_Item, Shirt_Asset, Pants_Item, Pants_Asset, Shoes_Item, Shoes_Asset, Socks_Item, Socks_Asset, Jacket_Item, Jacket_Asset, Gloves_Item, Gloves_Asset, Undershirt_Item, Undershirt_Asset, Underpants_Item, Underpants_Asset, Skirt_Item, Skirt_Asset) - SELECT CONVERT(uniqueidentifier, Owner), Serial, Visual_Params, Texture, Avatar_Height, CONVERT(uniqueidentifier, Body_Item), CONVERT(uniqueidentifier, Body_Asset), CONVERT(uniqueidentifier, Skin_Item), CONVERT(uniqueidentifier, Skin_Asset), CONVERT(uniqueidentifier, Hair_Item), CONVERT(uniqueidentifier, Hair_Asset), CONVERT(uniqueidentifier, Eyes_Item), CONVERT(uniqueidentifier, Eyes_Asset), CONVERT(uniqueidentifier, Shirt_Item), CONVERT(uniqueidentifier, Shirt_Asset), CONVERT(uniqueidentifier, Pants_Item), CONVERT(uniqueidentifier, Pants_Asset), CONVERT(uniqueidentifier, Shoes_Item), CONVERT(uniqueidentifier, Shoes_Asset), CONVERT(uniqueidentifier, Socks_Item), CONVERT(uniqueidentifier, Socks_Asset), CONVERT(uniqueidentifier, Jacket_Item), CONVERT(uniqueidentifier, Jacket_Asset), CONVERT(uniqueidentifier, Gloves_Item), CONVERT(uniqueidentifier, Gloves_Asset), CONVERT(uniqueidentifier, Undershirt_Item), CONVERT(uniqueidentifier, Undershirt_Asset), CONVERT(uniqueidentifier, Underpants_Item), CONVERT(uniqueidentifier, Underpants_Asset), CONVERT(uniqueidentifier, Skirt_Item), CONVERT(uniqueidentifier, Skirt_Asset) FROM dbo.avatarappearance WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.avatarappearance - -EXECUTE sp_rename N'dbo.Tmp_avatarappearance', N'avatarappearance', 'OBJECT' - -ALTER TABLE dbo.avatarappearance ADD CONSTRAINT - PK__avatarap__7DD115CC4E88ABD4 PRIMARY KEY CLUSTERED - ( - Owner - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/010_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/010_RegionStore.sql deleted file mode 100644 index 74ad9c2e28..0000000000 --- a/OpenSim/Data/MSSQL/Resources/010_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regionsettings ADD sunvectorx float NOT NULL default 0; -ALTER TABLE regionsettings ADD sunvectory float NOT NULL default 0; -ALTER TABLE regionsettings ADD sunvectorz float NOT NULL default 0; - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/010_UserStore.sql b/OpenSim/Data/MSSQL/Resources/010_UserStore.sql deleted file mode 100644 index 0af008aa13..0000000000 --- a/OpenSim/Data/MSSQL/Resources/010_UserStore.sql +++ /dev/null @@ -1,24 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_avatarattachments - ( - UUID uniqueidentifier NOT NULL, - attachpoint int NOT NULL, - item uniqueidentifier NOT NULL, - asset uniqueidentifier NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.avatarattachments) - EXEC('INSERT INTO dbo.Tmp_avatarattachments (UUID, attachpoint, item, asset) - SELECT CONVERT(uniqueidentifier, UUID), attachpoint, CONVERT(uniqueidentifier, item), CONVERT(uniqueidentifier, asset) FROM dbo.avatarattachments WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.avatarattachments - -EXECUTE sp_rename N'dbo.Tmp_avatarattachments', N'avatarattachments', 'OBJECT' - -CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/011_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/011_RegionStore.sql deleted file mode 100644 index 14c71a3737..0000000000 --- a/OpenSim/Data/MSSQL/Resources/011_RegionStore.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000' -ALTER TABLE prims ADD CollisionSoundVolume float not null default 0.0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/011_UserStore.sql b/OpenSim/Data/MSSQL/Resources/011_UserStore.sql deleted file mode 100644 index 5aa064fac8..0000000000 --- a/OpenSim/Data/MSSQL/Resources/011_UserStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE users ADD scopeID uniqueidentifier not null default '00000000-0000-0000-0000-000000000000' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/012_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/012_RegionStore.sql deleted file mode 100644 index eef8d9075c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/012_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD LinkNumber integer not null default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/013_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/013_RegionStore.sql deleted file mode 100644 index ef5d4c035c..0000000000 --- a/OpenSim/Data/MSSQL/Resources/013_RegionStore.sql +++ /dev/null @@ -1,112 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_prims - ( - UUID uniqueidentifier NOT NULL, - RegionUUID uniqueidentifier NULL, - ParentID int NULL, - CreationDate int NULL, - Name varchar(255) NULL, - SceneGroupID uniqueidentifier NULL, - Text varchar(255) NULL, - Description varchar(255) NULL, - SitName varchar(255) NULL, - TouchName varchar(255) NULL, - ObjectFlags int NULL, - CreatorID uniqueidentifier NULL, - OwnerID uniqueidentifier NULL, - GroupID uniqueidentifier NULL, - LastOwnerID uniqueidentifier NULL, - OwnerMask int NULL, - NextOwnerMask int NULL, - GroupMask int NULL, - EveryoneMask int NULL, - BaseMask int NULL, - PositionX float(53) NULL, - PositionY float(53) NULL, - PositionZ float(53) NULL, - GroupPositionX float(53) NULL, - GroupPositionY float(53) NULL, - GroupPositionZ float(53) NULL, - VelocityX float(53) NULL, - VelocityY float(53) NULL, - VelocityZ float(53) NULL, - AngularVelocityX float(53) NULL, - AngularVelocityY float(53) NULL, - AngularVelocityZ float(53) NULL, - AccelerationX float(53) NULL, - AccelerationY float(53) NULL, - AccelerationZ float(53) NULL, - RotationX float(53) NULL, - RotationY float(53) NULL, - RotationZ float(53) NULL, - RotationW float(53) NULL, - SitTargetOffsetX float(53) NULL, - SitTargetOffsetY float(53) NULL, - SitTargetOffsetZ float(53) NULL, - SitTargetOrientW float(53) NULL, - SitTargetOrientX float(53) NULL, - SitTargetOrientY float(53) NULL, - SitTargetOrientZ float(53) NULL, - PayPrice int NOT NULL DEFAULT ((0)), - PayButton1 int NOT NULL DEFAULT ((0)), - PayButton2 int NOT NULL DEFAULT ((0)), - PayButton3 int NOT NULL DEFAULT ((0)), - PayButton4 int NOT NULL DEFAULT ((0)), - LoopedSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - LoopedSoundGain float(53) NOT NULL DEFAULT ((0.0)), - TextureAnimation image NULL, - OmegaX float(53) NOT NULL DEFAULT ((0.0)), - OmegaY float(53) NOT NULL DEFAULT ((0.0)), - OmegaZ float(53) NOT NULL DEFAULT ((0.0)), - CameraEyeOffsetX float(53) NOT NULL DEFAULT ((0.0)), - CameraEyeOffsetY float(53) NOT NULL DEFAULT ((0.0)), - CameraEyeOffsetZ float(53) NOT NULL DEFAULT ((0.0)), - CameraAtOffsetX float(53) NOT NULL DEFAULT ((0.0)), - CameraAtOffsetY float(53) NOT NULL DEFAULT ((0.0)), - CameraAtOffsetZ float(53) NOT NULL DEFAULT ((0.0)), - ForceMouselook tinyint NOT NULL DEFAULT ((0)), - ScriptAccessPin int NOT NULL DEFAULT ((0)), - AllowedDrop tinyint NOT NULL DEFAULT ((0)), - DieAtEdge tinyint NOT NULL DEFAULT ((0)), - SalePrice int NOT NULL DEFAULT ((10)), - SaleType tinyint NOT NULL DEFAULT ((0)), - ColorR int NOT NULL DEFAULT ((0)), - ColorG int NOT NULL DEFAULT ((0)), - ColorB int NOT NULL DEFAULT ((0)), - ColorA int NOT NULL DEFAULT ((0)), - ParticleSystem image NULL, - ClickAction tinyint NOT NULL DEFAULT ((0)), - Material tinyint NOT NULL DEFAULT ((3)), - CollisionSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - CollisionSoundVolume float(53) NOT NULL DEFAULT ((0.0)), - LinkNumber int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.prims) - EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, LoopedSound, LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, LinkNumber) - SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), ParentID, CreationDate, Name, CONVERT(uniqueidentifier, SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(uniqueidentifier, CreatorID), CONVERT(uniqueidentifier, OwnerID), CONVERT(uniqueidentifier, GroupID), CONVERT(uniqueidentifier, LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, CONVERT(uniqueidentifier, LoopedSound), LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CONVERT(uniqueidentifier, CollisionSound), CollisionSoundVolume, LinkNumber FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.prims - -EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' - -ALTER TABLE dbo.prims ADD CONSTRAINT - PK__prims__10566F31 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - - -CREATE NONCLUSTERED INDEX prims_regionuuid ON dbo.prims - ( - RegionUUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX prims_parentid ON dbo.prims - ( - ParentID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/014_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/014_RegionStore.sql deleted file mode 100644 index 02f6f55b86..0000000000 --- a/OpenSim/Data/MSSQL/Resources/014_RegionStore.sql +++ /dev/null @@ -1,49 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_primshapes - ( - UUID uniqueidentifier NOT NULL, - Shape int NULL, - ScaleX float(53) NULL, - ScaleY float(53) NULL, - ScaleZ float(53) NULL, - PCode int NULL, - PathBegin int NULL, - PathEnd int NULL, - PathScaleX int NULL, - PathScaleY int NULL, - PathShearX int NULL, - PathShearY int NULL, - PathSkew int NULL, - PathCurve int NULL, - PathRadiusOffset int NULL, - PathRevolutions int NULL, - PathTaperX int NULL, - PathTaperY int NULL, - PathTwist int NULL, - PathTwistBegin int NULL, - ProfileBegin int NULL, - ProfileEnd int NULL, - ProfileCurve int NULL, - ProfileHollow int NULL, - State int NULL, - Texture image NULL, - ExtraParams image NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.primshapes) - EXEC('INSERT INTO dbo.Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) - SELECT CONVERT(uniqueidentifier, UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM dbo.primshapes WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.primshapes - -EXECUTE sp_rename N'dbo.Tmp_primshapes', N'primshapes', 'OBJECT' - -ALTER TABLE dbo.primshapes ADD CONSTRAINT - PK__primshapes__0880433F PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/015_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/015_RegionStore.sql deleted file mode 100644 index cbaaf88bb9..0000000000 --- a/OpenSim/Data/MSSQL/Resources/015_RegionStore.sql +++ /dev/null @@ -1,45 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_primitems - ( - itemID uniqueidentifier NOT NULL, - primID uniqueidentifier NULL, - assetID uniqueidentifier NULL, - parentFolderID uniqueidentifier NULL, - invType int NULL, - assetType int NULL, - name varchar(255) NULL, - description varchar(255) NULL, - creationDate varchar(255) NULL, - creatorID uniqueidentifier NULL, - ownerID uniqueidentifier NULL, - lastOwnerID uniqueidentifier NULL, - groupID uniqueidentifier NULL, - nextPermissions int NULL, - currentPermissions int NULL, - basePermissions int NULL, - everyonePermissions int NULL, - groupPermissions int NULL, - flags int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.primitems) - EXEC('INSERT INTO dbo.Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags) - SELECT CONVERT(uniqueidentifier, itemID), CONVERT(uniqueidentifier, primID), CONVERT(uniqueidentifier, assetID), CONVERT(uniqueidentifier, parentFolderID), invType, assetType, name, description, creationDate, CONVERT(uniqueidentifier, creatorID), CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, lastOwnerID), CONVERT(uniqueidentifier, groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags FROM dbo.primitems WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.primitems - -EXECUTE sp_rename N'dbo.Tmp_primitems', N'primitems', 'OBJECT' - -ALTER TABLE dbo.primitems ADD CONSTRAINT - PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED - ( - itemID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -CREATE NONCLUSTERED INDEX primitems_primid ON dbo.primitems - ( - primID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/016_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/016_RegionStore.sql deleted file mode 100644 index e91da19b12..0000000000 --- a/OpenSim/Data/MSSQL/Resources/016_RegionStore.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_terrain - ( - RegionUUID uniqueidentifier NULL, - Revision int NULL, - Heightfield image NULL - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.terrain) - EXEC('INSERT INTO dbo.Tmp_terrain (RegionUUID, Revision, Heightfield) - SELECT CONVERT(uniqueidentifier, RegionUUID), Revision, Heightfield FROM dbo.terrain WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.terrain - -EXECUTE sp_rename N'dbo.Tmp_terrain', N'terrain', 'OBJECT' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/017_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/017_RegionStore.sql deleted file mode 100644 index 3d3dbc0ee7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/017_RegionStore.sql +++ /dev/null @@ -1,56 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_land - ( - UUID uniqueidentifier NOT NULL, - RegionUUID uniqueidentifier NULL, - LocalLandID int NULL, - Bitmap image NULL, - Name varchar(255) NULL, - Description varchar(255) NULL, - OwnerUUID uniqueidentifier NULL, - IsGroupOwned int NULL, - Area int NULL, - AuctionID int NULL, - Category int NULL, - ClaimDate int NULL, - ClaimPrice int NULL, - GroupUUID uniqueidentifier NULL, - SalePrice int NULL, - LandStatus int NULL, - LandFlags int NULL, - LandingType int NULL, - MediaAutoScale int NULL, - MediaTextureUUID uniqueidentifier NULL, - MediaURL varchar(255) NULL, - MusicURL varchar(255) NULL, - PassHours float(53) NULL, - PassPrice int NULL, - SnapshotUUID uniqueidentifier NULL, - UserLocationX float(53) NULL, - UserLocationY float(53) NULL, - UserLocationZ float(53) NULL, - UserLookAtX float(53) NULL, - UserLookAtY float(53) NULL, - UserLookAtZ float(53) NULL, - AuthbuyerID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), - OtherCleanTime int NOT NULL DEFAULT ((0)), - Dwell int NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - TEXTIMAGE_ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.land) - EXEC('INSERT INTO dbo.Tmp_land (UUID, RegionUUID, LocalLandID, Bitmap, Name, Description, OwnerUUID, IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, GroupUUID, SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, MediaTextureUUID, MediaURL, MusicURL, PassHours, PassPrice, SnapshotUUID, UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, AuthbuyerID, OtherCleanTime, Dwell) - SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), LocalLandID, Bitmap, Name, Description, CONVERT(uniqueidentifier, OwnerUUID), IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, CONVERT(uniqueidentifier, GroupUUID), SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, CONVERT(uniqueidentifier, MediaTextureUUID), MediaURL, MusicURL, PassHours, PassPrice, CONVERT(uniqueidentifier, SnapshotUUID), UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, CONVERT(uniqueidentifier, AuthbuyerID), OtherCleanTime, Dwell FROM dbo.land WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.land - -EXECUTE sp_rename N'dbo.Tmp_land', N'land', 'OBJECT' - -ALTER TABLE dbo.land ADD CONSTRAINT - PK__land__65A475E71BFD2C07 PRIMARY KEY CLUSTERED - ( - UUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/018_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/018_RegionStore.sql deleted file mode 100644 index 6157e3536e..0000000000 --- a/OpenSim/Data/MSSQL/Resources/018_RegionStore.sql +++ /dev/null @@ -1,18 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_landaccesslist - ( - LandUUID uniqueidentifier NULL, - AccessUUID uniqueidentifier NULL, - Flags int NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.landaccesslist) - EXEC('INSERT INTO dbo.Tmp_landaccesslist (LandUUID, AccessUUID, Flags) - SELECT CONVERT(uniqueidentifier, LandUUID), CONVERT(uniqueidentifier, AccessUUID), Flags FROM dbo.landaccesslist WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.landaccesslist - -EXECUTE sp_rename N'dbo.Tmp_landaccesslist', N'landaccesslist', 'OBJECT' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/019_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/019_RegionStore.sql deleted file mode 100644 index 8e613b9d09..0000000000 --- a/OpenSim/Data/MSSQL/Resources/019_RegionStore.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_regionban - ( - regionUUID uniqueidentifier NOT NULL, - bannedUUID uniqueidentifier NOT NULL, - bannedIp varchar(16) NOT NULL, - bannedIpHostMask varchar(16) NOT NULL - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.regionban) - EXEC('INSERT INTO dbo.Tmp_regionban (regionUUID, bannedUUID, bannedIp, bannedIpHostMask) - SELECT CONVERT(uniqueidentifier, regionUUID), CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask FROM dbo.regionban WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.regionban - -EXECUTE sp_rename N'dbo.Tmp_regionban', N'regionban', 'OBJECT' - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/020_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/020_RegionStore.sql deleted file mode 100644 index 2ce91f6dca..0000000000 --- a/OpenSim/Data/MSSQL/Resources/020_RegionStore.sql +++ /dev/null @@ -1,58 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE dbo.Tmp_regionsettings - ( - regionUUID uniqueidentifier NOT NULL, - block_terraform bit NOT NULL, - block_fly bit NOT NULL, - allow_damage bit NOT NULL, - restrict_pushing bit NOT NULL, - allow_land_resell bit NOT NULL, - allow_land_join_divide bit NOT NULL, - block_show_in_search bit NOT NULL, - agent_limit int NOT NULL, - object_bonus float(53) NOT NULL, - maturity int NOT NULL, - disable_scripts bit NOT NULL, - disable_collisions bit NOT NULL, - disable_physics bit NOT NULL, - terrain_texture_1 uniqueidentifier NOT NULL, - terrain_texture_2 uniqueidentifier NOT NULL, - terrain_texture_3 uniqueidentifier NOT NULL, - terrain_texture_4 uniqueidentifier NOT NULL, - elevation_1_nw float(53) NOT NULL, - elevation_2_nw float(53) NOT NULL, - elevation_1_ne float(53) NOT NULL, - elevation_2_ne float(53) NOT NULL, - elevation_1_se float(53) NOT NULL, - elevation_2_se float(53) NOT NULL, - elevation_1_sw float(53) NOT NULL, - elevation_2_sw float(53) NOT NULL, - water_height float(53) NOT NULL, - terrain_raise_limit float(53) NOT NULL, - terrain_lower_limit float(53) NOT NULL, - use_estate_sun bit NOT NULL, - fixed_sun bit NOT NULL, - sun_position float(53) NOT NULL, - covenant uniqueidentifier NULL DEFAULT (NULL), - Sandbox bit NOT NULL, - sunvectorx float(53) NOT NULL DEFAULT ((0)), - sunvectory float(53) NOT NULL DEFAULT ((0)), - sunvectorz float(53) NOT NULL DEFAULT ((0)) - ) ON [PRIMARY] - -IF EXISTS(SELECT * FROM dbo.regionsettings) - EXEC('INSERT INTO dbo.Tmp_regionsettings (regionUUID, block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, terrain_texture_1, terrain_texture_2, terrain_texture_3, terrain_texture_4, elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, covenant, Sandbox, sunvectorx, sunvectory, sunvectorz) - SELECT CONVERT(uniqueidentifier, regionUUID), block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, CONVERT(uniqueidentifier, terrain_texture_1), CONVERT(uniqueidentifier, terrain_texture_2), CONVERT(uniqueidentifier, terrain_texture_3), CONVERT(uniqueidentifier, terrain_texture_4), elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, CONVERT(uniqueidentifier, covenant), Sandbox, sunvectorx, sunvectory, sunvectorz FROM dbo.regionsettings WITH (HOLDLOCK TABLOCKX)') - -DROP TABLE dbo.regionsettings - -EXECUTE sp_rename N'dbo.Tmp_regionsettings', N'regionsettings', 'OBJECT' - -ALTER TABLE dbo.regionsettings ADD CONSTRAINT - PK__regionse__5B35159D21B6055D PRIMARY KEY CLUSTERED - ( - regionUUID - ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/021_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/021_RegionStore.sql deleted file mode 100644 index ac59182abc..0000000000 --- a/OpenSim/Data/MSSQL/Resources/021_RegionStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE prims ADD PassTouches bit not null default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/022_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/022_RegionStore.sql deleted file mode 100644 index 421e8d3dc7..0000000000 --- a/OpenSim/Data/MSSQL/Resources/022_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regionsettings ADD loaded_creation_date varchar(20) -ALTER TABLE regionsettings ADD loaded_creation_time varchar(20) -ALTER TABLE regionsettings ADD loaded_creation_id varchar(64) - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/023_RegionStore.sql b/OpenSim/Data/MSSQL/Resources/023_RegionStore.sql deleted file mode 100644 index 75a16f3fb0..0000000000 --- a/OpenSim/Data/MSSQL/Resources/023_RegionStore.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN TRANSACTION - -ALTER TABLE regionsettings DROP COLUMN loaded_creation_date -ALTER TABLE regionsettings DROP COLUMN loaded_creation_time -ALTER TABLE regionsettings ADD loaded_creation_datetime int NOT NULL default 0 - -COMMIT diff --git a/OpenSim/Data/MSSQL/Resources/AssetStore.migrations b/OpenSim/Data/MSSQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..beb82b9fc2 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/AssetStore.migrations @@ -0,0 +1,100 @@ +:VERSION 1 + +CREATE TABLE [assets] ( + [id] [varchar](36) NOT NULL, + [name] [varchar](64) NOT NULL, + [description] [varchar](64) NOT NULL, + [assetType] [tinyint] NOT NULL, + [local] [tinyint] NOT NULL, + [temporary] [tinyint] NOT NULL, + [data] [image] NOT NULL, +PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_assets + ( + id varchar(36) NOT NULL, + name varchar(64) NOT NULL, + description varchar(64) NOT NULL, + assetType tinyint NOT NULL, + local bit NOT NULL, + temporary bit NOT NULL, + data image NOT NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM assets) + EXEC('INSERT INTO Tmp_assets (id, name, description, assetType, local, temporary, data) + SELECT id, name, description, assetType, CONVERT(bit, local), CONVERT(bit, temporary), data FROM assets WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE assets + +EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' + +ALTER TABLE dbo.assets ADD CONSTRAINT + PK__assets__id PRIMARY KEY CLUSTERED + ( + id + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 3 + +BEGIN TRANSACTION + +ALTER TABLE assets add create_time integer default 0 +ALTER TABLE assets add access_time integer default 0 + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_assets + ( + id uniqueidentifier NOT NULL, + name varchar(64) NOT NULL, + description varchar(64) NOT NULL, + assetType tinyint NOT NULL, + local bit NOT NULL, + temporary bit NOT NULL, + data image NOT NULL, + create_time int NULL, + access_time int NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.assets) + EXEC('INSERT INTO dbo.Tmp_assets (id, name, description, assetType, local, temporary, data, create_time, access_time) + SELECT CONVERT(uniqueidentifier, id), name, description, assetType, local, temporary, data, create_time, access_time FROM dbo.assets WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE assets + +EXECUTE sp_rename N'Tmp_assets', N'assets', 'OBJECT' + +ALTER TABLE dbo.assets ADD CONSTRAINT + PK__assets__id PRIMARY KEY CLUSTERED + ( + id + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; + + diff --git a/OpenSim/Data/MSSQL/Resources/001_AuthStore.sql b/OpenSim/Data/MSSQL/Resources/AuthStore.migrations similarity index 63% rename from OpenSim/Data/MSSQL/Resources/001_AuthStore.sql rename to OpenSim/Data/MSSQL/Resources/AuthStore.migrations index c70a19319b..5b90ca3d36 100644 --- a/OpenSim/Data/MSSQL/Resources/001_AuthStore.sql +++ b/OpenSim/Data/MSSQL/Resources/AuthStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [auth] ( @@ -14,4 +16,13 @@ CREATE TABLE [tokens] ( [validity] [datetime] NOT NULL ) ON [PRIMARY] +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey, accountType) SELECT [UUID] AS UUID, [passwordHash] AS passwordHash, [passwordSalt] AS passwordSalt, [webLoginKey] AS webLoginKey, 'UserAccount' as [accountType] FROM users; + + COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/001_Avatar.sql b/OpenSim/Data/MSSQL/Resources/Avatar.migrations similarity index 94% rename from OpenSim/Data/MSSQL/Resources/001_Avatar.sql rename to OpenSim/Data/MSSQL/Resources/Avatar.migrations index 48f4c00669..759e939caf 100644 --- a/OpenSim/Data/MSSQL/Resources/001_Avatar.sql +++ b/OpenSim/Data/MSSQL/Resources/Avatar.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [Avatars] ( diff --git a/OpenSim/Data/MSSQL/Resources/EstateStore.migrations b/OpenSim/Data/MSSQL/Resources/EstateStore.migrations new file mode 100644 index 0000000000..64b2d2bdb2 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/EstateStore.migrations @@ -0,0 +1,334 @@ +:VERSION 1 + +BEGIN TRANSACTION + +CREATE TABLE [dbo].[estate_managers]( + [EstateID] [int] NOT NULL, + [uuid] [varchar](36) NOT NULL, + CONSTRAINT [PK_estate_managers] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + +CREATE TABLE [dbo].[estate_groups]( + [EstateID] [int] NOT NULL, + [uuid] [varchar](36) NOT NULL, + CONSTRAINT [PK_estate_groups] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + + +CREATE TABLE [dbo].[estate_users]( + [EstateID] [int] NOT NULL, + [uuid] [varchar](36) NOT NULL, + CONSTRAINT [PK_estate_users] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + + +CREATE TABLE [dbo].[estateban]( + [EstateID] [int] NOT NULL, + [bannedUUID] [varchar](36) NOT NULL, + [bannedIp] [varchar](16) NOT NULL, + [bannedIpHostMask] [varchar](16) NOT NULL, + [bannedNameMask] [varchar](64) NULL DEFAULT (NULL), + CONSTRAINT [PK_estateban] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + +CREATE TABLE [dbo].[estate_settings]( + [EstateID] [int] IDENTITY(1,100) NOT NULL, + [EstateName] [varchar](64) NULL DEFAULT (NULL), + [AbuseEmailToEstateOwner] [bit] NOT NULL, + [DenyAnonymous] [bit] NOT NULL, + [ResetHomeOnTeleport] [bit] NOT NULL, + [FixedSun] [bit] NOT NULL, + [DenyTransacted] [bit] NOT NULL, + [BlockDwell] [bit] NOT NULL, + [DenyIdentified] [bit] NOT NULL, + [AllowVoice] [bit] NOT NULL, + [UseGlobalTime] [bit] NOT NULL, + [PricePerMeter] [int] NOT NULL, + [TaxFree] [bit] NOT NULL, + [AllowDirectTeleport] [bit] NOT NULL, + [RedirectGridX] [int] NOT NULL, + [RedirectGridY] [int] NOT NULL, + [ParentEstateID] [int] NOT NULL, + [SunPosition] [float] NOT NULL, + [EstateSkipScripts] [bit] NOT NULL, + [BillableFactor] [float] NOT NULL, + [PublicAccess] [bit] NOT NULL, + [AbuseEmail] [varchar](255) NOT NULL, + [EstateOwner] [varchar](36) NOT NULL, + [DenyMinors] [bit] NOT NULL, + CONSTRAINT [PK_estate_settings] PRIMARY KEY CLUSTERED +( + [EstateID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + + +CREATE TABLE [dbo].[estate_map]( + [RegionID] [varchar](36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + [EstateID] [int] NOT NULL, + CONSTRAINT [PK_estate_map] PRIMARY KEY CLUSTERED +( + [RegionID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY]; + +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +ALTER TABLE dbo.estate_managers DROP CONSTRAINT PK_estate_managers + +CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +ALTER TABLE dbo.estate_groups DROP CONSTRAINT PK_estate_groups + +CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +ALTER TABLE dbo.estate_users DROP CONSTRAINT PK_estate_users + +CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estateban + ( + EstateID int NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NULL, + bannedIpHostMask varchar(16) NULL, + bannedNameMask varchar(64) NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estateban) + EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) + SELECT EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban') + +DROP TABLE dbo.estateban + +EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_managers + ( + EstateID int NOT NULL, + uuid uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_managers) + EXEC('INSERT INTO dbo.Tmp_estate_managers (EstateID, uuid) + SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_managers WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_managers + +EXECUTE sp_rename N'dbo.Tmp_estate_managers', N'estate_managers', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estate_managers ON dbo.estate_managers + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_groups + ( + EstateID int NOT NULL, + uuid uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_groups) + EXEC('INSERT INTO dbo.Tmp_estate_groups (EstateID, uuid) + SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_groups WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_groups + +EXECUTE sp_rename N'dbo.Tmp_estate_groups', N'estate_groups', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estate_groups ON dbo.estate_groups + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_users + ( + EstateID int NOT NULL, + uuid uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_users) + EXEC('INSERT INTO dbo.Tmp_estate_users (EstateID, uuid) + SELECT EstateID, CONVERT(uniqueidentifier, uuid) FROM dbo.estate_users WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_users + +EXECUTE sp_rename N'dbo.Tmp_estate_users', N'estate_users', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estate_users ON dbo.estate_users + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estateban + ( + EstateID int NOT NULL, + bannedUUID uniqueidentifier NOT NULL, + bannedIp varchar(16) NULL, + bannedIpHostMask varchar(16) NULL, + bannedNameMask varchar(64) NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estateban) + EXEC('INSERT INTO dbo.Tmp_estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) + SELECT EstateID, CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask, bannedNameMask FROM dbo.estateban WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estateban + +EXECUTE sp_rename N'dbo.Tmp_estateban', N'estateban', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_estateban ON dbo.estateban + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 8 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_settings + ( + EstateID int NOT NULL IDENTITY (1, 100), + EstateName varchar(64) NULL DEFAULT (NULL), + AbuseEmailToEstateOwner bit NOT NULL, + DenyAnonymous bit NOT NULL, + ResetHomeOnTeleport bit NOT NULL, + FixedSun bit NOT NULL, + DenyTransacted bit NOT NULL, + BlockDwell bit NOT NULL, + DenyIdentified bit NOT NULL, + AllowVoice bit NOT NULL, + UseGlobalTime bit NOT NULL, + PricePerMeter int NOT NULL, + TaxFree bit NOT NULL, + AllowDirectTeleport bit NOT NULL, + RedirectGridX int NOT NULL, + RedirectGridY int NOT NULL, + ParentEstateID int NOT NULL, + SunPosition float(53) NOT NULL, + EstateSkipScripts bit NOT NULL, + BillableFactor float(53) NOT NULL, + PublicAccess bit NOT NULL, + AbuseEmail varchar(255) NOT NULL, + EstateOwner uniqueidentifier NOT NULL, + DenyMinors bit NOT NULL + ) ON [PRIMARY] + +SET IDENTITY_INSERT dbo.Tmp_estate_settings ON + +IF EXISTS(SELECT * FROM dbo.estate_settings) + EXEC('INSERT INTO dbo.Tmp_estate_settings (EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, EstateOwner, DenyMinors) + SELECT EstateID, EstateName, AbuseEmailToEstateOwner, DenyAnonymous, ResetHomeOnTeleport, FixedSun, DenyTransacted, BlockDwell, DenyIdentified, AllowVoice, UseGlobalTime, PricePerMeter, TaxFree, AllowDirectTeleport, RedirectGridX, RedirectGridY, ParentEstateID, SunPosition, EstateSkipScripts, BillableFactor, PublicAccess, AbuseEmail, CONVERT(uniqueidentifier, EstateOwner), DenyMinors FROM dbo.estate_settings WITH (HOLDLOCK TABLOCKX)') + +SET IDENTITY_INSERT dbo.Tmp_estate_settings OFF + +DROP TABLE dbo.estate_settings + +EXECUTE sp_rename N'dbo.Tmp_estate_settings', N'estate_settings', 'OBJECT' + +ALTER TABLE dbo.estate_settings ADD CONSTRAINT + PK_estate_settings PRIMARY KEY CLUSTERED + ( + EstateID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 9 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_estate_map + ( + RegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + EstateID int NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.estate_map) + EXEC('INSERT INTO dbo.Tmp_estate_map (RegionID, EstateID) + SELECT CONVERT(uniqueidentifier, RegionID), EstateID FROM dbo.estate_map WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.estate_map + +EXECUTE sp_rename N'dbo.Tmp_estate_map', N'estate_map', 'OBJECT' + +ALTER TABLE dbo.estate_map ADD CONSTRAINT + PK_estate_map PRIMARY KEY CLUSTERED + ( + RegionID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +COMMIT + + diff --git a/OpenSim/Data/MSSQL/Resources/001_FriendsStore.sql b/OpenSim/Data/MSSQL/Resources/FriendsStore.migrations similarity index 54% rename from OpenSim/Data/MSSQL/Resources/001_FriendsStore.sql rename to OpenSim/Data/MSSQL/Resources/FriendsStore.migrations index 94d240b512..f981a91999 100644 --- a/OpenSim/Data/MSSQL/Resources/001_FriendsStore.sql +++ b/OpenSim/Data/MSSQL/Resources/FriendsStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [Friends] ( @@ -7,5 +9,12 @@ CREATE TABLE [Friends] ( [Offered] varchar(32) NOT NULL DEFAULT 0) ON [PRIMARY] +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +INSERT INTO Friends (PrincipalID, Friend, Flags, Offered) SELECT [ownerID], [friendID], [friendPerms], 0 FROM userfriends; COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/GridStore.migrations b/OpenSim/Data/MSSQL/Resources/GridStore.migrations new file mode 100644 index 0000000000..d2ca27a071 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/GridStore.migrations @@ -0,0 +1,225 @@ +:VERSION 1 + +BEGIN TRANSACTION + +CREATE TABLE [dbo].[regions]( + [regionHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionName] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [uuid] [varchar](255) COLLATE Latin1_General_CI_AS NOT NULL, + [regionRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionSecret] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionDataURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverIP] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [locX] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [locY] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [locZ] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [eastOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [westOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [southOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [northOverrideHandle] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionAssetURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionAssetRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionAssetSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionUserURI] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionUserRecvKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionUserSendKey] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [regionMapTexture] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverHttpPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [serverRemotingPort] [varchar](255) COLLATE Latin1_General_CI_AS NULL, + [owner_uuid] [varchar](36) COLLATE Latin1_General_CI_AS NULL, +PRIMARY KEY CLUSTERED +( + [uuid] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +COMMIT + + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_regions + ( + uuid varchar(36) COLLATE Latin1_General_CI_AS NOT NULL, + regionHandle bigint NULL, + regionName varchar(20) NULL, + regionRecvKey varchar(128) NULL, + regionSendKey varchar(128) NULL, + regionSecret varchar(128) NULL, + regionDataURI varchar(128) NULL, + serverIP varchar(64) NULL, + serverPort int NULL, + serverURI varchar(255) NULL, + locX int NULL, + locY int NULL, + locZ int NULL, + eastOverrideHandle bigint NULL, + westOverrideHandle bigint NULL, + southOverrideHandle bigint NULL, + northOverrideHandle bigint NULL, + regionAssetURI varchar(255) NULL, + regionAssetRecvKey varchar(128) NULL, + regionAssetSendKey varchar(128) NULL, + regionUserURI varchar(255) NULL, + regionUserRecvKey varchar(128) NULL, + regionUserSendKey varchar(128) NULL, + regionMapTexture varchar(36) NULL, + serverHttpPort int NULL, + serverRemotingPort int NULL, + owner_uuid varchar(36) NULL, + originUUID varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM regions) + EXEC('INSERT INTO Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid) + SELECT CONVERT(varchar(36), uuid), CONVERT(bigint, regionHandle), CONVERT(varchar(20), regionName), CONVERT(varchar(128), regionRecvKey), CONVERT(varchar(128), regionSendKey), CONVERT(varchar(128), regionSecret), CONVERT(varchar(128), regionDataURI), CONVERT(varchar(64), serverIP), CONVERT(int, serverPort), serverURI, CONVERT(int, locX), CONVERT(int, locY), CONVERT(int, locZ), CONVERT(bigint, eastOverrideHandle), CONVERT(bigint, westOverrideHandle), CONVERT(bigint, southOverrideHandle), CONVERT(bigint, northOverrideHandle), regionAssetURI, CONVERT(varchar(128), regionAssetRecvKey), CONVERT(varchar(128), regionAssetSendKey), regionUserURI, CONVERT(varchar(128), regionUserRecvKey), CONVERT(varchar(128), regionUserSendKey), CONVERT(varchar(36), regionMapTexture), CONVERT(int, serverHttpPort), CONVERT(int, serverRemotingPort), owner_uuid FROM regions') + +DROP TABLE regions + +EXECUTE sp_rename N'Tmp_regions', N'regions', 'OBJECT' + +ALTER TABLE regions ADD CONSTRAINT + PK__regions__uuid PRIMARY KEY CLUSTERED + ( + uuid + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions + ( + regionName + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions + ( + regionHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions + ( + eastOverrideHandle, + westOverrideHandle, + southOverrideHandle, + northOverrideHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 4 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_regions + ( + uuid uniqueidentifier NOT NULL, + regionHandle bigint NULL, + regionName varchar(20) NULL, + regionRecvKey varchar(128) NULL, + regionSendKey varchar(128) NULL, + regionSecret varchar(128) NULL, + regionDataURI varchar(128) NULL, + serverIP varchar(64) NULL, + serverPort int NULL, + serverURI varchar(255) NULL, + locX int NULL, + locY int NULL, + locZ int NULL, + eastOverrideHandle bigint NULL, + westOverrideHandle bigint NULL, + southOverrideHandle bigint NULL, + northOverrideHandle bigint NULL, + regionAssetURI varchar(255) NULL, + regionAssetRecvKey varchar(128) NULL, + regionAssetSendKey varchar(128) NULL, + regionUserURI varchar(255) NULL, + regionUserRecvKey varchar(128) NULL, + regionUserSendKey varchar(128) NULL, + regionMapTexture uniqueidentifier NULL, + serverHttpPort int NULL, + serverRemotingPort int NULL, + owner_uuid uniqueidentifier NOT NULL, + originUUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.regions) + EXEC('INSERT INTO dbo.Tmp_regions (uuid, regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, regionMapTexture, serverHttpPort, serverRemotingPort, owner_uuid, originUUID) + SELECT CONVERT(uniqueidentifier, uuid), regionHandle, regionName, regionRecvKey, regionSendKey, regionSecret, regionDataURI, serverIP, serverPort, serverURI, locX, locY, locZ, eastOverrideHandle, westOverrideHandle, southOverrideHandle, northOverrideHandle, regionAssetURI, regionAssetRecvKey, regionAssetSendKey, regionUserURI, regionUserRecvKey, regionUserSendKey, CONVERT(uniqueidentifier, regionMapTexture), serverHttpPort, serverRemotingPort, CONVERT(uniqueidentifier, owner_uuid), CONVERT(uniqueidentifier, originUUID) FROM dbo.regions WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.regions + +EXECUTE sp_rename N'dbo.Tmp_regions', N'regions', 'OBJECT' + +ALTER TABLE dbo.regions ADD CONSTRAINT + PK__regions__uuid PRIMARY KEY CLUSTERED + ( + uuid + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_name ON dbo.regions + ( + regionName + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_handle ON dbo.regions + ( + regionHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_regions_override ON dbo.regions + ( + eastOverrideHandle, + westOverrideHandle, + southOverrideHandle, + northOverrideHandle + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + +ALTER TABLE regions ADD access int default 0; + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +ALTER TABLE regions ADD scopeid uniqueidentifier default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE regions ADD DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [owner_uuid]; +ALTER TABLE regions ADD sizeX integer not null default 0; +ALTER TABLE regions ADD sizeY integer not null default 0; + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +ALTER TABLE regions ADD [flags] integer NOT NULL DEFAULT 0; +CREATE INDEX [flags] ON regions(flags); +ALTER TABLE [regions] ADD [last_seen] integer NOT NULL DEFAULT 0; +ALTER TABLE [regions] ADD [PrincipalID] uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE [regions] ADD [Token] varchar(255) NOT NULL DEFAULT 0; + +COMMIT + + diff --git a/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations b/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..cd5dfdc1ca --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations @@ -0,0 +1,174 @@ +:VERSION 1 + +BEGIN TRANSACTION + +CREATE TABLE [inventoryfolders] ( + [folderID] [varchar](36) NOT NULL default '', + [agentID] [varchar](36) default NULL, + [parentFolderID] [varchar](36) default NULL, + [folderName] [varchar](64) default NULL, + [type] [smallint] NOT NULL default 0, + [version] [int] NOT NULL default 0, + PRIMARY KEY CLUSTERED +( + [folderID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [owner] ON [inventoryfolders] +( + [agentID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [parent] ON [inventoryfolders] +( + [parentFolderID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE TABLE [inventoryitems] ( + [inventoryID] [varchar](36) NOT NULL default '', + [assetID] [varchar](36) default NULL, + [assetType] [int] default NULL, + [parentFolderID] [varchar](36) default NULL, + [avatarID] [varchar](36) default NULL, + [inventoryName] [varchar](64) default NULL, + [inventoryDescription] [varchar](128) default NULL, + [inventoryNextPermissions] [int] default NULL, + [inventoryCurrentPermissions] [int] default NULL, + [invType] [int] default NULL, + [creatorID] [varchar](36) default NULL, + [inventoryBasePermissions] [int] NOT NULL default 0, + [inventoryEveryOnePermissions] [int] NOT NULL default 0, + [salePrice] [int] default NULL, + [saleType] [tinyint] default NULL, + [creationDate] [int] default NULL, + [groupID] [varchar](36) default NULL, + [groupOwned] [bit] default NULL, + [flags] [int] default NULL, + PRIMARY KEY CLUSTERED +( + [inventoryID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX [owner] ON [inventoryitems] +( + [avatarID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [folder] ON [inventoryitems] +( + [parentFolderID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 2 + +BEGIN TRANSACTION + +ALTER TABLE inventoryitems ADD inventoryGroupPermissions INTEGER NOT NULL default 0 + +COMMIT + +:VERSION 3 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/ +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_inventoryfolders + ( + folderID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + agentID uniqueidentifier NULL DEFAULT (NULL), + parentFolderID uniqueidentifier NULL DEFAULT (NULL), + folderName varchar(64) NULL DEFAULT (NULL), + type smallint NOT NULL DEFAULT ((0)), + version int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.inventoryfolders) + EXEC('INSERT INTO dbo.Tmp_inventoryfolders (folderID, agentID, parentFolderID, folderName, type, version) + SELECT CONVERT(uniqueidentifier, folderID), CONVERT(uniqueidentifier, agentID), CONVERT(uniqueidentifier, parentFolderID), folderName, type, version FROM dbo.inventoryfolders WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.inventoryfolders + +EXECUTE sp_rename N'dbo.Tmp_inventoryfolders', N'inventoryfolders', 'OBJECT' + +ALTER TABLE dbo.inventoryfolders ADD CONSTRAINT + PK__inventor__C2FABFB3173876EA PRIMARY KEY CLUSTERED + ( + folderID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX owner ON dbo.inventoryfolders + ( + agentID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX parent ON dbo.inventoryfolders + ( + parentFolderID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_inventoryitems + ( + inventoryID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + assetID uniqueidentifier NULL DEFAULT (NULL), + assetType int NULL DEFAULT (NULL), + parentFolderID uniqueidentifier NULL DEFAULT (NULL), + avatarID uniqueidentifier NULL DEFAULT (NULL), + inventoryName varchar(64) NULL DEFAULT (NULL), + inventoryDescription varchar(128) NULL DEFAULT (NULL), + inventoryNextPermissions int NULL DEFAULT (NULL), + inventoryCurrentPermissions int NULL DEFAULT (NULL), + invType int NULL DEFAULT (NULL), + creatorID uniqueidentifier NULL DEFAULT (NULL), + inventoryBasePermissions int NOT NULL DEFAULT ((0)), + inventoryEveryOnePermissions int NOT NULL DEFAULT ((0)), + salePrice int NULL DEFAULT (NULL), + saleType tinyint NULL DEFAULT (NULL), + creationDate int NULL DEFAULT (NULL), + groupID uniqueidentifier NULL DEFAULT (NULL), + groupOwned bit NULL DEFAULT (NULL), + flags int NULL DEFAULT (NULL), + inventoryGroupPermissions int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.inventoryitems) + EXEC('INSERT INTO dbo.Tmp_inventoryitems (inventoryID, assetID, assetType, parentFolderID, avatarID, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryGroupPermissions) + SELECT CONVERT(uniqueidentifier, inventoryID), CONVERT(uniqueidentifier, assetID), assetType, CONVERT(uniqueidentifier, parentFolderID), CONVERT(uniqueidentifier, avatarID), inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, CONVERT(uniqueidentifier, creatorID), inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, CONVERT(uniqueidentifier, groupID), groupOwned, flags, inventoryGroupPermissions FROM dbo.inventoryitems WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.inventoryitems + +EXECUTE sp_rename N'dbo.Tmp_inventoryitems', N'inventoryitems', 'OBJECT' + +ALTER TABLE dbo.inventoryitems ADD CONSTRAINT + PK__inventor__C4B7BC2220C1E124 PRIMARY KEY CLUSTERED + ( + inventoryID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX owner ON dbo.inventoryitems + ( + avatarID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX folder ON dbo.inventoryitems + ( + parentFolderID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + diff --git a/OpenSim/Data/MSSQL/Resources/001_LogStore.sql b/OpenSim/Data/MSSQL/Resources/LogStore.migrations similarity index 96% rename from OpenSim/Data/MSSQL/Resources/001_LogStore.sql rename to OpenSim/Data/MSSQL/Resources/LogStore.migrations index 9ece6274dd..1430d8d0ee 100644 --- a/OpenSim/Data/MSSQL/Resources/001_LogStore.sql +++ b/OpenSim/Data/MSSQL/Resources/LogStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [logs] ( diff --git a/OpenSim/Data/MSSQL/Resources/001_Presence.sql b/OpenSim/Data/MSSQL/Resources/Presence.migrations similarity index 81% rename from OpenSim/Data/MSSQL/Resources/001_Presence.sql rename to OpenSim/Data/MSSQL/Resources/Presence.migrations index 877881c859..35f78e1f52 100644 --- a/OpenSim/Data/MSSQL/Resources/001_Presence.sql +++ b/OpenSim/Data/MSSQL/Resources/Presence.migrations @@ -1,3 +1,5 @@ +:VERSION 1 + BEGIN TRANSACTION CREATE TABLE [Presence] ( @@ -16,4 +18,13 @@ CREATE TABLE [Presence] ( ) ON [PRIMARY] +COMMIT + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE UNIQUE INDEX SessionID ON Presence(SessionID); +CREATE INDEX UserID ON Presence(UserID); + COMMIT \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..e912d646a0 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations @@ -0,0 +1,929 @@ + +:VERSION 1 + +CREATE TABLE [dbo].[prims]( + [UUID] [varchar](255) NOT NULL, + [RegionUUID] [varchar](255) NULL, + [ParentID] [int] NULL, + [CreationDate] [int] NULL, + [Name] [varchar](255) NULL, + [SceneGroupID] [varchar](255) NULL, + [Text] [varchar](255) NULL, + [Description] [varchar](255) NULL, + [SitName] [varchar](255) NULL, + [TouchName] [varchar](255) NULL, + [ObjectFlags] [int] NULL, + [CreatorID] [varchar](255) NULL, + [OwnerID] [varchar](255) NULL, + [GroupID] [varchar](255) NULL, + [LastOwnerID] [varchar](255) NULL, + [OwnerMask] [int] NULL, + [NextOwnerMask] [int] NULL, + [GroupMask] [int] NULL, + [EveryoneMask] [int] NULL, + [BaseMask] [int] NULL, + [PositionX] [float] NULL, + [PositionY] [float] NULL, + [PositionZ] [float] NULL, + [GroupPositionX] [float] NULL, + [GroupPositionY] [float] NULL, + [GroupPositionZ] [float] NULL, + [VelocityX] [float] NULL, + [VelocityY] [float] NULL, + [VelocityZ] [float] NULL, + [AngularVelocityX] [float] NULL, + [AngularVelocityY] [float] NULL, + [AngularVelocityZ] [float] NULL, + [AccelerationX] [float] NULL, + [AccelerationY] [float] NULL, + [AccelerationZ] [float] NULL, + [RotationX] [float] NULL, + [RotationY] [float] NULL, + [RotationZ] [float] NULL, + [RotationW] [float] NULL, + [SitTargetOffsetX] [float] NULL, + [SitTargetOffsetY] [float] NULL, + [SitTargetOffsetZ] [float] NULL, + [SitTargetOrientW] [float] NULL, + [SitTargetOrientX] [float] NULL, + [SitTargetOrientY] [float] NULL, + [SitTargetOrientZ] [float] NULL, +PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +CREATE TABLE [dbo].[primshapes]( + [UUID] [varchar](255) NOT NULL, + [Shape] [int] NULL, + [ScaleX] [float] NULL, + [ScaleY] [float] NULL, + [ScaleZ] [float] NULL, + [PCode] [int] NULL, + [PathBegin] [int] NULL, + [PathEnd] [int] NULL, + [PathScaleX] [int] NULL, + [PathScaleY] [int] NULL, + [PathShearX] [int] NULL, + [PathShearY] [int] NULL, + [PathSkew] [int] NULL, + [PathCurve] [int] NULL, + [PathRadiusOffset] [int] NULL, + [PathRevolutions] [int] NULL, + [PathTaperX] [int] NULL, + [PathTaperY] [int] NULL, + [PathTwist] [int] NULL, + [PathTwistBegin] [int] NULL, + [ProfileBegin] [int] NULL, + [ProfileEnd] [int] NULL, + [ProfileCurve] [int] NULL, + [ProfileHollow] [int] NULL, + [State] [int] NULL, + [Texture] [image] NULL, + [ExtraParams] [image] NULL, +PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +CREATE TABLE [dbo].[primitems]( + [itemID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [primID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [assetID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [parentFolderID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [invType] [int] NULL, + [assetType] [int] NULL, + [name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [creationDate] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [creatorID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [ownerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [lastOwnerID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [groupID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [nextPermissions] [int] NULL, + [currentPermissions] [int] NULL, + [basePermissions] [int] NULL, + [everyonePermissions] [int] NULL, + [groupPermissions] [int] NULL, +PRIMARY KEY CLUSTERED +( + [itemID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +CREATE TABLE [dbo].[terrain]( + [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [Revision] [int] NULL, + [Heightfield] [image] NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +CREATE TABLE [dbo].[land]( + [UUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [RegionUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [LocalLandID] [int] NULL, + [Bitmap] [image] NULL, + [Name] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [Description] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [OwnerUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [IsGroupOwned] [int] NULL, + [Area] [int] NULL, + [AuctionID] [int] NULL, + [Category] [int] NULL, + [ClaimDate] [int] NULL, + [ClaimPrice] [int] NULL, + [GroupUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [SalePrice] [int] NULL, + [LandStatus] [int] NULL, + [LandFlags] [int] NULL, + [LandingType] [int] NULL, + [MediaAutoScale] [int] NULL, + [MediaTextureUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [MediaURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [MusicURL] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [PassHours] [float] NULL, + [PassPrice] [int] NULL, + [SnapshotUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [UserLocationX] [float] NULL, + [UserLocationY] [float] NULL, + [UserLocationZ] [float] NULL, + [UserLookAtX] [float] NULL, + [UserLookAtY] [float] NULL, + [UserLookAtZ] [float] NULL, +PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + +CREATE TABLE [dbo].[landaccesslist]( + [LandUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [AccessUUID] [varchar](255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL, + [Flags] [int] NULL +) ON [PRIMARY] + +:VERSION 2 + +BEGIN TRANSACTION + +CREATE TABLE regionban ( + [regionUUID] VARCHAR(36) NOT NULL, + [bannedUUID] VARCHAR(36) NOT NULL, + [bannedIp] VARCHAR(16) NOT NULL, + [bannedIpHostMask] VARCHAR(16) NOT NULL) + +create table [dbo].[regionsettings] ( + [regionUUID] [varchar](36) not null, + [block_terraform] [bit] not null, + [block_fly] [bit] not null, + [allow_damage] [bit] not null, + [restrict_pushing] [bit] not null, + [allow_land_resell] [bit] not null, + [allow_land_join_divide] [bit] not null, + [block_show_in_search] [bit] not null, + [agent_limit] [int] not null, + [object_bonus] [float] not null, + [maturity] [int] not null, + [disable_scripts] [bit] not null, + [disable_collisions] [bit] not null, + [disable_physics] [bit] not null, + [terrain_texture_1] [varchar](36) not null, + [terrain_texture_2] [varchar](36) not null, + [terrain_texture_3] [varchar](36) not null, + [terrain_texture_4] [varchar](36) not null, + [elevation_1_nw] [float] not null, + [elevation_2_nw] [float] not null, + [elevation_1_ne] [float] not null, + [elevation_2_ne] [float] not null, + [elevation_1_se] [float] not null, + [elevation_2_se] [float] not null, + [elevation_1_sw] [float] not null, + [elevation_2_sw] [float] not null, + [water_height] [float] not null, + [terrain_raise_limit] [float] not null, + [terrain_lower_limit] [float] not null, + [use_estate_sun] [bit] not null, + [fixed_sun] [bit] not null, + [sun_position] [float] not null, + [covenant] [varchar](36) default NULL, + [Sandbox] [bit] NOT NULL, +PRIMARY KEY CLUSTERED +( + [regionUUID] ASC +)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] +) ON [PRIMARY] + +COMMIT + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_prims + ( + UUID varchar(36) NOT NULL, + RegionUUID varchar(36) NULL, + ParentID int NULL, + CreationDate int NULL, + Name varchar(255) NULL, + SceneGroupID varchar(36) NULL, + Text varchar(255) NULL, + Description varchar(255) NULL, + SitName varchar(255) NULL, + TouchName varchar(255) NULL, + ObjectFlags int NULL, + CreatorID varchar(36) NULL, + OwnerID varchar(36) NULL, + GroupID varchar(36) NULL, + LastOwnerID varchar(36) NULL, + OwnerMask int NULL, + NextOwnerMask int NULL, + GroupMask int NULL, + EveryoneMask int NULL, + BaseMask int NULL, + PositionX float(53) NULL, + PositionY float(53) NULL, + PositionZ float(53) NULL, + GroupPositionX float(53) NULL, + GroupPositionY float(53) NULL, + GroupPositionZ float(53) NULL, + VelocityX float(53) NULL, + VelocityY float(53) NULL, + VelocityZ float(53) NULL, + AngularVelocityX float(53) NULL, + AngularVelocityY float(53) NULL, + AngularVelocityZ float(53) NULL, + AccelerationX float(53) NULL, + AccelerationY float(53) NULL, + AccelerationZ float(53) NULL, + RotationX float(53) NULL, + RotationY float(53) NULL, + RotationZ float(53) NULL, + RotationW float(53) NULL, + SitTargetOffsetX float(53) NULL, + SitTargetOffsetY float(53) NULL, + SitTargetOffsetZ float(53) NULL, + SitTargetOrientW float(53) NULL, + SitTargetOrientX float(53) NULL, + SitTargetOrientY float(53) NULL, + SitTargetOrientZ float(53) NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.prims) + EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ) + SELECT CONVERT(varchar(36), UUID), CONVERT(varchar(36), RegionUUID), ParentID, CreationDate, Name, CONVERT(varchar(36), SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(varchar(36), CreatorID), CONVERT(varchar(36), OwnerID), CONVERT(varchar(36), GroupID), CONVERT(varchar(36), LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.prims + +EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' + +ALTER TABLE dbo.prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_primitems + ( + itemID varchar(36) NOT NULL, + primID varchar(36) NULL, + assetID varchar(36) NULL, + parentFolderID varchar(36) NULL, + invType int NULL, + assetType int NULL, + name varchar(255) NULL, + description varchar(255) NULL, + creationDate varchar(255) NULL, + creatorID varchar(36) NULL, + ownerID varchar(36) NULL, + lastOwnerID varchar(36) NULL, + groupID varchar(36) NULL, + nextPermissions int NULL, + currentPermissions int NULL, + basePermissions int NULL, + everyonePermissions int NULL, + groupPermissions int NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM primitems) + EXEC('INSERT INTO Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions) + SELECT CONVERT(varchar(36), itemID), CONVERT(varchar(36), primID), CONVERT(varchar(36), assetID), CONVERT(varchar(36), parentFolderID), invType, assetType, name, description, creationDate, CONVERT(varchar(36), creatorID), CONVERT(varchar(36), ownerID), CONVERT(varchar(36), lastOwnerID), CONVERT(varchar(36), groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions') + +DROP TABLE primitems + +EXECUTE sp_rename N'Tmp_primitems', N'primitems', 'OBJECT' + +ALTER TABLE primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED + ( + itemID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_primshapes + ( + UUID varchar(36) NOT NULL, + Shape int NULL, + ScaleX float(53) NULL, + ScaleY float(53) NULL, + ScaleZ float(53) NULL, + PCode int NULL, + PathBegin int NULL, + PathEnd int NULL, + PathScaleX int NULL, + PathScaleY int NULL, + PathShearX int NULL, + PathShearY int NULL, + PathSkew int NULL, + PathCurve int NULL, + PathRadiusOffset int NULL, + PathRevolutions int NULL, + PathTaperX int NULL, + PathTaperY int NULL, + PathTwist int NULL, + PathTwistBegin int NULL, + ProfileBegin int NULL, + ProfileEnd int NULL, + ProfileCurve int NULL, + ProfileHollow int NULL, + State int NULL, + Texture image NULL, + ExtraParams image NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM primshapes) + EXEC('INSERT INTO Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) + SELECT CONVERT(varchar(36), UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM primshapes WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE primshapes + +EXECUTE sp_rename N'Tmp_primshapes', N'primshapes', 'OBJECT' + +ALTER TABLE primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD PayPrice int not null default 0 +ALTER TABLE prims ADD PayButton1 int not null default 0 +ALTER TABLE prims ADD PayButton2 int not null default 0 +ALTER TABLE prims ADD PayButton3 int not null default 0 +ALTER TABLE prims ADD PayButton4 int not null default 0 +ALTER TABLE prims ADD LoopedSound varchar(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD LoopedSoundGain float not null default 0.0; +ALTER TABLE prims ADD TextureAnimation image +ALTER TABLE prims ADD OmegaX float not null default 0.0 +ALTER TABLE prims ADD OmegaY float not null default 0.0 +ALTER TABLE prims ADD OmegaZ float not null default 0.0 +ALTER TABLE prims ADD CameraEyeOffsetX float not null default 0.0 +ALTER TABLE prims ADD CameraEyeOffsetY float not null default 0.0 +ALTER TABLE prims ADD CameraEyeOffsetZ float not null default 0.0 +ALTER TABLE prims ADD CameraAtOffsetX float not null default 0.0 +ALTER TABLE prims ADD CameraAtOffsetY float not null default 0.0 +ALTER TABLE prims ADD CameraAtOffsetZ float not null default 0.0 +ALTER TABLE prims ADD ForceMouselook tinyint not null default 0 +ALTER TABLE prims ADD ScriptAccessPin int not null default 0 +ALTER TABLE prims ADD AllowedDrop tinyint not null default 0 +ALTER TABLE prims ADD DieAtEdge tinyint not null default 0 +ALTER TABLE prims ADD SalePrice int not null default 10 +ALTER TABLE prims ADD SaleType tinyint not null default 0 + +ALTER TABLE primitems add flags integer not null default 0 + +ALTER TABLE land ADD AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000' + +CREATE index prims_regionuuid on prims(RegionUUID) +CREATE index prims_parentid on prims(ParentID) + +CREATE index primitems_primid on primitems(primID) + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD ColorR int not null default 0; +ALTER TABLE prims ADD ColorG int not null default 0; +ALTER TABLE prims ADD ColorB int not null default 0; +ALTER TABLE prims ADD ColorA int not null default 0; +ALTER TABLE prims ADD ParticleSystem IMAGE; +ALTER TABLE prims ADD ClickAction tinyint NOT NULL default 0; + +COMMIT + + +:VERSION 8 + +BEGIN TRANSACTION + +ALTER TABLE land ADD OtherCleanTime integer NOT NULL default 0; +ALTER TABLE land ADD Dwell integer NOT NULL default 0; + +COMMIT + +:VERSION 9 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD Material tinyint NOT NULL default 3 + +COMMIT + + +:VERSION 10 + +BEGIN TRANSACTION + +ALTER TABLE regionsettings ADD sunvectorx float NOT NULL default 0; +ALTER TABLE regionsettings ADD sunvectory float NOT NULL default 0; +ALTER TABLE regionsettings ADD sunvectorz float NOT NULL default 0; + +COMMIT + + +:VERSION 11 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000' +ALTER TABLE prims ADD CollisionSoundVolume float not null default 0.0 + +COMMIT + + +:VERSION 12 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD LinkNumber integer not null default 0 + +COMMIT + + +:VERSION 13 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_prims + ( + UUID uniqueidentifier NOT NULL, + RegionUUID uniqueidentifier NULL, + ParentID int NULL, + CreationDate int NULL, + Name varchar(255) NULL, + SceneGroupID uniqueidentifier NULL, + Text varchar(255) NULL, + Description varchar(255) NULL, + SitName varchar(255) NULL, + TouchName varchar(255) NULL, + ObjectFlags int NULL, + CreatorID uniqueidentifier NULL, + OwnerID uniqueidentifier NULL, + GroupID uniqueidentifier NULL, + LastOwnerID uniqueidentifier NULL, + OwnerMask int NULL, + NextOwnerMask int NULL, + GroupMask int NULL, + EveryoneMask int NULL, + BaseMask int NULL, + PositionX float(53) NULL, + PositionY float(53) NULL, + PositionZ float(53) NULL, + GroupPositionX float(53) NULL, + GroupPositionY float(53) NULL, + GroupPositionZ float(53) NULL, + VelocityX float(53) NULL, + VelocityY float(53) NULL, + VelocityZ float(53) NULL, + AngularVelocityX float(53) NULL, + AngularVelocityY float(53) NULL, + AngularVelocityZ float(53) NULL, + AccelerationX float(53) NULL, + AccelerationY float(53) NULL, + AccelerationZ float(53) NULL, + RotationX float(53) NULL, + RotationY float(53) NULL, + RotationZ float(53) NULL, + RotationW float(53) NULL, + SitTargetOffsetX float(53) NULL, + SitTargetOffsetY float(53) NULL, + SitTargetOffsetZ float(53) NULL, + SitTargetOrientW float(53) NULL, + SitTargetOrientX float(53) NULL, + SitTargetOrientY float(53) NULL, + SitTargetOrientZ float(53) NULL, + PayPrice int NOT NULL DEFAULT ((0)), + PayButton1 int NOT NULL DEFAULT ((0)), + PayButton2 int NOT NULL DEFAULT ((0)), + PayButton3 int NOT NULL DEFAULT ((0)), + PayButton4 int NOT NULL DEFAULT ((0)), + LoopedSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + LoopedSoundGain float(53) NOT NULL DEFAULT ((0.0)), + TextureAnimation image NULL, + OmegaX float(53) NOT NULL DEFAULT ((0.0)), + OmegaY float(53) NOT NULL DEFAULT ((0.0)), + OmegaZ float(53) NOT NULL DEFAULT ((0.0)), + CameraEyeOffsetX float(53) NOT NULL DEFAULT ((0.0)), + CameraEyeOffsetY float(53) NOT NULL DEFAULT ((0.0)), + CameraEyeOffsetZ float(53) NOT NULL DEFAULT ((0.0)), + CameraAtOffsetX float(53) NOT NULL DEFAULT ((0.0)), + CameraAtOffsetY float(53) NOT NULL DEFAULT ((0.0)), + CameraAtOffsetZ float(53) NOT NULL DEFAULT ((0.0)), + ForceMouselook tinyint NOT NULL DEFAULT ((0)), + ScriptAccessPin int NOT NULL DEFAULT ((0)), + AllowedDrop tinyint NOT NULL DEFAULT ((0)), + DieAtEdge tinyint NOT NULL DEFAULT ((0)), + SalePrice int NOT NULL DEFAULT ((10)), + SaleType tinyint NOT NULL DEFAULT ((0)), + ColorR int NOT NULL DEFAULT ((0)), + ColorG int NOT NULL DEFAULT ((0)), + ColorB int NOT NULL DEFAULT ((0)), + ColorA int NOT NULL DEFAULT ((0)), + ParticleSystem image NULL, + ClickAction tinyint NOT NULL DEFAULT ((0)), + Material tinyint NOT NULL DEFAULT ((3)), + CollisionSound uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + CollisionSoundVolume float(53) NOT NULL DEFAULT ((0.0)), + LinkNumber int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.prims) + EXEC('INSERT INTO dbo.Tmp_prims (UUID, RegionUUID, ParentID, CreationDate, Name, SceneGroupID, Text, Description, SitName, TouchName, ObjectFlags, CreatorID, OwnerID, GroupID, LastOwnerID, OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, LoopedSound, LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, LinkNumber) + SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), ParentID, CreationDate, Name, CONVERT(uniqueidentifier, SceneGroupID), Text, Description, SitName, TouchName, ObjectFlags, CONVERT(uniqueidentifier, CreatorID), CONVERT(uniqueidentifier, OwnerID), CONVERT(uniqueidentifier, GroupID), CONVERT(uniqueidentifier, LastOwnerID), OwnerMask, NextOwnerMask, GroupMask, EveryoneMask, BaseMask, PositionX, PositionY, PositionZ, GroupPositionX, GroupPositionY, GroupPositionZ, VelocityX, VelocityY, VelocityZ, AngularVelocityX, AngularVelocityY, AngularVelocityZ, AccelerationX, AccelerationY, AccelerationZ, RotationX, RotationY, RotationZ, RotationW, SitTargetOffsetX, SitTargetOffsetY, SitTargetOffsetZ, SitTargetOrientW, SitTargetOrientX, SitTargetOrientY, SitTargetOrientZ, PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, CONVERT(uniqueidentifier, LoopedSound), LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, ParticleSystem, ClickAction, Material, CONVERT(uniqueidentifier, CollisionSound), CollisionSoundVolume, LinkNumber FROM dbo.prims WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.prims + +EXECUTE sp_rename N'dbo.Tmp_prims', N'prims', 'OBJECT' + +ALTER TABLE dbo.prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX prims_regionuuid ON dbo.prims + ( + RegionUUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX prims_parentid ON dbo.prims + ( + ParentID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 14 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_primshapes + ( + UUID uniqueidentifier NOT NULL, + Shape int NULL, + ScaleX float(53) NULL, + ScaleY float(53) NULL, + ScaleZ float(53) NULL, + PCode int NULL, + PathBegin int NULL, + PathEnd int NULL, + PathScaleX int NULL, + PathScaleY int NULL, + PathShearX int NULL, + PathShearY int NULL, + PathSkew int NULL, + PathCurve int NULL, + PathRadiusOffset int NULL, + PathRevolutions int NULL, + PathTaperX int NULL, + PathTaperY int NULL, + PathTwist int NULL, + PathTwistBegin int NULL, + ProfileBegin int NULL, + ProfileEnd int NULL, + ProfileCurve int NULL, + ProfileHollow int NULL, + State int NULL, + Texture image NULL, + ExtraParams image NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.primshapes) + EXEC('INSERT INTO dbo.Tmp_primshapes (UUID, Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams) + SELECT CONVERT(uniqueidentifier, UUID), Shape, ScaleX, ScaleY, ScaleZ, PCode, PathBegin, PathEnd, PathScaleX, PathScaleY, PathShearX, PathShearY, PathSkew, PathCurve, PathRadiusOffset, PathRevolutions, PathTaperX, PathTaperY, PathTwist, PathTwistBegin, ProfileBegin, ProfileEnd, ProfileCurve, ProfileHollow, State, Texture, ExtraParams FROM dbo.primshapes WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.primshapes + +EXECUTE sp_rename N'dbo.Tmp_primshapes', N'primshapes', 'OBJECT' + +ALTER TABLE dbo.primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 15 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_primitems + ( + itemID uniqueidentifier NOT NULL, + primID uniqueidentifier NULL, + assetID uniqueidentifier NULL, + parentFolderID uniqueidentifier NULL, + invType int NULL, + assetType int NULL, + name varchar(255) NULL, + description varchar(255) NULL, + creationDate varchar(255) NULL, + creatorID uniqueidentifier NULL, + ownerID uniqueidentifier NULL, + lastOwnerID uniqueidentifier NULL, + groupID uniqueidentifier NULL, + nextPermissions int NULL, + currentPermissions int NULL, + basePermissions int NULL, + everyonePermissions int NULL, + groupPermissions int NULL, + flags int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.primitems) + EXEC('INSERT INTO dbo.Tmp_primitems (itemID, primID, assetID, parentFolderID, invType, assetType, name, description, creationDate, creatorID, ownerID, lastOwnerID, groupID, nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags) + SELECT CONVERT(uniqueidentifier, itemID), CONVERT(uniqueidentifier, primID), CONVERT(uniqueidentifier, assetID), CONVERT(uniqueidentifier, parentFolderID), invType, assetType, name, description, creationDate, CONVERT(uniqueidentifier, creatorID), CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, lastOwnerID), CONVERT(uniqueidentifier, groupID), nextPermissions, currentPermissions, basePermissions, everyonePermissions, groupPermissions, flags FROM dbo.primitems WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.primitems + +EXECUTE sp_rename N'dbo.Tmp_primitems', N'primitems', 'OBJECT' + +ALTER TABLE dbo.primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY CLUSTERED + ( + itemID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX primitems_primid ON dbo.primitems + ( + primID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 16 + + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_terrain + ( + RegionUUID uniqueidentifier NULL, + Revision int NULL, + Heightfield image NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.terrain) + EXEC('INSERT INTO dbo.Tmp_terrain (RegionUUID, Revision, Heightfield) + SELECT CONVERT(uniqueidentifier, RegionUUID), Revision, Heightfield FROM dbo.terrain WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.terrain + +EXECUTE sp_rename N'dbo.Tmp_terrain', N'terrain', 'OBJECT' + +COMMIT + + +:VERSION 17 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_land + ( + UUID uniqueidentifier NOT NULL, + RegionUUID uniqueidentifier NULL, + LocalLandID int NULL, + Bitmap image NULL, + Name varchar(255) NULL, + Description varchar(255) NULL, + OwnerUUID uniqueidentifier NULL, + IsGroupOwned int NULL, + Area int NULL, + AuctionID int NULL, + Category int NULL, + ClaimDate int NULL, + ClaimPrice int NULL, + GroupUUID uniqueidentifier NULL, + SalePrice int NULL, + LandStatus int NULL, + LandFlags int NULL, + LandingType int NULL, + MediaAutoScale int NULL, + MediaTextureUUID uniqueidentifier NULL, + MediaURL varchar(255) NULL, + MusicURL varchar(255) NULL, + PassHours float(53) NULL, + PassPrice int NULL, + SnapshotUUID uniqueidentifier NULL, + UserLocationX float(53) NULL, + UserLocationY float(53) NULL, + UserLocationZ float(53) NULL, + UserLookAtX float(53) NULL, + UserLookAtY float(53) NULL, + UserLookAtZ float(53) NULL, + AuthbuyerID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + OtherCleanTime int NOT NULL DEFAULT ((0)), + Dwell int NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.land) + EXEC('INSERT INTO dbo.Tmp_land (UUID, RegionUUID, LocalLandID, Bitmap, Name, Description, OwnerUUID, IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, GroupUUID, SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, MediaTextureUUID, MediaURL, MusicURL, PassHours, PassPrice, SnapshotUUID, UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, AuthbuyerID, OtherCleanTime, Dwell) + SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, RegionUUID), LocalLandID, Bitmap, Name, Description, CONVERT(uniqueidentifier, OwnerUUID), IsGroupOwned, Area, AuctionID, Category, ClaimDate, ClaimPrice, CONVERT(uniqueidentifier, GroupUUID), SalePrice, LandStatus, LandFlags, LandingType, MediaAutoScale, CONVERT(uniqueidentifier, MediaTextureUUID), MediaURL, MusicURL, PassHours, PassPrice, CONVERT(uniqueidentifier, SnapshotUUID), UserLocationX, UserLocationY, UserLocationZ, UserLookAtX, UserLookAtY, UserLookAtZ, CONVERT(uniqueidentifier, AuthbuyerID), OtherCleanTime, Dwell FROM dbo.land WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.land + +EXECUTE sp_rename N'dbo.Tmp_land', N'land', 'OBJECT' + +ALTER TABLE dbo.land ADD CONSTRAINT + PK__land__65A475E71BFD2C07 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + + +:VERSION 18 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_landaccesslist + ( + LandUUID uniqueidentifier NULL, + AccessUUID uniqueidentifier NULL, + Flags int NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.landaccesslist) + EXEC('INSERT INTO dbo.Tmp_landaccesslist (LandUUID, AccessUUID, Flags) + SELECT CONVERT(uniqueidentifier, LandUUID), CONVERT(uniqueidentifier, AccessUUID), Flags FROM dbo.landaccesslist WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.landaccesslist + +EXECUTE sp_rename N'dbo.Tmp_landaccesslist', N'landaccesslist', 'OBJECT' + +COMMIT + + + +:VERSION 19 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_regionban + ( + regionUUID uniqueidentifier NOT NULL, + bannedUUID uniqueidentifier NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.regionban) + EXEC('INSERT INTO dbo.Tmp_regionban (regionUUID, bannedUUID, bannedIp, bannedIpHostMask) + SELECT CONVERT(uniqueidentifier, regionUUID), CONVERT(uniqueidentifier, bannedUUID), bannedIp, bannedIpHostMask FROM dbo.regionban WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.regionban + +EXECUTE sp_rename N'dbo.Tmp_regionban', N'regionban', 'OBJECT' + +COMMIT + + +:VERSION 20 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_regionsettings + ( + regionUUID uniqueidentifier NOT NULL, + block_terraform bit NOT NULL, + block_fly bit NOT NULL, + allow_damage bit NOT NULL, + restrict_pushing bit NOT NULL, + allow_land_resell bit NOT NULL, + allow_land_join_divide bit NOT NULL, + block_show_in_search bit NOT NULL, + agent_limit int NOT NULL, + object_bonus float(53) NOT NULL, + maturity int NOT NULL, + disable_scripts bit NOT NULL, + disable_collisions bit NOT NULL, + disable_physics bit NOT NULL, + terrain_texture_1 uniqueidentifier NOT NULL, + terrain_texture_2 uniqueidentifier NOT NULL, + terrain_texture_3 uniqueidentifier NOT NULL, + terrain_texture_4 uniqueidentifier NOT NULL, + elevation_1_nw float(53) NOT NULL, + elevation_2_nw float(53) NOT NULL, + elevation_1_ne float(53) NOT NULL, + elevation_2_ne float(53) NOT NULL, + elevation_1_se float(53) NOT NULL, + elevation_2_se float(53) NOT NULL, + elevation_1_sw float(53) NOT NULL, + elevation_2_sw float(53) NOT NULL, + water_height float(53) NOT NULL, + terrain_raise_limit float(53) NOT NULL, + terrain_lower_limit float(53) NOT NULL, + use_estate_sun bit NOT NULL, + fixed_sun bit NOT NULL, + sun_position float(53) NOT NULL, + covenant uniqueidentifier NULL DEFAULT (NULL), + Sandbox bit NOT NULL, + sunvectorx float(53) NOT NULL DEFAULT ((0)), + sunvectory float(53) NOT NULL DEFAULT ((0)), + sunvectorz float(53) NOT NULL DEFAULT ((0)) + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.regionsettings) + EXEC('INSERT INTO dbo.Tmp_regionsettings (regionUUID, block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, terrain_texture_1, terrain_texture_2, terrain_texture_3, terrain_texture_4, elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, covenant, Sandbox, sunvectorx, sunvectory, sunvectorz) + SELECT CONVERT(uniqueidentifier, regionUUID), block_terraform, block_fly, allow_damage, restrict_pushing, allow_land_resell, allow_land_join_divide, block_show_in_search, agent_limit, object_bonus, maturity, disable_scripts, disable_collisions, disable_physics, CONVERT(uniqueidentifier, terrain_texture_1), CONVERT(uniqueidentifier, terrain_texture_2), CONVERT(uniqueidentifier, terrain_texture_3), CONVERT(uniqueidentifier, terrain_texture_4), elevation_1_nw, elevation_2_nw, elevation_1_ne, elevation_2_ne, elevation_1_se, elevation_2_se, elevation_1_sw, elevation_2_sw, water_height, terrain_raise_limit, terrain_lower_limit, use_estate_sun, fixed_sun, sun_position, CONVERT(uniqueidentifier, covenant), Sandbox, sunvectorx, sunvectory, sunvectorz FROM dbo.regionsettings WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.regionsettings + +EXECUTE sp_rename N'dbo.Tmp_regionsettings', N'regionsettings', 'OBJECT' + +ALTER TABLE dbo.regionsettings ADD CONSTRAINT + PK__regionse__5B35159D21B6055D PRIMARY KEY CLUSTERED + ( + regionUUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 21 + +BEGIN TRANSACTION + +ALTER TABLE prims ADD PassTouches bit not null default 0 + +COMMIT + + +:VERSION 22 + +BEGIN TRANSACTION + +ALTER TABLE regionsettings ADD loaded_creation_date varchar(20) +ALTER TABLE regionsettings ADD loaded_creation_time varchar(20) +ALTER TABLE regionsettings ADD loaded_creation_id varchar(64) + +COMMIT + +:VERSION 23 + +BEGIN TRANSACTION + +ALTER TABLE regionsettings DROP COLUMN loaded_creation_date +ALTER TABLE regionsettings DROP COLUMN loaded_creation_time +ALTER TABLE regionsettings ADD loaded_creation_datetime int NOT NULL default 0 + +COMMIT + + + diff --git a/OpenSim/Data/MSSQL/Resources/UserAccount.migrations b/OpenSim/Data/MSSQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..8534e235c9 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/UserAccount.migrations @@ -0,0 +1,55 @@ +:VERSION 1 + +CREATE TABLE [UserAccounts] ( + [PrincipalID] uniqueidentifier NOT NULL, + [ScopeID] uniqueidentifier NOT NULL, + [FirstName] [varchar](64) NOT NULL, + [LastName] [varchar](64) NOT NULL, + [Email] [varchar](64) NULL, + [ServiceURLs] [text] NULL, + [Created] [int] default NULL, + + PRIMARY KEY CLUSTERED +( + [PrincipalID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +:VERSION 2 + +BEGIN TRANSACTION + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT [UUID] AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, +username AS FirstName, +lastname AS LastName, +email as Email, ( +'AssetServerURI=' + +userAssetURI + ' InventoryServerURI=' + userInventoryURI + ' GatewayURI= HomeURI=') AS ServiceURLs, +created as Created FROM users; + + +COMMIT + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); +CREATE INDEX Email ON UserAccounts(Email); +CREATE INDEX FirstName ON UserAccounts(FirstName); +CREATE INDEX LastName ON UserAccounts(LastName); +CREATE INDEX Name ON UserAccounts(FirstName,LastName); + +COMMIT + +:VERSION 4 + +BEGIN TRANSACTION + +ALTER TABLE UserAccounts ADD UserLevel integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD UserFlags integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD UserTitle varchar(64) NOT NULL DEFAULT ''; + +COMMIT + diff --git a/OpenSim/Data/MSSQL/Resources/UserStore.migrations b/OpenSim/Data/MSSQL/Resources/UserStore.migrations new file mode 100644 index 0000000000..050c544f64 --- /dev/null +++ b/OpenSim/Data/MSSQL/Resources/UserStore.migrations @@ -0,0 +1,421 @@ +:VERSION 1 + +CREATE TABLE [users] ( + [UUID] [varchar](36) NOT NULL default '', + [username] [varchar](32) NOT NULL, + [lastname] [varchar](32) NOT NULL, + [passwordHash] [varchar](32) NOT NULL, + [passwordSalt] [varchar](32) NOT NULL, + [homeRegion] [bigint] default NULL, + [homeLocationX] [float] default NULL, + [homeLocationY] [float] default NULL, + [homeLocationZ] [float] default NULL, + [homeLookAtX] [float] default NULL, + [homeLookAtY] [float] default NULL, + [homeLookAtZ] [float] default NULL, + [created] [int] NOT NULL, + [lastLogin] [int] NOT NULL, + [userInventoryURI] [varchar](255) default NULL, + [userAssetURI] [varchar](255) default NULL, + [profileCanDoMask] [int] default NULL, + [profileWantDoMask] [int] default NULL, + [profileAboutText] [ntext], + [profileFirstText] [ntext], + [profileImage] [varchar](36) default NULL, + [profileFirstImage] [varchar](36) default NULL, + [webLoginKey] [varchar](36) default NULL, + PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX [usernames] ON [users] +( + [username] ASC, + [lastname] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE TABLE [agents] ( + [UUID] [varchar](36) NOT NULL, + [sessionID] [varchar](36) NOT NULL, + [secureSessionID] [varchar](36) NOT NULL, + [agentIP] [varchar](16) NOT NULL, + [agentPort] [int] NOT NULL, + [agentOnline] [tinyint] NOT NULL, + [loginTime] [int] NOT NULL, + [logoutTime] [int] NOT NULL, + [currentRegion] [varchar](36) NOT NULL, + [currentHandle] [bigint] NOT NULL, + [currentPos] [varchar](64) NOT NULL, + PRIMARY KEY CLUSTERED +( + [UUID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + + +CREATE NONCLUSTERED INDEX [session] ON [agents] +( + [sessionID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX [ssession] ON [agents] +( + [secureSessionID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +CREATE TABLE [dbo].[userfriends]( + [ownerID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, + [friendID] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL, + [friendPerms] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [datetimestamp] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL +) ON [PRIMARY] + +CREATE TABLE [avatarappearance] ( + [Owner] [varchar](36) NOT NULL, + [Serial] int NOT NULL, + [Visual_Params] [image] NOT NULL, + [Texture] [image] NOT NULL, + [Avatar_Height] float NOT NULL, + [Body_Item] [varchar](36) NOT NULL, + [Body_Asset] [varchar](36) NOT NULL, + [Skin_Item] [varchar](36) NOT NULL, + [Skin_Asset] [varchar](36) NOT NULL, + [Hair_Item] [varchar](36) NOT NULL, + [Hair_Asset] [varchar](36) NOT NULL, + [Eyes_Item] [varchar](36) NOT NULL, + [Eyes_Asset] [varchar](36) NOT NULL, + [Shirt_Item] [varchar](36) NOT NULL, + [Shirt_Asset] [varchar](36) NOT NULL, + [Pants_Item] [varchar](36) NOT NULL, + [Pants_Asset] [varchar](36) NOT NULL, + [Shoes_Item] [varchar](36) NOT NULL, + [Shoes_Asset] [varchar](36) NOT NULL, + [Socks_Item] [varchar](36) NOT NULL, + [Socks_Asset] [varchar](36) NOT NULL, + [Jacket_Item] [varchar](36) NOT NULL, + [Jacket_Asset] [varchar](36) NOT NULL, + [Gloves_Item] [varchar](36) NOT NULL, + [Gloves_Asset] [varchar](36) NOT NULL, + [Undershirt_Item] [varchar](36) NOT NULL, + [Undershirt_Asset] [varchar](36) NOT NULL, + [Underpants_Item] [varchar](36) NOT NULL, + [Underpants_Asset] [varchar](36) NOT NULL, + [Skirt_Item] [varchar](36) NOT NULL, + [Skirt_Asset] [varchar](36) NOT NULL, + + PRIMARY KEY CLUSTERED ( + [Owner] + ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +:VERSION 2 + +BEGIN TRANSACTION + +ALTER TABLE users ADD homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE users ADD userFlags int NOT NULL default 0; +ALTER TABLE users ADD godLevel int NOT NULL default 0; +ALTER TABLE users ADD customType varchar(32) not null default ''; +ALTER TABLE users ADD partner varchar(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT + + +:VERSION 3 + +BEGIN TRANSACTION + +CREATE TABLE [avatarattachments] ( + [UUID] varchar(36) NOT NULL + , [attachpoint] int NOT NULL + , [item] varchar(36) NOT NULL + , [asset] varchar(36) NOT NULL) + +CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + + +COMMIT + + +:VERSION 4 + +BEGIN TRANSACTION + +CREATE TABLE Tmp_userfriends + ( + ownerID varchar(36) NOT NULL, + friendID varchar(36) NOT NULL, + friendPerms int NOT NULL, + datetimestamp int NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM userfriends) + EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) + SELECT CONVERT(varchar(36), ownerID), CONVERT(varchar(36), friendID), CONVERT(int, friendPerms), CONVERT(int, datetimestamp) FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.userfriends + +EXECUTE sp_rename N'Tmp_userfriends', N'userfriends', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON userfriends + ( + ownerID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON userfriends + ( + friendID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 5 + +BEGIN TRANSACTION + + ALTER TABLE users add email varchar(250); + +COMMIT + + +:VERSION 6 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_users + ( + UUID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + username varchar(32) NOT NULL, + lastname varchar(32) NOT NULL, + passwordHash varchar(32) NOT NULL, + passwordSalt varchar(32) NOT NULL, + homeRegion bigint NULL DEFAULT (NULL), + homeLocationX float(53) NULL DEFAULT (NULL), + homeLocationY float(53) NULL DEFAULT (NULL), + homeLocationZ float(53) NULL DEFAULT (NULL), + homeLookAtX float(53) NULL DEFAULT (NULL), + homeLookAtY float(53) NULL DEFAULT (NULL), + homeLookAtZ float(53) NULL DEFAULT (NULL), + created int NOT NULL, + lastLogin int NOT NULL, + userInventoryURI varchar(255) NULL DEFAULT (NULL), + userAssetURI varchar(255) NULL DEFAULT (NULL), + profileCanDoMask int NULL DEFAULT (NULL), + profileWantDoMask int NULL DEFAULT (NULL), + profileAboutText ntext NULL, + profileFirstText ntext NULL, + profileImage uniqueidentifier NULL DEFAULT (NULL), + profileFirstImage uniqueidentifier NULL DEFAULT (NULL), + webLoginKey uniqueidentifier NULL DEFAULT (NULL), + homeRegionID uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + userFlags int NOT NULL DEFAULT ((0)), + godLevel int NOT NULL DEFAULT ((0)), + customType varchar(32) NOT NULL DEFAULT (''), + partner uniqueidentifier NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + email varchar(250) NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.users) + EXEC('INSERT INTO dbo.Tmp_users (UUID, username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, profileImage, profileFirstImage, webLoginKey, homeRegionID, userFlags, godLevel, customType, partner, email) + SELECT CONVERT(uniqueidentifier, UUID), username, lastname, passwordHash, passwordSalt, homeRegion, homeLocationX, homeLocationY, homeLocationZ, homeLookAtX, homeLookAtY, homeLookAtZ, created, lastLogin, userInventoryURI, userAssetURI, profileCanDoMask, profileWantDoMask, profileAboutText, profileFirstText, CONVERT(uniqueidentifier, profileImage), CONVERT(uniqueidentifier, profileFirstImage), CONVERT(uniqueidentifier, webLoginKey), CONVERT(uniqueidentifier, homeRegionID), userFlags, godLevel, customType, CONVERT(uniqueidentifier, partner), email FROM dbo.users WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.users + +EXECUTE sp_rename N'dbo.Tmp_users', N'users', 'OBJECT' + +ALTER TABLE dbo.users ADD CONSTRAINT + PK__users__65A475E737A5467C PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX usernames ON dbo.users + ( + username, + lastname + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 7 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_agents + ( + UUID uniqueidentifier NOT NULL, + sessionID uniqueidentifier NOT NULL, + secureSessionID uniqueidentifier NOT NULL, + agentIP varchar(16) NOT NULL, + agentPort int NOT NULL, + agentOnline tinyint NOT NULL, + loginTime int NOT NULL, + logoutTime int NOT NULL, + currentRegion uniqueidentifier NOT NULL, + currentHandle bigint NOT NULL, + currentPos varchar(64) NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.agents) + EXEC('INSERT INTO dbo.Tmp_agents (UUID, sessionID, secureSessionID, agentIP, agentPort, agentOnline, loginTime, logoutTime, currentRegion, currentHandle, currentPos) + SELECT CONVERT(uniqueidentifier, UUID), CONVERT(uniqueidentifier, sessionID), CONVERT(uniqueidentifier, secureSessionID), agentIP, agentPort, agentOnline, loginTime, logoutTime, CONVERT(uniqueidentifier, currentRegion), currentHandle, currentPos FROM dbo.agents WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.agents + +EXECUTE sp_rename N'dbo.Tmp_agents', N'agents', 'OBJECT' + +ALTER TABLE dbo.agents ADD CONSTRAINT + PK__agents__65A475E749C3F6B7 PRIMARY KEY CLUSTERED + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX session ON dbo.agents + ( + sessionID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX ssession ON dbo.agents + ( + secureSessionID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 8 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_userfriends + ( + ownerID uniqueidentifier NOT NULL, + friendID uniqueidentifier NOT NULL, + friendPerms int NOT NULL, + datetimestamp int NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.userfriends) + EXEC('INSERT INTO dbo.Tmp_userfriends (ownerID, friendID, friendPerms, datetimestamp) + SELECT CONVERT(uniqueidentifier, ownerID), CONVERT(uniqueidentifier, friendID), friendPerms, datetimestamp FROM dbo.userfriends WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.userfriends + +EXECUTE sp_rename N'dbo.Tmp_userfriends', N'userfriends', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_userfriends_ownerID ON dbo.userfriends + ( + ownerID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +CREATE NONCLUSTERED INDEX IX_userfriends_friendID ON dbo.userfriends + ( + friendID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 9 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_avatarappearance + ( + Owner uniqueidentifier NOT NULL, + Serial int NOT NULL, + Visual_Params image NOT NULL, + Texture image NOT NULL, + Avatar_Height float(53) NOT NULL, + Body_Item uniqueidentifier NOT NULL, + Body_Asset uniqueidentifier NOT NULL, + Skin_Item uniqueidentifier NOT NULL, + Skin_Asset uniqueidentifier NOT NULL, + Hair_Item uniqueidentifier NOT NULL, + Hair_Asset uniqueidentifier NOT NULL, + Eyes_Item uniqueidentifier NOT NULL, + Eyes_Asset uniqueidentifier NOT NULL, + Shirt_Item uniqueidentifier NOT NULL, + Shirt_Asset uniqueidentifier NOT NULL, + Pants_Item uniqueidentifier NOT NULL, + Pants_Asset uniqueidentifier NOT NULL, + Shoes_Item uniqueidentifier NOT NULL, + Shoes_Asset uniqueidentifier NOT NULL, + Socks_Item uniqueidentifier NOT NULL, + Socks_Asset uniqueidentifier NOT NULL, + Jacket_Item uniqueidentifier NOT NULL, + Jacket_Asset uniqueidentifier NOT NULL, + Gloves_Item uniqueidentifier NOT NULL, + Gloves_Asset uniqueidentifier NOT NULL, + Undershirt_Item uniqueidentifier NOT NULL, + Undershirt_Asset uniqueidentifier NOT NULL, + Underpants_Item uniqueidentifier NOT NULL, + Underpants_Asset uniqueidentifier NOT NULL, + Skirt_Item uniqueidentifier NOT NULL, + Skirt_Asset uniqueidentifier NOT NULL + ) ON [PRIMARY] + TEXTIMAGE_ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.avatarappearance) + EXEC('INSERT INTO dbo.Tmp_avatarappearance (Owner, Serial, Visual_Params, Texture, Avatar_Height, Body_Item, Body_Asset, Skin_Item, Skin_Asset, Hair_Item, Hair_Asset, Eyes_Item, Eyes_Asset, Shirt_Item, Shirt_Asset, Pants_Item, Pants_Asset, Shoes_Item, Shoes_Asset, Socks_Item, Socks_Asset, Jacket_Item, Jacket_Asset, Gloves_Item, Gloves_Asset, Undershirt_Item, Undershirt_Asset, Underpants_Item, Underpants_Asset, Skirt_Item, Skirt_Asset) + SELECT CONVERT(uniqueidentifier, Owner), Serial, Visual_Params, Texture, Avatar_Height, CONVERT(uniqueidentifier, Body_Item), CONVERT(uniqueidentifier, Body_Asset), CONVERT(uniqueidentifier, Skin_Item), CONVERT(uniqueidentifier, Skin_Asset), CONVERT(uniqueidentifier, Hair_Item), CONVERT(uniqueidentifier, Hair_Asset), CONVERT(uniqueidentifier, Eyes_Item), CONVERT(uniqueidentifier, Eyes_Asset), CONVERT(uniqueidentifier, Shirt_Item), CONVERT(uniqueidentifier, Shirt_Asset), CONVERT(uniqueidentifier, Pants_Item), CONVERT(uniqueidentifier, Pants_Asset), CONVERT(uniqueidentifier, Shoes_Item), CONVERT(uniqueidentifier, Shoes_Asset), CONVERT(uniqueidentifier, Socks_Item), CONVERT(uniqueidentifier, Socks_Asset), CONVERT(uniqueidentifier, Jacket_Item), CONVERT(uniqueidentifier, Jacket_Asset), CONVERT(uniqueidentifier, Gloves_Item), CONVERT(uniqueidentifier, Gloves_Asset), CONVERT(uniqueidentifier, Undershirt_Item), CONVERT(uniqueidentifier, Undershirt_Asset), CONVERT(uniqueidentifier, Underpants_Item), CONVERT(uniqueidentifier, Underpants_Asset), CONVERT(uniqueidentifier, Skirt_Item), CONVERT(uniqueidentifier, Skirt_Asset) FROM dbo.avatarappearance WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.avatarappearance + +EXECUTE sp_rename N'dbo.Tmp_avatarappearance', N'avatarappearance', 'OBJECT' + +ALTER TABLE dbo.avatarappearance ADD CONSTRAINT + PK__avatarap__7DD115CC4E88ABD4 PRIMARY KEY CLUSTERED + ( + Owner + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 10 + +BEGIN TRANSACTION + +CREATE TABLE dbo.Tmp_avatarattachments + ( + UUID uniqueidentifier NOT NULL, + attachpoint int NOT NULL, + item uniqueidentifier NOT NULL, + asset uniqueidentifier NOT NULL + ) ON [PRIMARY] + +IF EXISTS(SELECT * FROM dbo.avatarattachments) + EXEC('INSERT INTO dbo.Tmp_avatarattachments (UUID, attachpoint, item, asset) + SELECT CONVERT(uniqueidentifier, UUID), attachpoint, CONVERT(uniqueidentifier, item), CONVERT(uniqueidentifier, asset) FROM dbo.avatarattachments WITH (HOLDLOCK TABLOCKX)') + +DROP TABLE dbo.avatarattachments + +EXECUTE sp_rename N'dbo.Tmp_avatarattachments', N'avatarattachments', 'OBJECT' + +CREATE NONCLUSTERED INDEX IX_avatarattachments ON dbo.avatarattachments + ( + UUID + ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + +COMMIT + + +:VERSION 11 + +BEGIN TRANSACTION + +ALTER TABLE users ADD scopeID uniqueidentifier not null default '00000000-0000-0000-0000-000000000000' + +COMMIT From df49756e7b8125595db20f63d5f9809a27642b77 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 18 May 2010 21:15:58 +0100 Subject: [PATCH 149/260] Looks like the new files were never added to prebuild.xml --- prebuild.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prebuild.xml b/prebuild.xml index 329ef7b41b..f9608fbf92 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2141,6 +2141,7 @@ + @@ -2247,6 +2248,7 @@ + From f253758c2e4e30c8e09f23135d79765c70198802 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 18 May 2010 21:29:02 +0100 Subject: [PATCH 150/260] Revert "Looks like the new files were never added to prebuild.xml" This reverts commit df49756e7b8125595db20f63d5f9809a27642b77. --- prebuild.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/prebuild.xml b/prebuild.xml index f9608fbf92..329ef7b41b 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2141,7 +2141,6 @@ - @@ -2248,7 +2247,6 @@ - From a27d49b1887700d78498ee0a369c2b3d93d2642c Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 18 May 2010 14:19:19 +0300 Subject: [PATCH 151/260] Added DBGuids.cs (static func DBGuid.FromDB() This DBMS-independent function to be used converting UUIDs from whatever format used in the DB (string/binary/Guid). This is mostly needed for MySQL, as in MSSQL they are always UNIQUEIDENTIFIERs and in SQLite always strings (but would look better if we use it there anyway). --- OpenSim/Data/DBGuids.cs | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 OpenSim/Data/DBGuids.cs diff --git a/OpenSim/Data/DBGuids.cs b/OpenSim/Data/DBGuids.cs new file mode 100644 index 0000000000..1a13e06f06 --- /dev/null +++ b/OpenSim/Data/DBGuids.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Data +{ + + public static class DBGuid + { + /// This function converts a value returned from the database in one of the + /// supported formats into a UUID. This function is not actually DBMS-specific right + /// now + /// + /// + /// + /// + public static UUID FromDB(object id) + { + if( (id == null) || (id == DBNull.Value)) + return UUID.Zero; + + if (id.GetType() == typeof(Guid)) + return new UUID((Guid)id); + + if (id.GetType() == typeof(byte[])) + { + if (((byte[])id).Length == 0) + return UUID.Zero; + else if (((byte[])id).Length == 36) + return new UUID((byte[])id, 0); + } + else if (id.GetType() == typeof(string)) + { + if (((string)id).Length == 0) + return UUID.Zero; + else if (((string)id).Length == 36) + return new UUID((string)id); + } + + throw new Exception("Failed to convert db value to UUID: " + id.ToString()); + } + } +} From 8a0c5d14d45571c3e7529fdacea6f0483af482e5 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 18 May 2010 14:28:12 +0300 Subject: [PATCH 152/260] All (?) MySQL stores fixed to use DBGuid.FromDB() This was needed if we want to update to the latest MySQL connector dll. It automatically converts CHAR(36) to Guids, so getting them as strings no longer works. By using DBGuid.FromDB(), we unlink from any particular storage format of GUIDs, could even make them BINARY(16) if we like. Actually not all MySql units are touched, but the remaining ones don't seem to be affected (they don't read GUIDs from DB) --- OpenSim/Data/MySQL/MySQLEstateData.cs | 24 ++---- .../Data/MySQL/MySQLGenericTableHandler.cs | 11 +-- OpenSim/Data/MySQL/MySQLInventoryData.cs | 33 +++----- OpenSim/Data/MySQL/MySQLLegacyRegionData.cs | 82 +++++++++---------- OpenSim/Data/MySQL/MySQLRegionData.cs | 10 +-- 5 files changed, 68 insertions(+), 92 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLEstateData.cs b/OpenSim/Data/MySQL/MySQLEstateData.cs index 08e21446f2..9158f7a427 100644 --- a/OpenSim/Data/MySQL/MySQLEstateData.cs +++ b/OpenSim/Data/MySQL/MySQLEstateData.cs @@ -34,6 +34,7 @@ using MySql.Data.MySqlClient; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; +using OpenSim.Data; namespace OpenSim.Data.MySQL { @@ -156,20 +157,13 @@ namespace OpenSim.Data.MySQL foreach (string name in FieldList) { - if (m_FieldMap[name].GetValue(es) is bool) + if (m_FieldMap[name].FieldType == typeof(bool)) { - int v = Convert.ToInt32(r[name]); - if (v != 0) - m_FieldMap[name].SetValue(es, true); - else - m_FieldMap[name].SetValue(es, false); + m_FieldMap[name].SetValue(es, Convert.ToInt32(r[name]) != 0); } - else if (m_FieldMap[name].GetValue(es) is UUID) + else if (m_FieldMap[name].FieldType == typeof(UUID)) { - UUID uuid = UUID.Zero; - - UUID.TryParse(r[name].ToString(), out uuid); - m_FieldMap[name].SetValue(es, uuid); + m_FieldMap[name].SetValue(es, DBGuid.FromDB(r[name])); } else { @@ -385,11 +379,7 @@ namespace OpenSim.Data.MySQL while (r.Read()) { // EstateBan eb = new EstateBan(); - - UUID uuid = new UUID(); - UUID.TryParse(r["uuid"].ToString(), out uuid); - - uuids.Add(uuid); + uuids.Add(DBGuid.FromDB(r["uuid"])); } } } @@ -490,7 +480,7 @@ namespace OpenSim.Data.MySQL using (IDataReader reader = cmd.ExecuteReader()) { while(reader.Read()) - result.Add(new UUID(reader["RegionID"].ToString())); + result.Add(DBGuid.FromDB(reader["RegionID"])); reader.Close(); } } diff --git a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs index 1253e0b455..6cbb2ee9a2 100644 --- a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs +++ b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs @@ -148,19 +148,16 @@ namespace OpenSim.Data.MySQL foreach (string name in m_Fields.Keys) { - if (m_Fields[name].GetValue(row) is bool) + if (m_Fields[name].FieldType == typeof(bool)) { int v = Convert.ToInt32(reader[name]); m_Fields[name].SetValue(row, v != 0 ? true : false); } - else if (m_Fields[name].GetValue(row) is UUID) + else if (m_Fields[name].FieldType == typeof(UUID)) { - UUID uuid = UUID.Zero; - - UUID.TryParse(reader[name].ToString(), out uuid); - m_Fields[name].SetValue(row, uuid); + m_Fields[name].SetValue(row, DBGuid.FromDB(reader[name])); } - else if (m_Fields[name].GetValue(row) is int) + else if (m_Fields[name].FieldType == typeof(int)) { int v = Convert.ToInt32(reader[name]); m_Fields[name].SetValue(row, v); diff --git a/OpenSim/Data/MySQL/MySQLInventoryData.cs b/OpenSim/Data/MySQL/MySQLInventoryData.cs index e0e9b9cc99..8fbe7a80ea 100644 --- a/OpenSim/Data/MySQL/MySQLInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLInventoryData.cs @@ -32,6 +32,7 @@ using log4net; using MySql.Data.MySqlClient; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Data; namespace OpenSim.Data.MySQL { @@ -285,31 +286,23 @@ namespace OpenSim.Data.MySQL InventoryItemBase item = new InventoryItemBase(); // TODO: this is to handle a case where NULLs creep in there, which we are not sure is endemic to the system, or legacy. It would be nice to live fix these. - if (reader["creatorID"] == null) - { - item.CreatorId = UUID.Zero.ToString(); - } - else - { - item.CreatorId = (string)reader["creatorID"]; - } + // ( DBGuid.FromDB() reads db NULLs as well, returns UUID.Zero ) + item.CreatorId = DBGuid.FromDB(reader["creatorID"]).ToString(); // Be a bit safer in parsing these because the // database doesn't enforce them to be not null, and // the inventory still works if these are weird in the // db - UUID Owner = UUID.Zero; - UUID GroupID = UUID.Zero; - UUID.TryParse((string)reader["avatarID"], out Owner); - UUID.TryParse((string)reader["groupID"], out GroupID); - item.Owner = Owner; - item.GroupID = GroupID; + + // (Empty is Ok, but "weird" will throw!) + item.Owner = DBGuid.FromDB(reader["avatarID"]); + item.GroupID = DBGuid.FromDB(reader["groupID"]); // Rest of the parsing. If these UUID's fail, we're dead anyway - item.ID = new UUID((string) reader["inventoryID"]); - item.AssetID = new UUID((string) reader["assetID"]); + item.ID = DBGuid.FromDB(reader["inventoryID"]); + item.AssetID = DBGuid.FromDB(reader["assetID"]); item.AssetType = (int) reader["assetType"]; - item.Folder = new UUID((string) reader["parentFolderID"]); + item.Folder = DBGuid.FromDB(reader["parentFolderID"]); item.Name = (string)(reader["inventoryName"] ?? String.Empty); item.Description = (string)(reader["inventoryDescription"] ?? String.Empty); item.NextPermissions = (uint) reader["inventoryNextPermissions"]; @@ -382,9 +375,9 @@ namespace OpenSim.Data.MySQL try { InventoryFolderBase folder = new InventoryFolderBase(); - folder.Owner = new UUID((string) reader["agentID"]); - folder.ParentID = new UUID((string) reader["parentFolderID"]); - folder.ID = new UUID((string) reader["folderID"]); + folder.Owner = DBGuid.FromDB(reader["agentID"]); + folder.ParentID = DBGuid.FromDB(reader["parentFolderID"]); + folder.ID = DBGuid.FromDB(reader["folderID"]); folder.Name = (string) reader["folderName"]; folder.Type = (short) reader["type"]; folder.Version = (ushort) ((int) reader["version"]); diff --git a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs index 07371e7473..bfeae12385 100644 --- a/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLLegacyRegionData.cs @@ -38,6 +38,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSim.Data; namespace OpenSim.Data.MySQL { @@ -269,7 +270,7 @@ namespace OpenSim.Data.MySQL using (IDataReader reader = ExecuteReader(cmd)) { while (reader.Read()) - uuids.Add(new UUID(reader["UUID"].ToString())); + uuids.Add(DBGuid.FromDB(reader["UUID"].ToString())); } // delete the main prims @@ -422,7 +423,7 @@ namespace OpenSim.Data.MySQL else prim.Shape = BuildShape(reader); - UUID parentID = new UUID(reader["SceneGroupID"].ToString()); + UUID parentID = DBGuid.FromDB(reader["SceneGroupID"].ToString()); if (parentID != prim.UUID) prim.ParentUUID = parentID; @@ -500,7 +501,7 @@ namespace OpenSim.Data.MySQL { if (!(itemReader["primID"] is DBNull)) { - UUID primID = new UUID(itemReader["primID"].ToString()); + UUID primID = DBGuid.FromDB(itemReader["primID"].ToString()); if (prims.ContainsKey(primID)) primsWithInventory.Add(prims[primID]); } @@ -738,7 +739,7 @@ namespace OpenSim.Data.MySQL } else { - UUID.TryParse(result["region_id"].ToString(), out nWP.regionID); + nWP.regionID = DBGuid.FromDB(result["region_id"]); nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]); nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]); nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]); @@ -1055,7 +1056,14 @@ namespace OpenSim.Data.MySQL private SceneObjectPart BuildPrim(IDataReader row) { SceneObjectPart prim = new SceneObjectPart(); - prim.UUID = new UUID((string)row["UUID"]); + + // depending on the MySQL connector version, CHAR(36) may be already converted to Guid! + prim.UUID = DBGuid.FromDB(row["UUID"]); + prim.CreatorID = DBGuid.FromDB(row["CreatorID"]); + prim.OwnerID = DBGuid.FromDB(row["OwnerID"]); + prim.GroupID = DBGuid.FromDB(row["GroupID"]); + prim.LastOwnerID = DBGuid.FromDB(row["LastOwnerID"]); + // explicit conversion of integers is required, which sort // of sucks. No idea if there is a shortcut here or not. prim.CreationDate = (int)row["CreationDate"]; @@ -1074,15 +1082,12 @@ namespace OpenSim.Data.MySQL prim.TouchName = (string)row["TouchName"]; // Permissions prim.ObjectFlags = (uint)(int)row["ObjectFlags"]; - prim.CreatorID = new UUID((string)row["CreatorID"]); - prim.OwnerID = new UUID((string)row["OwnerID"]); - prim.GroupID = new UUID((string)row["GroupID"]); - prim.LastOwnerID = new UUID((string)row["LastOwnerID"]); prim.OwnerMask = (uint)(int)row["OwnerMask"]; prim.NextOwnerMask = (uint)(int)row["NextOwnerMask"]; prim.GroupMask = (uint)(int)row["GroupMask"]; prim.EveryoneMask = (uint)(int)row["EveryoneMask"]; prim.BaseMask = (uint)(int)row["BaseMask"]; + // Vectors prim.OffsetPosition = new Vector3( (float)(double)row["PositionX"], @@ -1134,7 +1139,7 @@ namespace OpenSim.Data.MySQL prim.PayPrice[3] = (int)row["PayButton3"]; prim.PayPrice[4] = (int)row["PayButton4"]; - prim.Sound = new UUID(row["LoopedSound"].ToString()); + prim.Sound = DBGuid.FromDB(row["LoopedSound"].ToString()); prim.SoundGain = (float)(double)row["LoopedSoundGain"]; prim.SoundFlags = 1; // If it's persisted at all, it's looped @@ -1161,16 +1166,10 @@ namespace OpenSim.Data.MySQL (float)(double)row["CameraAtOffsetZ"] )); - if ((sbyte)row["ForceMouselook"] != 0) - prim.SetForceMouselook(true); - + prim.SetForceMouselook((sbyte)row["ForceMouselook"] != 0); prim.ScriptAccessPin = (int)row["ScriptAccessPin"]; - - if ((sbyte)row["AllowedDrop"] != 0) - prim.AllowedDrop = true; - - if ((sbyte)row["DieAtEdge"] != 0) - prim.DIE_AT_EDGE = true; + prim.AllowedDrop = ((sbyte)row["AllowedDrop"] != 0); + prim.DIE_AT_EDGE = ((sbyte)row["DieAtEdge"] != 0); prim.SalePrice = (int)row["SalePrice"]; prim.ObjectSaleType = unchecked((byte)(sbyte)row["SaleType"]); @@ -1180,11 +1179,10 @@ namespace OpenSim.Data.MySQL if (!(row["ClickAction"] is DBNull)) prim.ClickAction = unchecked((byte)(sbyte)row["ClickAction"]); - prim.CollisionSound = new UUID(row["CollisionSound"].ToString()); + prim.CollisionSound = DBGuid.FromDB(row["CollisionSound"]); prim.CollisionSoundVolume = (float)(double)row["CollisionSoundVolume"]; - if ((sbyte)row["PassTouches"] != 0) - prim.PassTouches = true; + prim.PassTouches = ((sbyte)row["PassTouches"] != 0); prim.LinkNum = (int)row["LinkNumber"]; return prim; @@ -1200,10 +1198,10 @@ namespace OpenSim.Data.MySQL { TaskInventoryItem taskItem = new TaskInventoryItem(); - taskItem.ItemID = new UUID((String)row["itemID"]); - taskItem.ParentPartID = new UUID((String)row["primID"]); - taskItem.AssetID = new UUID((String)row["assetID"]); - taskItem.ParentID = new UUID((String)row["parentFolderID"]); + taskItem.ItemID = DBGuid.FromDB(row["itemID"]); + taskItem.ParentPartID = DBGuid.FromDB(row["primID"]); + taskItem.AssetID = DBGuid.FromDB(row["assetID"]); + taskItem.ParentID = DBGuid.FromDB(row["parentFolderID"]); taskItem.InvType = Convert.ToInt32(row["invType"]); taskItem.Type = Convert.ToInt32(row["assetType"]); @@ -1211,10 +1209,10 @@ namespace OpenSim.Data.MySQL taskItem.Name = (String)row["name"]; taskItem.Description = (String)row["description"]; taskItem.CreationDate = Convert.ToUInt32(row["creationDate"]); - taskItem.CreatorID = new UUID((String)row["creatorID"]); - taskItem.OwnerID = new UUID((String)row["ownerID"]); - taskItem.LastOwnerID = new UUID((String)row["lastOwnerID"]); - taskItem.GroupID = new UUID((String)row["groupID"]); + taskItem.CreatorID = DBGuid.FromDB(row["creatorID"]); + taskItem.OwnerID = DBGuid.FromDB(row["ownerID"]); + taskItem.LastOwnerID = DBGuid.FromDB(row["lastOwnerID"]); + taskItem.GroupID = DBGuid.FromDB(row["groupID"]); taskItem.NextPermissions = Convert.ToUInt32(row["nextPermissions"]); taskItem.CurrentPermissions = Convert.ToUInt32(row["currentPermissions"]); @@ -1230,7 +1228,7 @@ namespace OpenSim.Data.MySQL { RegionSettings newSettings = new RegionSettings(); - newSettings.RegionUUID = new UUID((string) row["regionUUID"]); + newSettings.RegionUUID = DBGuid.FromDB(row["regionUUID"]); newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]); newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]); newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]); @@ -1244,10 +1242,10 @@ namespace OpenSim.Data.MySQL newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]); newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]); newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]); - newSettings.TerrainTexture1 = new UUID((String) row["terrain_texture_1"]); - newSettings.TerrainTexture2 = new UUID((String) row["terrain_texture_2"]); - newSettings.TerrainTexture3 = new UUID((String) row["terrain_texture_3"]); - newSettings.TerrainTexture4 = new UUID((String) row["terrain_texture_4"]); + newSettings.TerrainTexture1 = DBGuid.FromDB(row["terrain_texture_1"]); + newSettings.TerrainTexture2 = DBGuid.FromDB(row["terrain_texture_2"]); + newSettings.TerrainTexture3 = DBGuid.FromDB(row["terrain_texture_3"]); + newSettings.TerrainTexture4 = DBGuid.FromDB(row["terrain_texture_4"]); newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]); newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]); newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]); @@ -1268,7 +1266,7 @@ namespace OpenSim.Data.MySQL ); newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); - newSettings.Covenant = new UUID((String) row["covenant"]); + newSettings.Covenant = DBGuid.FromDB(row["covenant"]); newSettings.LoadedCreationDateTime = Convert.ToInt32(row["loaded_creation_datetime"]); @@ -1277,7 +1275,7 @@ namespace OpenSim.Data.MySQL else newSettings.LoadedCreationID = (String) row["loaded_creation_id"]; - newSettings.TerrainImageID = new UUID((String)row["map_tile_ID"]); + newSettings.TerrainImageID = DBGuid.FromDB(row["map_tile_ID"]); return newSettings; } @@ -1291,7 +1289,7 @@ namespace OpenSim.Data.MySQL { LandData newData = new LandData(); - newData.GlobalID = new UUID((String) row["UUID"]); + newData.GlobalID = DBGuid.FromDB(row["UUID"]); newData.LocalID = Convert.ToInt32(row["LocalLandID"]); // Bitmap is a byte[512] @@ -1299,7 +1297,7 @@ namespace OpenSim.Data.MySQL newData.Name = (String) row["Name"]; newData.Description = (String) row["Description"]; - newData.OwnerID = new UUID((String)row["OwnerUUID"]); + newData.OwnerID = DBGuid.FromDB(row["OwnerUUID"]); newData.IsGroupOwned = Convert.ToBoolean(row["IsGroupOwned"]); newData.Area = Convert.ToInt32(row["Area"]); newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unimplemented @@ -1307,14 +1305,14 @@ namespace OpenSim.Data.MySQL //Enum libsecondlife.Parcel.ParcelCategory newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]); newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]); - newData.GroupID = new UUID((String) row["GroupUUID"]); + newData.GroupID = DBGuid.FromDB(row["GroupUUID"]); newData.SalePrice = Convert.ToInt32(row["SalePrice"]); newData.Status = (ParcelStatus) Convert.ToInt32(row["LandStatus"]); //Enum. libsecondlife.Parcel.ParcelStatus newData.Flags = Convert.ToUInt32(row["LandFlags"]); newData.LandingType = Convert.ToByte(row["LandingType"]); newData.MediaAutoScale = Convert.ToByte(row["MediaAutoScale"]); - newData.MediaID = new UUID((String) row["MediaTextureUUID"]); + newData.MediaID = DBGuid.FromDB(row["MediaTextureUUID"]); newData.MediaURL = (String) row["MediaURL"]; newData.MusicURL = (String) row["MusicURL"]; newData.PassHours = Convert.ToSingle(row["PassHours"]); @@ -1358,7 +1356,7 @@ namespace OpenSim.Data.MySQL private static ParcelManager.ParcelAccessEntry BuildLandAccessData(IDataReader row) { ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry(); - entry.AgentID = new UUID((string) row["AccessUUID"]); + entry.AgentID = DBGuid.FromDB(row["AccessUUID"]); entry.Flags = (AccessList) Convert.ToInt32(row["Flags"]); entry.Time = new DateTime(); return entry; diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs index aa9a104d78..c7bddacd16 100644 --- a/OpenSim/Data/MySQL/MySQLRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLRegionData.cs @@ -31,6 +31,7 @@ using System.Collections.Generic; using System.Data; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Data; using MySql.Data.MySqlClient; namespace OpenSim.Data.MySQL @@ -143,12 +144,9 @@ namespace OpenSim.Data.MySQL RegionData ret = new RegionData(); ret.Data = new Dictionary(); - UUID regionID; - UUID.TryParse(result["uuid"].ToString(), out regionID); - ret.RegionID = regionID; - UUID scope; - UUID.TryParse(result["ScopeID"].ToString(), out scope); - ret.ScopeID = scope; + ret.RegionID = DBGuid.FromDB(result["uuid"]); + ret.ScopeID = DBGuid.FromDB(result["ScopeID"]); + ret.RegionName = result["regionName"].ToString(); ret.posX = Convert.ToInt32(result["locX"]); ret.posY = Convert.ToInt32(result["locY"]); From deae0301456a169540aafc4ff1a574e1304e327d Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 02:28:19 +0300 Subject: [PATCH 153/260] Some more corrections after MySQL connector update --- OpenSim/Data/MySQL/MySQLAssetData.cs | 3 ++- OpenSim/Data/MySQL/MySQLInventoryData.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index 13f5fa2ac6..ec18c28391 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -33,6 +33,7 @@ using log4net; using MySql.Data.MySqlClient; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Data; namespace OpenSim.Data.MySQL { @@ -320,7 +321,7 @@ namespace OpenSim.Data.MySQL metadata.Type = (sbyte)dbReader["assetType"]; metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); // Not sure if this is correct. metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); - metadata.FullID = new UUID((string)dbReader["id"]); + metadata.FullID = DBGuid.FromDB(dbReader["id"]); // Current SHA1s are not stored/computed. metadata.SHA1 = new byte[] { }; diff --git a/OpenSim/Data/MySQL/MySQLInventoryData.cs b/OpenSim/Data/MySQL/MySQLInventoryData.cs index 8fbe7a80ea..0aea30ffb9 100644 --- a/OpenSim/Data/MySQL/MySQLInventoryData.cs +++ b/OpenSim/Data/MySQL/MySQLInventoryData.cs @@ -287,7 +287,7 @@ namespace OpenSim.Data.MySQL // TODO: this is to handle a case where NULLs creep in there, which we are not sure is endemic to the system, or legacy. It would be nice to live fix these. // ( DBGuid.FromDB() reads db NULLs as well, returns UUID.Zero ) - item.CreatorId = DBGuid.FromDB(reader["creatorID"]).ToString(); + item.CreatorId = reader["creatorID"].ToString(); // Be a bit safer in parsing these because the // database doesn't enforce them to be not null, and From 4fee3f9548307ce3017f4744ce419447eaa31b37 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 18 May 2010 23:15:29 +0100 Subject: [PATCH 154/260] Revert a revert? Am i mad? Don't answer that....! Revert "Revert "Looks like the new files were never added to prebuild.xml"" This reverts commit f253758c2e4e30c8e09f23135d79765c70198802. --- prebuild.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prebuild.xml b/prebuild.xml index 329ef7b41b..f9608fbf92 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2141,6 +2141,7 @@ + @@ -2247,6 +2248,7 @@ + From caf61ab7d814232d3da87e21e409fd9e3916d5c1 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 18 May 2010 23:22:30 +0100 Subject: [PATCH 155/260] Binary Guids are 16 chars long. Fix parser. --- OpenSim/Data/DBGuids.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Data/DBGuids.cs b/OpenSim/Data/DBGuids.cs index 1a13e06f06..fb6832b8d1 100644 --- a/OpenSim/Data/DBGuids.cs +++ b/OpenSim/Data/DBGuids.cs @@ -27,7 +27,7 @@ namespace OpenSim.Data { if (((byte[])id).Length == 0) return UUID.Zero; - else if (((byte[])id).Length == 36) + else if (((byte[])id).Length == 16) return new UUID((byte[])id, 0); } else if (id.GetType() == typeof(string)) From d2bc6736675f0418903f70a649808ccabbfb3fd9 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 18 May 2010 23:33:05 +0100 Subject: [PATCH 156/260] Make m_log in migrations private. Define new m_log in derived class --- OpenSim/Data/Migration.cs | 2 +- OpenSim/Data/MySQL/MySQLMigrations.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 7980c35ba0..e43d7c1feb 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -70,7 +70,7 @@ namespace OpenSim.Data public class Migration { - protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected string _type; protected DbConnection _conn; diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs index b16655d1cb..959b5d0a7b 100644 --- a/OpenSim/Data/MySQL/MySQLMigrations.cs +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -43,6 +43,8 @@ namespace OpenSim.Data.MySQL ///
public class MySqlMigration : Migration { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public MySqlMigration() : base() { From 167db502593de5f535d8c322005c63ef263940ed Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 19 May 2010 02:33:23 +0100 Subject: [PATCH 157/260] Allow migration steps to fail again without bringing down the house --- OpenSim/Data/MySQL/MySQLMigrations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs index 959b5d0a7b..a1f7844845 100644 --- a/OpenSim/Data/MySQL/MySQLMigrations.cs +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -77,7 +77,7 @@ namespace OpenSim.Data.MySQL { m_log.ErrorFormat("[MySQL MIGRATION]: Error {0}", args.Exception.Message); m_log.ErrorFormat("[MySQL MIGRATION]: In SQL: {0}", args.StatementText); - throw args.Exception; + m_log.Error("[MySQL MIGRATION]: The above migration failed to complete. This may cause errors in your install, but could also be normal. Continuing."); }; scr.Execute(); } From dedc0c0bd4ebdf6f8af056c63c7aeeed70b39627 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 19 May 2010 02:47:31 +0100 Subject: [PATCH 158/260] Revert "Allow migration steps to fail again without bringing down the house" This reverts commit 167db502593de5f535d8c322005c63ef263940ed. --- OpenSim/Data/MySQL/MySQLMigrations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs index a1f7844845..959b5d0a7b 100644 --- a/OpenSim/Data/MySQL/MySQLMigrations.cs +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -77,7 +77,7 @@ namespace OpenSim.Data.MySQL { m_log.ErrorFormat("[MySQL MIGRATION]: Error {0}", args.Exception.Message); m_log.ErrorFormat("[MySQL MIGRATION]: In SQL: {0}", args.StatementText); - m_log.Error("[MySQL MIGRATION]: The above migration failed to complete. This may cause errors in your install, but could also be normal. Continuing."); + throw args.Exception; }; scr.Execute(); } From 9fa8013ca5e788862a3afa8fa7a354e492d51c7e Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 19 May 2010 03:07:31 +0100 Subject: [PATCH 159/260] Remove the return that was inserted in the last merge to allow migrations to continue in the face of an error --- OpenSim/Data/Migration.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index e43d7c1feb..ddf8078d0a 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -202,7 +202,6 @@ namespace OpenSim.Data m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", kvp.Value.ToString()); m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Migration aborted.", e.Message); ExecuteScript("ROLLBACK;"); - return; } if (version == 0) From 20642f2f212d46a97bd48575dc059da327450520 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 19 May 2010 03:26:37 +0100 Subject: [PATCH 160/260] Fix the migration message to say "Continuing" again. Remove line feed, which prevented the full message from displaying. --- OpenSim/Data/Migration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index ddf8078d0a..987eab58bc 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -200,7 +200,7 @@ namespace OpenSim.Data catch (Exception e) { m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", kvp.Value.ToString()); - m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}.\n This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Migration aborted.", e.Message); + m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}. This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); ExecuteScript("ROLLBACK;"); } From 0c209a469b54a52ac7d54a7bddd2bdcf02a80522 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 19 May 2010 03:48:03 +0100 Subject: [PATCH 161/260] Clean up output a bit --- OpenSim/Data/Migration.cs | 13 ++++++++++--- OpenSim/Data/MySQL/MySQLMigrations.cs | 6 +----- OpenSim/Framework/Console/OpenSimAppender.cs | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 987eab58bc..e54296deea 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -146,7 +146,14 @@ namespace OpenSim.Data foreach (string sql in script) { cmd.CommandText = sql; - cmd.ExecuteNonQuery(); + try + { + cmd.ExecuteNonQuery(); + } + catch + { + throw new Exception(sql); + } } } } @@ -199,8 +206,8 @@ namespace OpenSim.Data } catch (Exception e) { - m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", kvp.Value.ToString()); - m_log.DebugFormat("[MIGRATIONS]: An error has occurred in the migration {0}. This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing.", e.Message); + m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", e.Message); + m_log.Debug("[MIGRATIONS]: An error has occurred in the migration. This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing."); ExecuteScript("ROLLBACK;"); } diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs index 959b5d0a7b..81a0e837ea 100644 --- a/OpenSim/Data/MySQL/MySQLMigrations.cs +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -43,8 +43,6 @@ namespace OpenSim.Data.MySQL ///
public class MySqlMigration : Migration { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public MySqlMigration() : base() { @@ -75,9 +73,7 @@ namespace OpenSim.Data.MySQL scr.Query = sql; scr.Error += delegate(object sender, MySqlScriptErrorEventArgs args) { - m_log.ErrorFormat("[MySQL MIGRATION]: Error {0}", args.Exception.Message); - m_log.ErrorFormat("[MySQL MIGRATION]: In SQL: {0}", args.StatementText); - throw args.Exception; + throw new Exception(sql); }; scr.Execute(); } diff --git a/OpenSim/Framework/Console/OpenSimAppender.cs b/OpenSim/Framework/Console/OpenSimAppender.cs index cd95506d29..282ad384b9 100644 --- a/OpenSim/Framework/Console/OpenSimAppender.cs +++ b/OpenSim/Framework/Console/OpenSimAppender.cs @@ -66,7 +66,7 @@ namespace OpenSim.Framework.Console } else { - System.Console.Write(loggingMessage); + System.Console.WriteLine(loggingMessage); } } catch (Exception e) From e4b8d76b108725fa83dd1e1f3eb909b39482e769 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 19 May 2010 04:17:56 +0100 Subject: [PATCH 162/260] Change appender to deal with line feeds more intelligently. Change migration error reporting to not truncate the statement when reporting. It's a bit messier than the old error reporting, but at least one gets an idea of what could be wrong again. And things look a lot neater now. --- OpenSim/Data/Migration.cs | 2 +- OpenSim/Framework/Console/OpenSimAppender.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index e54296deea..6d4807ff40 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -206,7 +206,7 @@ namespace OpenSim.Data } catch (Exception e) { - m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", e.Message); + m_log.DebugFormat("[MIGRATIONS] Cmd was {0}", e.Message.Replace("\n", " ")); m_log.Debug("[MIGRATIONS]: An error has occurred in the migration. This may mean you could see errors trying to run OpenSim. If you see database related errors, you will need to fix the issue manually. Continuing."); ExecuteScript("ROLLBACK;"); } diff --git a/OpenSim/Framework/Console/OpenSimAppender.cs b/OpenSim/Framework/Console/OpenSimAppender.cs index 282ad384b9..72a251e9b1 100644 --- a/OpenSim/Framework/Console/OpenSimAppender.cs +++ b/OpenSim/Framework/Console/OpenSimAppender.cs @@ -66,7 +66,10 @@ namespace OpenSim.Framework.Console } else { - System.Console.WriteLine(loggingMessage); + if (!loggingMessage.EndsWith("\n")) + System.Console.WriteLine(loggingMessage); + else + System.Console.Write(loggingMessage); } } catch (Exception e) From 527a257b9fac15273ae232f66533f47b2112ec2e Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 17:00:02 +0300 Subject: [PATCH 163/260] Kind of fixed Melanie's "Exception(sql)" correction Throwing an Ex. with SQL command in the message looks weird, this is a bit better, but I'm still not sure if that's the proper way to handle. Also, there is a catch one level up, so is this one necessary? --- OpenSim/Data/Migration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs index 6d4807ff40..4f113a2f49 100644 --- a/OpenSim/Data/Migration.cs +++ b/OpenSim/Data/Migration.cs @@ -150,9 +150,9 @@ namespace OpenSim.Data { cmd.ExecuteNonQuery(); } - catch + catch(Exception e) { - throw new Exception(sql); + throw new Exception(e.Message + " in SQL: " + sql); } } } From d71c483bf0669967d9b58aa3b70247ae800e566f Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 10:28:37 +0300 Subject: [PATCH 164/260] Prebuild: added *.migrations as resources to MSSQL, SQLiteLegacy (just in case) --- prebuild.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prebuild.xml b/prebuild.xml index f9608fbf92..870826cbaf 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2175,6 +2175,7 @@ + @@ -2212,6 +2213,7 @@ + From ccee95552f07aeab003acf8dc8f5422209e41e7d Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 29 Apr 2010 12:42:52 +0300 Subject: [PATCH 165/260] A bit of harmless refactoring in SQLiteAssetData.cs --- OpenSim/Data/SQLite/SQLiteAssetData.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 7081f99054..0fc1f2503b 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -194,16 +194,9 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); using (IDataReader reader = cmd.ExecuteReader()) { - if (reader.Read()) - { - reader.Close(); - return true; - } - else - { - reader.Close(); - return false; - } + bool ok = reader.Read(); + reader.Close(); + return ok; } } } From c6977cbd4d00159c034f66bfae40ee4984dea3f2 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 29 Apr 2010 12:44:03 +0300 Subject: [PATCH 166/260] Added CreatorID to SQLite asset data --- OpenSim/Data/SQLite/SQLiteAssetData.cs | 42 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 0fc1f2503b..40e51db42d 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -44,10 +44,10 @@ namespace OpenSim.Data.SQLite // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private const string SelectAssetSQL = "select * from assets where UUID=:UUID"; - private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, asset_flags, UUID from assets limit :start, :count"; + private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, asset_flags, UUID, CreatorID from assets limit :start, :count"; private const string DeleteAssetSQL = "delete from assets where UUID=:UUID"; - private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, asset_flags, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Flags, :Data)"; - private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, asset_flags=:Flags, Data=:Data where UUID=:UUID"; + private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, asset_flags, CreatorID, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Flags, :CreatorID, :Data)"; + private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, asset_flags=:Flags, CreatorID=:CreatorID, Data=:Data where UUID=:UUID"; private const string assetSelect = "select * from assets"; private SqliteConnection m_conn; @@ -137,6 +137,7 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags)); + cmd.Parameters.Add(new SqliteParameter(":CreatorID", asset.Metadata.CreatorID)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); cmd.ExecuteNonQuery(); @@ -156,6 +157,7 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags)); + cmd.Parameters.Add(new SqliteParameter(":CreatorID", asset.Metadata.CreatorID)); cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); cmd.ExecuteNonQuery(); @@ -216,7 +218,7 @@ namespace OpenSim.Data.SQLite new UUID((String)row["UUID"]), (String)row["Name"], Convert.ToSByte(row["Type"]), - UUID.Zero.ToString() + (String)row["CreatorID"] ); asset.Description = (String) row["Description"]; @@ -237,6 +239,7 @@ namespace OpenSim.Data.SQLite metadata.Type = Convert.ToSByte(row["Type"]); metadata.Temporary = Convert.ToBoolean(row["Temporary"]); // Not sure if this is correct. metadata.Flags = (AssetFlags)Convert.ToInt32(row["asset_flags"]); + metadata.CreatorID = row["CreatorID"].ToString(); // Current SHA1s are not stored/computed. metadata.SHA1 = new byte[] {}; @@ -321,6 +324,25 @@ namespace OpenSim.Data.SQLite get { return "SQLite Asset storage engine"; } } + // TODO: (AlexRa): one of these is to be removed eventually (?) + + /// + /// Delete an asset from database + /// + /// + public bool DeleteAsset(UUID uuid) + { + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); + cmd.ExecuteNonQuery(); + } + } + return true; + } + public override bool Delete(string id) { UUID assetID; @@ -328,17 +350,7 @@ namespace OpenSim.Data.SQLite if (!UUID.TryParse(id, out assetID)) return false; - lock (this) - { - using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn)) - { - cmd.Parameters.Add(new SqliteParameter(":UUID", assetID.ToString())); - - cmd.ExecuteNonQuery(); - } - } - - return true; + return DeleteAsset(assetID); } #endregion From accf8c420de3b77f80e35c848291b2bb358f6a14 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 10:07:48 +0300 Subject: [PATCH 167/260] SQLite: CreatorID added (and asset_flags moved) to the migration script --- .../SQLite/Resources/AssetStore.migrations | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/OpenSim/Data/SQLite/Resources/AssetStore.migrations b/OpenSim/Data/SQLite/Resources/AssetStore.migrations index fb72b91af9..bc11e13d38 100644 --- a/OpenSim/Data/SQLite/Resources/AssetStore.migrations +++ b/OpenSim/Data/SQLite/Resources/AssetStore.migrations @@ -40,3 +40,27 @@ update assets COMMIT; +:VERSION 5 + +BEGIN TRANSACTION; + +CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; +DROP TABLE assets; +CREATE TABLE assets( + UUID NOT NULL PRIMARY KEY, + Name, + Description, + Type, + Local, + Temporary, + asset_flags INTEGER NOT NULL DEFAULT 0, + CreatorID varchar(36) default '', + Data); + +INSERT INTO assets(UUID,Name,Description,Type,Local,Temporary,Data) +SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; +DROP TABLE assets_backup; + +COMMIT; + From eacd8d0263fa41c4d45c880b02f5be709eaab32f Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 10:16:56 +0300 Subject: [PATCH 168/260] MySQL: added CreatorID, moved asset_flag to migration script --- OpenSim/Data/MySQL/Resources/AssetStore.migrations | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/OpenSim/Data/MySQL/Resources/AssetStore.migrations b/OpenSim/Data/MySQL/Resources/AssetStore.migrations index b9595f0f47..3fd58b772c 100644 --- a/OpenSim/Data/MySQL/Resources/AssetStore.migrations +++ b/OpenSim/Data/MySQL/Resources/AssetStore.migrations @@ -67,3 +67,11 @@ COMMIT; DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621' +:VERSION 7 + +ALTER TABLE assets ADD COLUMN asset_flags INTEGER NOT NULL DEFAULT 0; + +:VERSION 8 + +alter table assets add CreatorID varchar(36) not null default '' + From 64fe823b9255f0314277f80926435d5525039a2d Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 21:30:16 +0300 Subject: [PATCH 169/260] MySQLAssetData.cs now supports asset_flags, CreatorID --- OpenSim/Data/MySQL/MySQLAssetData.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index ec18c28391..fe5152ad85 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -112,7 +112,7 @@ namespace OpenSim.Data.MySQL dbcon.Open(); using (MySqlCommand cmd = new MySqlCommand( - "SELECT name, description, assetType, local, temporary, asset_flags, data FROM assets WHERE id=?id", + "SELECT name, description, assetType, local, temporary, asset_flags, CreatorID, data FROM assets WHERE id=?id", dbcon)) { cmd.Parameters.AddWithValue("?id", assetID.ToString()); @@ -123,7 +123,7 @@ namespace OpenSim.Data.MySQL { if (dbReader.Read()) { - asset = new AssetBase(assetID, (string)dbReader["name"], (sbyte)dbReader["assetType"], UUID.Zero.ToString()); + asset = new AssetBase(assetID, (string)dbReader["name"], (sbyte)dbReader["assetType"], dbReader["CreatorID"].ToString()); asset.Data = (byte[])dbReader["data"]; asset.Description = (string)dbReader["description"]; @@ -163,8 +163,8 @@ namespace OpenSim.Data.MySQL MySqlCommand cmd = new MySqlCommand( - "replace INTO assets(id, name, description, assetType, local, temporary, create_time, access_time, asset_flags, data)" + - "VALUES(?id, ?name, ?description, ?assetType, ?local, ?temporary, ?create_time, ?access_time, ?asset_flags, ?data)", + "replace INTO assets(id, name, description, assetType, local, temporary, create_time, access_time, asset_flags, CreatorID, data)" + + "VALUES(?id, ?name, ?description, ?assetType, ?local, ?temporary, ?create_time, ?access_time, ?asset_flags, ?CreatorID, ?data)", dbcon); string assetName = asset.Name; @@ -196,6 +196,7 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("?temporary", asset.Temporary); cmd.Parameters.AddWithValue("?create_time", now); cmd.Parameters.AddWithValue("?access_time", now); + cmd.Parameters.AddWithValue("?CreatorID", asset.Metadata.CreatorID); cmd.Parameters.AddWithValue("?asset_flags", (int)asset.Flags); cmd.Parameters.AddWithValue("?data", asset.Data); cmd.ExecuteNonQuery(); @@ -305,7 +306,7 @@ namespace OpenSim.Data.MySQL using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { dbcon.Open(); - MySqlCommand cmd = new MySqlCommand("SELECT name,description,assetType,temporary,id,asset_flags FROM assets LIMIT ?start, ?count", dbcon); + MySqlCommand cmd = new MySqlCommand("SELECT name,description,assetType,temporary,id,asset_flags,CreatorID FROM assets LIMIT ?start, ?count", dbcon); cmd.Parameters.AddWithValue("?start", start); cmd.Parameters.AddWithValue("?count", count); @@ -322,6 +323,7 @@ namespace OpenSim.Data.MySQL metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); // Not sure if this is correct. metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); metadata.FullID = DBGuid.FromDB(dbReader["id"]); + metadata.CreatorID = dbReader["CreatorID"].ToString(); // Current SHA1s are not stored/computed. metadata.SHA1 = new byte[] { }; From d6a6668bd7230099b18e1c45556160fc36bcce8d Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 27 Apr 2010 09:53:01 +0300 Subject: [PATCH 170/260] Scrambled asset type in BasicAssetTest.cs! The asset type wasn't in the list of "DontScramble" fields, so the test assets were stored with randomized type, which caused exception on reading them. Also the scrambler was moved from local var to the class level, so it could be used in the new tests I've added (see the next commit). --- OpenSim/Data/Tests/BasicAssetTest.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/OpenSim/Data/Tests/BasicAssetTest.cs b/OpenSim/Data/Tests/BasicAssetTest.cs index e80cff96b8..cd6903b08f 100644 --- a/OpenSim/Data/Tests/BasicAssetTest.cs +++ b/OpenSim/Data/Tests/BasicAssetTest.cs @@ -43,6 +43,7 @@ namespace OpenSim.Data.Tests public UUID uuid2; public UUID uuid3; public byte[] asset1; + PropertyScrambler scrambler; public void SuperInit() { @@ -53,6 +54,15 @@ namespace OpenSim.Data.Tests uuid3 = UUID.Random(); asset1 = new byte[100]; asset1.Initialize(); + + scrambler = new PropertyScrambler() + .DontScramble(x => x.ID) + .DontScramble(x => x.FullID) + .DontScramble(x => x.Metadata.ID) + .DontScramble(x => x.Metadata.Type) + .DontScramble(x => x.Metadata.CreatorID) + .DontScramble(x => x.Metadata.ContentType) + .DontScramble(x => x.Metadata.FullID); } [Test] @@ -73,15 +83,6 @@ namespace OpenSim.Data.Tests a2.Data = asset1; a3.Data = asset1; - PropertyScrambler scrambler = new PropertyScrambler() - .DontScramble(x => x.Data) - .DontScramble(x => x.ID) - .DontScramble(x => x.FullID) - .DontScramble(x => x.Metadata.ID) - .DontScramble(x => x.Metadata.CreatorID) - .DontScramble(x => x.Metadata.ContentType) - .DontScramble(x => x.Metadata.FullID); - scrambler.Scramble(a1); scrambler.Scramble(a2); scrambler.Scramble(a3); From ce787a4c413cbfc70f4e31e49510e19127c9b01b Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 16:55:31 +0300 Subject: [PATCH 171/260] Series of patches to include creator ID in assets. Contains a migration. SQLite: May contain nuts. The SQLite migration copies the entire asset table. Be prepared for quite a wait. Don't interrupt it. Back up your assets db. BasicAssetTest checks CreatorID storage, new test for weird CreatorID (now also checks that non-GUID or empty CreatorID gets stored correctly) Signed-off-by: Melanie --- OpenSim/Data/Tests/BasicAssetTest.cs | 39 +++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/OpenSim/Data/Tests/BasicAssetTest.cs b/OpenSim/Data/Tests/BasicAssetTest.cs index cd6903b08f..71d6314690 100644 --- a/OpenSim/Data/Tests/BasicAssetTest.cs +++ b/OpenSim/Data/Tests/BasicAssetTest.cs @@ -42,6 +42,9 @@ namespace OpenSim.Data.Tests public UUID uuid1; public UUID uuid2; public UUID uuid3; + public string critter1 = UUID.Random().ToString(); + public string critter2 = UUID.Random().ToString(); + public string critter3 = UUID.Random().ToString(); public byte[] asset1; PropertyScrambler scrambler; @@ -54,6 +57,7 @@ namespace OpenSim.Data.Tests uuid3 = UUID.Random(); asset1 = new byte[100]; asset1.Initialize(); + scrambler = new PropertyScrambler() .DontScramble(x => x.ID) @@ -76,9 +80,9 @@ namespace OpenSim.Data.Tests [Test] public void T010_StoreSimpleAsset() { - AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); - AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, UUID.Zero.ToString()); - AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1); + AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, critter2); + AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, critter3); a1.Data = asset1; a2.Data = asset1; a3.Data = asset1; @@ -87,7 +91,6 @@ namespace OpenSim.Data.Tests scrambler.Scramble(a2); scrambler.Scramble(a3); - db.StoreAsset(a1); db.StoreAsset(a2); db.StoreAsset(a3); @@ -131,5 +134,33 @@ namespace OpenSim.Data.Tests Assert.That(metadata.Temporary, Is.EqualTo(a1b.Temporary)); Assert.That(metadata.FullID, Is.EqualTo(a1b.FullID)); } + + + [Test] + public void T020_CheckForWeirdCreatorID() + { + // It is expected that eventually the CreatorID might be an arbitrary string (an URI) + // rather than a valid UUID (?). This test is to make sure that the database layer does not + // attempt to convert CreatorID to GUID, but just passes it both ways as a string. + AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1); + AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, "This is not a GUID!"); + AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, ""); + a1.Data = asset1; + a2.Data = asset1; + a3.Data = asset1; + + db.StoreAsset(a1); + db.StoreAsset(a2); + db.StoreAsset(a3); + + AssetBase a1a = db.GetAsset(uuid1); + Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); + + AssetBase a2a = db.GetAsset(uuid2); + Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); + + AssetBase a3a = db.GetAsset(uuid3); + Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); + } } } From 859beaf830ecf554b82d150e6f805e096e927df7 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 20 May 2010 11:03:38 +0100 Subject: [PATCH 172/260] Revert one of the previous patches' hunks. The new code looked better, but was less efficient. --- OpenSim/Data/SQLite/SQLiteAssetData.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs index 40e51db42d..16e560c6df 100644 --- a/OpenSim/Data/SQLite/SQLiteAssetData.cs +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -196,9 +196,16 @@ namespace OpenSim.Data.SQLite cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); using (IDataReader reader = cmd.ExecuteReader()) { - bool ok = reader.Read(); - reader.Close(); - return ok; + if (reader.Read()) + { + reader.Close(); + return true; + } + else + { + reader.Close(); + return false; + } } } } From 59dec2f989474158c94a2383b150c25d132777aa Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 May 2010 11:51:57 -0700 Subject: [PATCH 173/260] * Added sessionID to IGridUserService.SetLastPosition(), as some connectors will want to track position against sessionID instead of userID * Updated SimianPresenceServiceConnector to use the new LoggedOut/SetHome/etc methods and only update session position on parcel crossing --- .../GridUser/ActivityDetector.cs | 4 +- .../GridUser/LocalGridUserServiceConnector.cs | 4 +- .../RemoteGridUserServiceConnector.cs | 4 +- .../GridUser/GridUserServerPostHandler.cs | 3 +- .../GridUser/GridUserServiceConnector.cs | 2 +- .../SimianGrid/SimianActivityDetector.cs | 113 +++++++++++ .../SimianPresenceServiceConnector.cs | 187 ++++++------------ .../Services/Interfaces/IGridUserService.cs | 2 +- .../UserAccountService/GridUserService.cs | 2 +- 9 files changed, 189 insertions(+), 132 deletions(-) create mode 100644 OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs index 6c01927c5e..7d2dc5a9cc 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs @@ -72,7 +72,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser { m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); - m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); } public void OnNewClient(IClientAPI client) @@ -109,7 +109,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser { // TODO: grab the parcel ID from ILandModule // and send that along - m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs index d914a576e1..76e030fea0 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs @@ -162,9 +162,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser return m_GridUserService.SetHome(userID, homeID, homePosition, homeLookAt); } - public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { - return m_GridUserService.SetLastPosition(userID, regionID, lastPosition, lastLookAt); + return m_GridUserService.SetLastPosition(userID, sessionID, regionID, lastPosition, lastLookAt); } public GridUserInfo GetGridUserInfo(string userID) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs index e3e2e619c1..fb11e9a454 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs @@ -137,9 +137,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser return m_RemoteConnector.SetHome(userID, regionID, position, lookAt); } - public bool SetLastPosition(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) { - return m_RemoteConnector.SetLastPosition(userID, regionID, position, lookAt); + return m_RemoteConnector.SetLastPosition(userID, sessionID, regionID, position, lookAt); } public GridUserInfo GetGridUserInfo(string userID) diff --git a/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs index f8fa42967f..b1e7eac41c 100644 --- a/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs +++ b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs @@ -156,6 +156,7 @@ namespace OpenSim.Server.Handlers.GridUser byte[] SetPosition(Dictionary request) { string user = string.Empty; + UUID sessionID = UUID.Zero; UUID region = UUID.Zero; Vector3 position = new Vector3(128, 128, 70); Vector3 look = Vector3.Zero; @@ -166,7 +167,7 @@ namespace OpenSim.Server.Handlers.GridUser if (!UnpackArgs(request, out user, out region, out position, out look)) return FailureResult(); - if (m_GridUserService.SetLastPosition(user, region, position, look)) + if (m_GridUserService.SetLastPosition(user, sessionID, region, position, look)) return SuccessResult(); return FailureResult(); diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs index b4500a5f55..f222e31cfa 100644 --- a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs +++ b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs @@ -123,7 +123,7 @@ namespace OpenSim.Services.Connectors return Set(sendData, userID, regionID, position, lookAt); } - public bool SetLastPosition(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) { Dictionary sendData = new Dictionary(); //sendData["SCOPEID"] = scopeID.ToString(); diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs new file mode 100644 index 0000000000..8cc5671538 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs @@ -0,0 +1,113 @@ +/* + * 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 OpenSimulator 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 OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using log4net; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + public class SimianActivityDetector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGridUserService m_GridUserService; + private Scene m_aScene; + + public SimianActivityDetector(IGridUserService guservice) + { + m_GridUserService = guservice; + m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Started"); + } + + public void AddRegion(Scene scene) + { + // For now the only events we listen to are these + // But we could trigger the position update more often + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnAvatarEnteringNewParcel += OnEnteringNewParcel; + + if (m_aScene == null) + m_aScene = scene; + } + + public void RemoveRegion(Scene scene) + { + scene.EventManager.OnMakeRootAgent -= OnMakeRootAgent; + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnAvatarEnteringNewParcel -= OnEnteringNewParcel; + } + + public void OnMakeRootAgent(ScenePresence sp) + { + m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + + public void OnNewClient(IClientAPI client) + { + client.OnConnectionClosed += OnConnectionClose; + } + + public void OnConnectionClose(IClientAPI client) + { + if (client.IsLoggingOut) + { + object sp = null; + Vector3 position = new Vector3(128, 128, 0); + Vector3 lookat = new Vector3(0, 1, 0); + + if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) + { + if (sp is ScenePresence) + { + if (((ScenePresence)sp).IsChildAgent) + return; + + position = ((ScenePresence)sp).AbsolutePosition; + lookat = ((ScenePresence)sp).Lookat; + } + } + + m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); + m_GridUserService.LoggedOut(client.AgentId.ToString(), client.Scene.RegionInfo.RegionID, position, lookat); + } + + } + + void OnEnteringNewParcel(ScenePresence sp, int localLandID, UUID regionID) + { + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs index b86c45c033..ec9cf6704f 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs @@ -58,6 +58,7 @@ namespace OpenSim.Services.Connectors.SimianGrid MethodBase.GetCurrentMethod().DeclaringType); private string m_serverUrl = String.Empty; + private SimianActivityDetector m_activityDetector; #region ISharedRegionModule @@ -66,7 +67,7 @@ namespace OpenSim.Services.Connectors.SimianGrid public void PostInitialise() { } public void Close() { } - public SimianPresenceServiceConnector() { } + public SimianPresenceServiceConnector() { m_activityDetector = new SimianActivityDetector(this); } public string Name { get { return "SimianPresenceServiceConnector"; } } public void AddRegion(Scene scene) { @@ -75,9 +76,7 @@ namespace OpenSim.Services.Connectors.SimianGrid scene.RegisterModuleInterface(this); scene.RegisterModuleInterface(this); - scene.EventManager.OnMakeRootAgent += MakeRootAgentHandler; - scene.EventManager.OnNewClient += NewClientHandler; - scene.EventManager.OnSignificantClientMovement += SignificantClientMovementHandler; + m_activityDetector.AddRegion(scene); LogoutRegionAgents(scene.RegionInfo.RegionID); } @@ -89,9 +88,7 @@ namespace OpenSim.Services.Connectors.SimianGrid scene.UnregisterModuleInterface(this); scene.UnregisterModuleInterface(this); - scene.EventManager.OnMakeRootAgent -= MakeRootAgentHandler; - scene.EventManager.OnNewClient -= NewClientHandler; - scene.EventManager.OnSignificantClientMovement -= SignificantClientMovementHandler; + m_activityDetector.RemoveRegion(scene); LogoutRegionAgents(scene.RegionInfo.RegionID); } @@ -193,29 +190,8 @@ namespace OpenSim.Services.Connectors.SimianGrid public bool ReportAgent(UUID sessionID, UUID regionID) { - return ReportAgent(sessionID, regionID, Vector3.Zero, Vector3.Zero); - } - - protected bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) - { - //m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Updating session data for agent with sessionID " + sessionID); - - NameValueCollection requestArgs = new NameValueCollection - { - { "RequestMethod", "UpdateSession" }, - { "SessionID", sessionID.ToString() }, - { "SceneID", regionID.ToString() }, - { "ScenePosition", position.ToString() }, - { "SceneLookAt", lookAt.ToString() } - }; - - OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); - bool success = response["Success"].AsBoolean(); - - if (!success) - m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString()); - - return success; + // Not needed for SimianGrid + return true; } public PresenceInfo GetAgent(UUID sessionID) @@ -274,14 +250,27 @@ namespace OpenSim.Services.Connectors.SimianGrid public GridUserInfo LoggedIn(string userID) { - // never implemented at the sim + // Never implemented at the sim return null; } public bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { - // Not needed for simian grid, event handler is doing it - return true; + // Save our last position as user data + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "LastLocation", SerializeLocation(regionID, lastPosition, lastLookAt) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString()); + + return success; } public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) @@ -304,10 +293,9 @@ namespace OpenSim.Services.Connectors.SimianGrid return success; } - public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { - // Not needed for simian grid, presence detection is doing it - return true; + return UpdateSession(sessionID, regionID, lastPosition, lastLookAt); } public GridUserInfo GetGridUserInfo(string user) @@ -334,54 +322,6 @@ namespace OpenSim.Services.Connectors.SimianGrid #endregion - #region Presence Detection - - private void MakeRootAgentHandler(ScenePresence sp) - { - m_log.DebugFormat("[PRESENCE DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); - - ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); - SetLastLocation(sp.UUID, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); - } - - private void NewClientHandler(IClientAPI client) - { - client.OnConnectionClosed += LogoutHandler; - } - - private void SignificantClientMovementHandler(IClientAPI client) - { - ScenePresence sp; - if (client.Scene is Scene && ((Scene)client.Scene).TryGetScenePresence(client.AgentId, out sp)) - ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); - } - - private void LogoutHandler(IClientAPI client) - { - if (client.IsLoggingOut) - { - client.OnConnectionClosed -= LogoutHandler; - - object obj; - if (client.Scene.TryGetScenePresence(client.AgentId, out obj) && obj is ScenePresence) - { - // The avatar is still in the scene, we can get the exact logout position - ScenePresence sp = (ScenePresence)obj; - SetLastLocation(client.AgentId, client.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); - } - else - { - // The avatar was already removed from the scene, store LastLocation using the most recent session data - m_log.Warn("[PRESENCE]: " + client.Name + " has already been removed from the scene, storing approximate LastLocation"); - SetLastLocation(client.SessionId); - } - - LogoutAgent(client.SessionId); - } - } - - #endregion Presence Detection - #region Helpers private OSDMap GetUserData(UUID userID) @@ -453,57 +393,60 @@ namespace OpenSim.Services.Connectors.SimianGrid return presences; } - /// - /// Fetch the last known avatar location with GetSession and persist it - /// as user data with AddUserData - /// - private bool SetLastLocation(UUID sessionID) + private bool UpdateSession(UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { + // Save our current location as session data NameValueCollection requestArgs = new NameValueCollection { - { "RequestMethod", "GetSession" }, - { "SessionID", sessionID.ToString() } + { "RequestMethod", "UpdateSession" }, + { "SessionID", sessionID.ToString() }, + { "SceneID", regionID.ToString() }, + { "ScenePosition", lastPosition.ToString() }, + { "SceneLookAt", lastLookAt.ToString() } }; OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); bool success = response["Success"].AsBoolean(); - if (success) - { - UUID userID = response["UserID"].AsUUID(); - UUID sceneID = response["SceneID"].AsUUID(); - Vector3 position = response["ScenePosition"].AsVector3(); - Vector3 lookAt = response["SceneLookAt"].AsVector3(); - - return SetLastLocation(userID, sceneID, position, lookAt); - } - else - { - m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve presence information for session " + sessionID + - " while saving last location: " + response["Message"].AsString()); - } - - return success; - } - - private bool SetLastLocation(UUID userID, UUID sceneID, Vector3 position, Vector3 lookAt) - { - NameValueCollection requestArgs = new NameValueCollection - { - { "RequestMethod", "AddUserData" }, - { "UserID", userID.ToString() }, - { "LastLocation", SerializeLocation(sceneID, position, lookAt) } - }; - - OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); - bool success = response["Success"].AsBoolean(); - if (!success) - m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString()); + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString()); return success; } + ///// + ///// Fetch the last known avatar location with GetSession and persist it + ///// as user data with AddUserData + ///// + //private bool SetLastLocation(UUID sessionID) + //{ + // NameValueCollection requestArgs = new NameValueCollection + // { + // { "RequestMethod", "GetSession" }, + // { "SessionID", sessionID.ToString() } + // }; + + // OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + // bool success = response["Success"].AsBoolean(); + + // if (success) + // { + // UUID userID = response["UserID"].AsUUID(); + // UUID sceneID = response["SceneID"].AsUUID(); + // Vector3 position = response["ScenePosition"].AsVector3(); + // Vector3 lookAt = response["SceneLookAt"].AsVector3(); + + // return SetLastLocation(userID, sceneID, position, lookAt); + // } + // else + // { + // m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve presence information for session " + sessionID + + // " while saving last location: " + response["Message"].AsString()); + // } + + // return success; + //} + private PresenceInfo ResponseToPresenceInfo(OSDMap sessionResponse, OSDMap userResponse) { if (sessionResponse == null) diff --git a/OpenSim/Services/Interfaces/IGridUserService.cs b/OpenSim/Services/Interfaces/IGridUserService.cs index e629dffda4..3d11b3e16f 100644 --- a/OpenSim/Services/Interfaces/IGridUserService.cs +++ b/OpenSim/Services/Interfaces/IGridUserService.cs @@ -108,7 +108,7 @@ namespace OpenSim.Services.Interfaces bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt); - bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); + bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); GridUserInfo GetGridUserInfo(string userID); } diff --git a/OpenSim/Services/UserAccountService/GridUserService.cs b/OpenSim/Services/UserAccountService/GridUserService.cs index 697ba639d2..00a1ae2e60 100644 --- a/OpenSim/Services/UserAccountService/GridUserService.cs +++ b/OpenSim/Services/UserAccountService/GridUserService.cs @@ -139,7 +139,7 @@ namespace OpenSim.Services.UserAccountService return m_Database.Store(d); } - public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { //m_log.DebugFormat("[Grid User Service]: SetLastPosition for {0}", userID); GridUserData d = m_Database.Get(userID); From 56f3cb6da0ff48a9af0e11c5b1769788cc0a1a22 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 20 May 2010 12:04:12 -0700 Subject: [PATCH 174/260] * Don't send texture data for prims in ImprovedTerseObjectUpdate packets unless we were asked to --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 11dca8db89..07c3a6a91b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4363,7 +4363,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP acceleration = part.Acceleration; angularVelocity = part.AngularVelocity; rotation = part.RotationOffset; - textureEntry = part.Shape.TextureEntry; + + if (sendTexture) + textureEntry = part.Shape.TextureEntry; + else + textureEntry = null; } #endregion ScenePresence/SOP Handling From 13c22015e588284df1b74b7f5edad0df97ca3d3c Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 20 May 2010 15:14:14 -0700 Subject: [PATCH 175/260] Removed sessionID from GridUserservice again. Removed parcel crossing detection from Robust connector. Fixed Simian to continue to send those location updates upon parcel crossing, without changing the interface. --- .../ServiceConnectorsOut/GridUser/ActivityDetector.cs | 10 +--------- .../GridUser/LocalGridUserServiceConnector.cs | 4 ++-- .../GridUser/RemoteGridUserServiceConnector.cs | 4 ++-- .../Handlers/GridUser/GridUserServerPostHandler.cs | 3 +-- .../Connectors/GridUser/GridUserServiceConnector.cs | 4 ++-- .../Connectors/SimianGrid/SimianActivityDetector.cs | 4 ++-- .../SimianGrid/SimianPresenceServiceConnector.cs | 6 ++++++ OpenSim/Services/Interfaces/IGridUserService.cs | 2 +- OpenSim/Services/UserAccountService/GridUserService.cs | 2 +- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs index 7d2dc5a9cc..83c8eac8b3 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs @@ -56,7 +56,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser // But we could trigger the position update more often scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; scene.EventManager.OnNewClient += OnNewClient; - scene.EventManager.OnAvatarEnteringNewParcel += OnEnteringNewParcel; if (m_aScene == null) m_aScene = scene; @@ -72,7 +71,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser { m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); - m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); } public void OnNewClient(IClientAPI client) @@ -105,12 +104,5 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser } - void OnEnteringNewParcel(ScenePresence sp, int localLandID, UUID regionID) - { - // TODO: grab the parcel ID from ILandModule - // and send that along - m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); - } - } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs index 76e030fea0..d914a576e1 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/LocalGridUserServiceConnector.cs @@ -162,9 +162,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser return m_GridUserService.SetHome(userID, homeID, homePosition, homeLookAt); } - public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { - return m_GridUserService.SetLastPosition(userID, sessionID, regionID, lastPosition, lastLookAt); + return m_GridUserService.SetLastPosition(userID, regionID, lastPosition, lastLookAt); } public GridUserInfo GetGridUserInfo(string userID) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs index fb11e9a454..e3e2e619c1 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/RemoteGridUserServiceConnector.cs @@ -137,9 +137,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser return m_RemoteConnector.SetHome(userID, regionID, position, lookAt); } - public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool SetLastPosition(string userID, UUID regionID, Vector3 position, Vector3 lookAt) { - return m_RemoteConnector.SetLastPosition(userID, sessionID, regionID, position, lookAt); + return m_RemoteConnector.SetLastPosition(userID, regionID, position, lookAt); } public GridUserInfo GetGridUserInfo(string userID) diff --git a/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs index b1e7eac41c..f8fa42967f 100644 --- a/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs +++ b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs @@ -156,7 +156,6 @@ namespace OpenSim.Server.Handlers.GridUser byte[] SetPosition(Dictionary request) { string user = string.Empty; - UUID sessionID = UUID.Zero; UUID region = UUID.Zero; Vector3 position = new Vector3(128, 128, 70); Vector3 look = Vector3.Zero; @@ -167,7 +166,7 @@ namespace OpenSim.Server.Handlers.GridUser if (!UnpackArgs(request, out user, out region, out position, out look)) return FailureResult(); - if (m_GridUserService.SetLastPosition(user, sessionID, region, position, look)) + if (m_GridUserService.SetLastPosition(user, region, position, look)) return SuccessResult(); return FailureResult(); diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs index f222e31cfa..600ddfdaf0 100644 --- a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs +++ b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs @@ -84,7 +84,7 @@ namespace OpenSim.Services.Connectors } - #region IPresenceService + #region IGridUserService public GridUserInfo LoggedIn(string userID) @@ -123,7 +123,7 @@ namespace OpenSim.Services.Connectors return Set(sendData, userID, regionID, position, lookAt); } - public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + public bool SetLastPosition(string userID, UUID regionID, Vector3 position, Vector3 lookAt) { Dictionary sendData = new Dictionary(); //sendData["SCOPEID"] = scopeID.ToString(); diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs index 8cc5671538..a871d07f16 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs @@ -40,10 +40,10 @@ namespace OpenSim.Services.Connectors.SimianGrid { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private IGridUserService m_GridUserService; + private SimianPresenceServiceConnector m_GridUserService; private Scene m_aScene; - public SimianActivityDetector(IGridUserService guservice) + public SimianActivityDetector(SimianPresenceServiceConnector guservice) { m_GridUserService = guservice; m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Started"); diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs index ec9cf6704f..6f179317d2 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs @@ -298,6 +298,12 @@ namespace OpenSim.Services.Connectors.SimianGrid return UpdateSession(sessionID, regionID, lastPosition, lastLookAt); } + public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + // Never called + return false; + } + public GridUserInfo GetGridUserInfo(string user) { m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent " + user); diff --git a/OpenSim/Services/Interfaces/IGridUserService.cs b/OpenSim/Services/Interfaces/IGridUserService.cs index 3d11b3e16f..e629dffda4 100644 --- a/OpenSim/Services/Interfaces/IGridUserService.cs +++ b/OpenSim/Services/Interfaces/IGridUserService.cs @@ -108,7 +108,7 @@ namespace OpenSim.Services.Interfaces bool LoggedOut(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt); - bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); + bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); GridUserInfo GetGridUserInfo(string userID); } diff --git a/OpenSim/Services/UserAccountService/GridUserService.cs b/OpenSim/Services/UserAccountService/GridUserService.cs index 00a1ae2e60..697ba639d2 100644 --- a/OpenSim/Services/UserAccountService/GridUserService.cs +++ b/OpenSim/Services/UserAccountService/GridUserService.cs @@ -139,7 +139,7 @@ namespace OpenSim.Services.UserAccountService return m_Database.Store(d); } - public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + public bool SetLastPosition(string userID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) { //m_log.DebugFormat("[Grid User Service]: SetLastPosition for {0}", userID); GridUserData d = m_Database.Get(userID); From 213e372253eb8a59f0afb627e11b0a5b6f6b088f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 20 May 2010 20:24:50 -0700 Subject: [PATCH 176/260] Cleaned up MySql migrations a bit more, got rid of all old-form migration files. Restored Presence table to its taboo-breaking form. --- .../Data/MySQL/Resources/007_AssetStore.sql | 5 ---- .../Data/MySQL/Resources/033_RegionStore.sql | 3 --- ...UserStore.sql => GridUserStore.migrations} | 2 ++ .../Data/MySQL/Resources/Presence.migrations | 25 ++----------------- .../MySQL/Resources/RegionStore.migrations | 5 +++- 5 files changed, 8 insertions(+), 32 deletions(-) delete mode 100644 OpenSim/Data/MySQL/Resources/007_AssetStore.sql delete mode 100644 OpenSim/Data/MySQL/Resources/033_RegionStore.sql rename OpenSim/Data/MySQL/Resources/{001_GridUserStore.sql => GridUserStore.migrations} (92%) diff --git a/OpenSim/Data/MySQL/Resources/007_AssetStore.sql b/OpenSim/Data/MySQL/Resources/007_AssetStore.sql deleted file mode 100644 index f06121abc1..0000000000 --- a/OpenSim/Data/MySQL/Resources/007_AssetStore.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -ALTER TABLE assets ADD COLUMN asset_flags INTEGER NOT NULL DEFAULT 0; - -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/033_RegionStore.sql b/OpenSim/Data/MySQL/Resources/033_RegionStore.sql deleted file mode 100644 index 2832b413ff..0000000000 --- a/OpenSim/Data/MySQL/Resources/033_RegionStore.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -ALTER TABLE regionsettings ADD map_tile_ID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; -COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/001_GridUserStore.sql b/OpenSim/Data/MySQL/Resources/GridUserStore.migrations similarity index 92% rename from OpenSim/Data/MySQL/Resources/001_GridUserStore.sql rename to OpenSim/Data/MySQL/Resources/GridUserStore.migrations index ce4ab96e97..32b85ee8c1 100644 --- a/OpenSim/Data/MySQL/Resources/001_GridUserStore.sql +++ b/OpenSim/Data/MySQL/Resources/GridUserStore.migrations @@ -1,3 +1,5 @@ +:VERSION 1 # -------------------------- + BEGIN; CREATE TABLE `GridUser` ( diff --git a/OpenSim/Data/MySQL/Resources/Presence.migrations b/OpenSim/Data/MySQL/Resources/Presence.migrations index d513024987..91f7de55f7 100644 --- a/OpenSim/Data/MySQL/Resources/Presence.migrations +++ b/OpenSim/Data/MySQL/Resources/Presence.migrations @@ -4,32 +4,11 @@ BEGIN; CREATE TABLE `Presence` ( `UserID` VARCHAR(255) NOT NULL, - `RegionID` CHAR(36) NOT NULL, + `RegionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', - `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', - `Online` CHAR(5) NOT NULL DEFAULT 'false', - `Login` CHAR(16) NOT NULL DEFAULT '0', - `Logout` CHAR(16) NOT NULL DEFAULT '0', - `Position` CHAR(64) NOT NULL DEFAULT '<0,0,0>', - `LookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>' + `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' ) ENGINE=InnoDB; -COMMIT; - -:VERSION 2 # -------------------------- - -BEGIN; - -ALTER TABLE Presence ADD COLUMN `HomeRegionID` CHAR(36) NOT NULL; -ALTER TABLE Presence ADD COLUMN `HomePosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; -ALTER TABLE Presence ADD COLUMN `HomeLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>'; - -COMMIT; - -:VERSION 3 # -------------------------- - -BEGIN; - CREATE UNIQUE INDEX SessionID ON Presence(SessionID); CREATE INDEX UserID ON Presence(UserID); diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations index 3dab67e58e..baeeedd0c2 100644 --- a/OpenSim/Data/MySQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -801,6 +801,9 @@ BEGIN; ALTER TABLE estate_settings AUTO_INCREMENT = 100; COMMIT; +:VERSION 33 #--------------------- - +BEGIN; +ALTER TABLE regionsettings ADD map_tile_ID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +COMMIT; From 792e149c7eb26678a309a2c21c66183906db10f0 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Fri, 21 May 2010 07:37:55 +0200 Subject: [PATCH 177/260] Ensure that the first update sent out for any given prim is a full update --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 07c3a6a91b..cf78266b9b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -325,6 +325,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private Dictionary m_seenPrims = new Dictionary(); /// /// List used in construction of data blocks for an object update packet. This is to stop us having to @@ -3495,7 +3496,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Compressed object updates only make sense for LL primitives if (!(update.Entity is SceneObjectPart)) + { canUseCompressed = false; + } + else + { + if (!m_seenPrims.ContainsKey(((SceneObjectPart)update.Entity).LocalId)) + { + updateFlags = PrimUpdateFlags.FullUpdate; + m_seenPrims[((SceneObjectPart)update.Entity).LocalId] = true; + } + } if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate)) { From 9f2d1e9294f7f183e52d0cc5d98c8231e0e55fca Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 20 May 2010 23:53:27 -0700 Subject: [PATCH 178/260] * Made PriorityQueue non-generic so it natively understands EntityUpdate structs * Replaced the per-avatar seen update tracking with update flag combining, to avoid overwriting full updates with terse updates --- .../ClientStack/LindenUDP/LLClientView.cs | 70 +++++++++---------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index cf78266b9b..c8a542b360 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -324,8 +324,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private readonly IGroupsModule m_GroupsModule; private int m_cachedTextureSerial; - private PriorityQueue m_entityUpdates; - private Dictionary m_seenPrims = new Dictionary(); + private PriorityQueue m_entityUpdates; /// /// List used in construction of data blocks for an object update packet. This is to stop us having to @@ -436,7 +435,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; - m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); @@ -3499,14 +3498,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { canUseCompressed = false; } - else - { - if (!m_seenPrims.ContainsKey(((SceneObjectPart)update.Entity).LocalId)) - { - updateFlags = PrimUpdateFlags.FullUpdate; - m_seenPrims[((SceneObjectPart)update.Entity).LocalId] = true; - } - } if (updateFlags.HasFlag(PrimUpdateFlags.FullUpdate)) { @@ -3626,7 +3617,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName); - PriorityQueue.UpdatePriorityHandler update_priority_handler = + PriorityQueue.UpdatePriorityHandler update_priority_handler = delegate(ref double priority, uint local_id) { priority = handler(new UpdatePriorityData(priority, local_id)); @@ -11562,26 +11553,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #region PriorityQueue - public class PriorityQueue + public class PriorityQueue { - internal delegate bool UpdatePriorityHandler(ref TPriority priority, uint local_id); + internal delegate bool UpdatePriorityHandler(ref double priority, uint local_id); private MinHeap[] m_heaps = new MinHeap[1]; private Dictionary m_lookupTable; - private Comparison m_comparison; + private Comparison m_comparison; private object m_syncRoot = new object(); internal PriorityQueue() : - this(MinHeap.DEFAULT_CAPACITY, Comparer.Default) { } + this(MinHeap.DEFAULT_CAPACITY, Comparer.Default) { } internal PriorityQueue(int capacity) : - this(capacity, Comparer.Default) { } - internal PriorityQueue(IComparer comparer) : - this(new Comparison(comparer.Compare)) { } - internal PriorityQueue(Comparison comparison) : + this(capacity, Comparer.Default) { } + internal PriorityQueue(IComparer comparer) : + this(new Comparison(comparer.Compare)) { } + internal PriorityQueue(Comparison comparison) : this(MinHeap.DEFAULT_CAPACITY, comparison) { } - internal PriorityQueue(int capacity, IComparer comparer) : - this(capacity, new Comparison(comparer.Compare)) { } - internal PriorityQueue(int capacity, Comparison comparison) + internal PriorityQueue(int capacity, IComparer comparer) : + this(capacity, new Comparison(comparer.Compare)) { } + internal PriorityQueue(int capacity, Comparison comparison) { m_lookupTable = new Dictionary(capacity); @@ -11602,12 +11593,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(TPriority priority, TValue value, uint local_id) + public bool Enqueue(double priority, EntityUpdate value, uint local_id) { LookupItem item; if (m_lookupTable.TryGetValue(local_id, out item)) { + // Combine flags + value.Flags |= item.Heap[item.Handle].Value.Flags; + item.Heap[item.Handle] = new MinHeapItem(priority, value, local_id, this.m_comparison); return false; } @@ -11620,7 +11614,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - internal TValue Peek() + internal EntityUpdate Peek() { for (int i = 0; i < m_heaps.Length; ++i) if (m_heaps[i].Count > 0) @@ -11628,7 +11622,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); } - internal bool TryDequeue(out TValue value) + internal bool TryDequeue(out EntityUpdate value) { for (int i = 0; i < m_heaps.Length; ++i) { @@ -11641,14 +11635,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - value = default(TValue); + value = default(EntityUpdate); return false; } internal void Reprioritize(UpdatePriorityHandler handler) { MinHeapItem item; - TPriority priority; + double priority; foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) { @@ -11674,16 +11668,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private TPriority priority; - private TValue value; + private double priority; + private EntityUpdate value; private uint local_id; - private Comparison comparison; + private Comparison comparison; - internal MinHeapItem(TPriority priority, TValue value, uint local_id) : - this(priority, value, local_id, Comparer.Default) { } - internal MinHeapItem(TPriority priority, TValue value, uint local_id, IComparer comparer) : - this(priority, value, local_id, new Comparison(comparer.Compare)) { } - internal MinHeapItem(TPriority priority, TValue value, uint local_id, Comparison comparison) + internal MinHeapItem(double priority, EntityUpdate value, uint local_id) : + this(priority, value, local_id, Comparer.Default) { } + internal MinHeapItem(double priority, EntityUpdate value, uint local_id, IComparer comparer) : + this(priority, value, local_id, new Comparison(comparer.Compare)) { } + internal MinHeapItem(double priority, EntityUpdate value, uint local_id, Comparison comparison) { this.priority = priority; this.value = value; @@ -11691,8 +11685,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.comparison = comparison; } - internal TPriority Priority { get { return this.priority; } } - internal TValue Value { get { return this.value; } } + internal double Priority { get { return this.priority; } } + internal EntityUpdate Value { get { return this.value; } } internal uint LocalID { get { return this.local_id; } } public override string ToString() From a2e24377b4a8b33a16885a2785db964d4e3d3e04 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 21 May 2010 15:26:04 +0100 Subject: [PATCH 179/260] Replace CSJ2K with fixed version (Mantis #3318) --- bin/CSJ2K.dll | Bin 491520 -> 502784 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/CSJ2K.dll b/bin/CSJ2K.dll index 31e0d59d546a4fca4a597a57dd80a947ae57226f..238291f7e7ed6cf868afc1a1cf15ade5274313ba 100644 GIT binary patch literal 502784 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 literal 491520 zcmeFa34B~vbw56OGxKKESWi!SGm>meu_I@q(PEECB0GuY?0ZN;NHj9RiJgQvaWd(X zLm|^KW+^l&HiQ}oYAS*#P$@;Q*~%LBq#=~OEfm^PmS6J!e$Tn@%^OK}oRt2T z&p-OubMHBKJNMjk&fV|3X!fPCcq|r6;Qu3!#A5G(%fD^%d*q+}2rdl1uMm4j=JUtA zr{luUA9MK)yXS^(_3O{@ueo_>*EP4?Qr|nY^SUAbwp)gF-!gROlbsup=I;zx0#m2cOVjsTQ`1iw&#T>zRDcwYne);sJhttR1rQiN25%gR^H<3CDfA)VbIFLMfA^+h`K7p{frnx-)FAvz13ou|VzKeP z*F9@5;KaqIUaV`BFoYq=)#Lu0zYBh$jdf?7tKmlewxPMk{pq!GR|@@B{~*;J_0cc!C4}KjOfHoLA$Ib5kppjIEwd z#9llek97$4vH!RmEm=E2ddF%Y@5GqZGh$`!Ixu$DCS*4-Gfr6YJN^T^j%l}@t)%R_ zB~cnlruuh##NC*P-3=W6IhF=m5;V z1DGDb1vpLqz`u%q{+M)D12gQz43pcEQLRaY$;E0otx)DxYZ8G_8_sB2D^)RWvDRcw z5t@b5w~(gnDE{ zGKE&eBT+&b{3HsTqXS53158i4B-EuN)WJ`l3S&`8k_9%?0Ti+oe&MJxOSp@KWTWuX zBn)`DD=qmbClMA>2Y-Y*KuTRnsmt^Ub(sit8LhKWm(g5>HsC|rfDc&)d`J}VA&W8* zBH$+xD3lH$C8AG>7+GtTyJNAe$m$EAE|6tMQC3_kYbn>Qq)=rQv@NYdQX0VO^s<$U zGE(P)eQ@`tG=Gb=N?LKnTFwNfBoM#RzJq&uF zC(uJ`)Tlk@g3{YnRCB4bxMYTa*arNs;{R6spZa_JSVmVz3vHT8%@y?<(Fmf0fuD*9 zHK!JjTN&3VQc|`uWFVH6t#tb+=+?O& z)@hmPh=j#r0Dw(`vu+l6+D$L>TFm;ooQDpWllwvjSg9WofB z(-U1Lz-Uf2XwhutOwBGAS4p23NTei-iHu=QWu2Crt@K5LLdz>Gwbk;z!yX~e;<2ZI z!LX>#;OYbLb>vc>|01w!F;!H8UVI|3i>Z=-K(KvEmcjP>FBj~9R?1*k`>zq~8m*zh zuJzv{*g>cl&5J<~X8rdoKBuN8z~}u>E51|JF~Ar6`xL)Qg*?D_`9D;=QH2+uB!mxk z`~RtUS9_2kevhB*U_A$W)er=D&o3$-41m^$be8-v#e)UVu@K+ypP_g#0dkO7OqD%| zTGocyXh3iibRm4=B1GJxaZASSYj?|#NPFabE1pOe5`|>JE~Luf8QUxlIe!J^4u4B@ zzSu+yC#?A1QxVi;mR3Uj{q)bFFuO@gjF&_hFX=K~lB5J%O_FalB+&RJjqyuN_19rH z(d$VypDsEFxiUHj8@U$w7`>Qnu^3u@4Pw-59q8yiH&GcC8X$0oOZioy-H?&%u#>F#wvZtx-cuU5NAjVVm8<~o zba5t+5tyXnENCyUX z{3{e5&ur!ix)=AEwl1W&fq?1%!^xuGyVSeN$vTDNjI)Zr zwK_~lw!EHk@(wCUVy|+$0`GR$ak_$fJFIsq;Ps5#>16z0LCT#@>4uT-s`|0aZWupK zP8+^k8h*>=k38~-`C3V-KgbD-Dv5QMPaWhgBMDCcTI9c+4i~1z| ziWR$6YLT^7g{{UYB8)kf(O1Z8N0TV1@@l0nH6OpLI00iMdOaU6-h&no^%?yhqsJ4A zBj2(bT3XKkILe6@Fp3tGX!Bz!t9~^M%}9U{ySAr|-m!q(M0^>!6i6XAP$**tLUQBt zsk=%6xd2K5xnfm7E^{EcUsH0&+NotIsg+n!Y83$kV0q(lIP4MIsD>yq(Lo1r56Qlxg|nE}OZwMGZ~47Q2XZNCzXx|tI1%Gxq> zV+|;h6h|1)Smm;yDHHXb(xmbFwc50-gA&LgJaq{}ctapY??PZIAW(;>qS_<=t3kWY zj>cOAjLfREM8wGQMv6o)X)i%*8=b49q8_AX^pq&uvbLeh4ydvXRdzs?Eo>92#w1tj z)dk==v>JUj+C7`KygI}%o9oT@a-mX}Wt zW@l4_8*4N9sh1#Dx_3OA8XO(>QiEgTj{hYvz!CvbHl)33A|0=usO)qzPR8@yBpB*s ziWYuK{*?;#rBwi&j8Lwrk@4g)PGYo#t`!zPkB%!FUqef5wX#!zRDi$4Y*>0WQhHEuS_(!1LFa*jr9dZ1nX_W&fDaHY zv=vR*>rMn4N39}kgfuTCf!bcIWh;YWA!8B6(Z0L)&1;KAJztqiTpE~@m6aDMeN(7``z-&Ke; zY5N#6+&W_IL-iui4uw9fVOJ~kX@#y)=qn0ARhjO7g?1_QLxrHe4EuM5u2bl@3SF;I z(njh2D*o#H?*l>|>3g!qIG$nFKFoh2^j)gZq=sFl(5VVNMWJ&Px?G`46?&>d+ZDP} zq3aZyQK+s^O`&HiG^^0_722-Q0fjKoC1tN9Vgv|`w1Z;yX&h+_ z#n_sUw1;ADj!P5 zf{5ttCqpGcw?HM?O()sBkq(sPCc!3!Pt6+ujJ(ilFlzZ(pxyr|^0OMmm;VrO8TL0j zg0+H<7^Xk+=TkTDDIto&je9Df36egd-qadZpr}QXDaT4)Skn$9%0@c+2br_j?>QgT@ zGKImXVk$^U@Tr*!Qk+CJ6{IZr)J+8`B%+)OQbK%cr-Bp@pX#X~WdkZI_7(G+SPx`d z#KEdhpg|xN8{ybqd6P5`+Q*LFCanWqhRi3|&XjLQ3t2FwREQ=9X^@3v|BQk+w}ZEY z;95qmD>#@pv2P87C9gkd2YrA_Ya)g^$9h4lA5UQ@Pz7;Txj`|4ea-qQAnWv5jb|cJ z8KyWZuzw6ftOf)QmQ>LGW?)44Btq8q&k7RPPe3xv%J-~ig&%z6XWO=q_*MwaiVp$P zGi8|O>=^Q}*9Zgb=WqGVL(Pt-; z;G12Vohh#eFNb(acE^Iu4m45q>mwU9YW&d1c=;rdxPJ}_N3IAJ_t^lUxM;vc5CghN zCUYDEO;H}72aX7j!JTIa1HtOVi#LO)@>Iau4Yogbgs>%Cr7isn7OW#0 zwkAq?Lwjx-fs-p~s12BVoM)>kh`WO6?WKx1SXRdR%nTiPT*}?P9uLvRTyO zu4ZH=Dr7?St;?F`+1kLffB?AuSm7C~4X?F_AxKa;30lBn+hgwl$p ze6FN4*?{mwV$lH42ap87V)ijwfk6T>ULg!)#Dcvza!_URIRL%=q{!RPWX1iAUfR!S zBiUlBj&rYI9ZwIcT(HY8BSK_#yu--qeQ0)()qTuY%mHMnk~RiGu+r{^YL+-(-vUOr zf*{I=Pe9@xQv%jwg#^<9NfC}6MYO{dzcjbQ6uLwUu`EzN1y!fuoJtpqe=h>LT;-CL zKmk@qfi+QJZ4?-c0z*;Ym?*F=3apO;$3_7tdW(V$1h#}`i&Sy7ly01pELl5M2y*~g zwiGB)$CN4xLgFKFG3<#z82&^cjDzfv8%-IBIz}h9Wz}B;rwN5mjO;X=LND1K5YmN? zGm;LD>i@Kl1@9e}iRvRpxOZ{Yy6_+HAuaNlIC5g{KC!mS_ z9gt|9P;wk+#m)yo{KH~Yj>X5N{3eNjpA5=mCGlTj zBc|Oa{?D)|dtee9%l@CUPH#n`+wVl`*CWI#zlXo^=BrRRI)YcyDSt@GgDpzNz6Dz( zZDchVC{#9KyV#pE5mzGZ=G}^*quo1<8Dq2%f3_5HD1x|#c6mO6*h$2l5yX8&pqmLZ zl^8FMASA|t2!hhmJQzVx0-7(0ASA|J5ro8eX#{cjJiP4GyC@C~)|4}3GQ;MK1C#Q> z!5upntOo1c9>joR4MD6uR`xZ~JCO)D*4Lgp6SBK#HCR`B4&!kGtY;V^z`Th*-{_=> z@yBYg_F)O6u8ty&xU=3H?-&nm(jG+>6 z7Sw@tAat)fFjj;X>BwmiNbui-B&+@pB<$hGn>S> z%qaK0Bq-jXO=t>@@dkBV2!)D6htjc=;HEiNlE2>Jtc2RbIZ2Tk&PyfJ!<|j$oEdf# z!UHEE98vpoCGy~()gV=I`JFt+YLKqD{7!DR8cZLT-^q&AxK|+lPGSLu)Wzj@5?l9V zWL$n9?ilgd9&?{1BerrB_(3wZ{5irLJD-Mf`$@m(b8-A6_4JL8``B{F6okSgV^#mx zfOWaO`~?`AKD+)f99yl9@*^IDs>aYMJ8`x9>jCm_n~GjSlxUBP7jr2S_>Jk=JS^JY zP&z4&dv}5#vSEEercnUbox~yT4h4x~+qUnP?V|MV8u6vsrJ+CvDV^j(Yd49R9o$`g zW2UR4GHUlvmZdf&e;|vY7i-i`2P$N71+l9mj*&9fmOnz)$yn}wR19Md$KQpdI=2&c z$$U*lbh){$7_H;yu>JKjG~EAESeZa4llbw3Xg~QZ^}X3zX~H={v}&HM13Vh(XKp&(v3f> z{By*D<$EKlYn6XPp!{3@TIJutX_9U27}m>RB+3SCiji5?+U8%w8M)YMFta*TG>cgg z{UfKxTgT8O(s+pQMg}mC|5QgTwgxg8@p=ga18 zQQ>IMYP=aVjdrOUB%Q$YRK{jH+hvrXN~B<>Q=$A6q#c%3nx*k3#KJB-+hTS`Yd3SF zS3eyb#w=LlFegv5uAjjGY^8FTS5w@qIx|?FS08p;D?<)@e5Op@HS`Dy+_CSk0Mr&z z-1kP*lsEAmIRcQGz7-ww136l&@=d7S12Kpy`*=9)Y2un|lRZs*B&PfD{}Frhy&btu zbiZPP{uKf>a*H)A^}%6NR?7&>nV|T~Fvhg)7lM$rkcE=4s59dYUV=jjgD2dR;Us>e zRsJ>d$*1kqUE_nP;$&BPusWIZ-wL_c(XqcdA9!W$p;Y~x^cmM6-$CRHvv!hZl1mM) zpX{xqLMS`FCNF)OC}++f z8nOrNfoxMk#j&~XG=q&NEU2*_AYo{H6$`@tQ)WEL;=gT=MD>F_xdeGR_@_tmxDT=e zm6UxDN`Q>7Wg)80(Nh zc~%2MumtuV<3L#EKL%E@N}`B9+W&sY>V;tZo@iuo)TsG)NM|J)8T#Krf4up2W{1y-F*|8&@d#%vev=fqEua0+>nq&(H{O|h#Z@G5fWNn!b7z3SrDM9 zXA*&2HWjSCL?3-M2}pQF%R9u8ZpkW&J5#hu##1Z51=E;PtvIugCpgrq9u*v}saT#Yvt;)ef-voL-8lka*iDc%19Mooc zYL52tij~S@8D02~iD8V(`eJtybrgxE7|bbhOifRo9{!D_q^MTy~*rFZR7*e}_! zr^D_Y1KHM|&b4*;;ieMa^caE?rBgS+2?j3LSq)5A6QiJ-uFNa?#&ed;rkg}*08VP@ z;t^3!$_zmWv<+hv(OSHbOxp)3I1e_Ob0E@A%CToBdc@H|NjmPN&7kBx$ayRvgMig| zZxltqYG6626^nq?XvNiFWy7$W7y6z2u-mCVbyiBi5zeq%kUBbrVRx1K)LAJ3M|{I> zm#kemUBj-UK6O?~Kn4xF-Lk;tbPv0(`qaVd4@E%Jc_IfgUu$aB_!}AXWy0uWNih;# zS;XH+^lTWN>?%c~EBp8xiJrq5e-SWLrM2>q!Xq&{6K;>wBO?HIzt6}22l0Oj z%YHHDi8GUXFw?Bz5X%N9Z95tJP|3;Khd@HkJ~UI>CH9m<%5<@o&lGE&IK{p3J{(3B z255b12sN|@h&+xZhO{96S|`q0%B@S(?& z|63}zt(18t70EoP5o`0%qt~WI{97ueL|?UK&cq<;EgcM<#nwC@oL;wrtx@aJKfaQ6 z>8Ew!^4#y+x?t=#z|*G2x={84>w=j^hZ|ZKSi(-Vgg7jL*$dnE_iAejYGK55&}urX zjPYbiDn5a6Qd`rd7Dh}5F^*#l?D7O^Yr56Ki0L54adqK*1@ou2rbjJI&#>F8K6OHC zz=EEzHENBH%GP*}7uuRvLdlPZnVGr`eYsJ{rqTA3pzZ5k9G|H~9zdKikNXywrzPybp-(YOEj^@c~a|1BPU7)2-grqSufqN_LTy=2wKg+Q=J zcJewZ+u-CQU1Vp_%Q^Y5|AIHtGv$m(Y%(I@umk9XG=vqRZT?9Lsl-~?wV7PY41XpL zJeM$5>S5y}p+V;O5~D)1-b{Ki@?5%MX*FcRE5k=QfGal_ur>Z$j4JDAV|I#L!d{aD z*%8}Mt_qL^tez2Rm2vkB+hTraB&cM9Y#E>SBBV7QwPG)5qlxE<+IC9+Z>5O)a@c~E zJ(v}CUakY_I0)z8EN;nvow(R}krfB5&VH7Bf%`eSS6GeDkR^@3$M3Aw_%xguhC-es z)<=wsO#y*FWZaJ$_hWR?k0xWM(Qe>hus9KKegI+ER%B8frejJTren^$oXec8g56|J zRw317PVqWca_k~zl23Aj7Kqn}fg4N5>l@%=J^1o4$Z9ZeuX>)af*d30@U5SRp~uZp6?)2$2jn<@|k-%$X45MJ*-H`wuqmr7&?#sg@ii z28)l^hcpH=tVw+|sY#=hRLfY_Y7x*Dwq2vl&M1?FCZ!MKO;Rt3TlBVtAu*#Mu|^QK zE47;K1nyc&BS?*d950m3jiMeu;%e8=!2wzAB9Y;0*UlbdvD(GV!_}_q1*<2=Wn-j` zS9`SFJ=@FYKq+@ODP!Pfgja|`n!^|$2Qc!?xGrfx3f_hMgQfpZAS`j{aG{^nP%L>- z>dcZCynK9q$3tv&vCa`#lm8>8?!VD$V3K=p&V3m{(UX}$D1@wuqHHDaU6gU^{rqp zlVXarRMy#Sext#^pdTqdR@W#tK?LKifdkZLx?6|p4wun z=1d^@Gl7=SX{FK7w69hMZA=4eQXB)WxBw_=W1W^`&Pfndaf=`+eOBXB?J)^jjelrI z62vvSsd-4arf`zBkwaYIJ1!!K3z`_aqpk3$pdy^a;F3ZsJeoep9+N#K7`O0RF{3#J z1tDf|U(ZQQU&XTqJfoM0Z3ngd)6OPr6D&|^PsFwd%Wd7TYmWsJrU){f(d%zTC2>bg zq2i3fABvCmqqQk9x{;^BW;ZX$lIe9{4?8<#Fc!z)BUj-wc~D}4JV;rX2Ln`4H4hw} zXrX3<f1O~l`%n2aSTWWkUk;8@G(S&563u_4lWcy04m#;CikobHNnE>$C4k^8 z?N~=Fb}VT0??CP_e#`*(zLIQG{O2m*TKpfy|1J1mIU6?*5K|7sW-DjlG;9kx!S?^I z1s!MmpK3vUZBWj?A3@p|*m>QX!G2YGF;nzc6~UuS$!f3?TFp$KKPXr%_pnX|J0Kh7 zi+W=y3zgK2xYg!430^Su*E1PT3s?>EK+`!%lbw@9e&k7jHyL85&Odtneeb6Y_+XCGZ))jV2^PS?s2rK zasbM|8S(UBA8uBi2R@cRj`eW+c5@!Zj^&(K;T7oWDD!)fwkJ%B9%nNs&ElNGkCfOS z(`g9MpuhfroU$Xr_SV)PB$QY?)BGvp;R@IBU_tYT1dxpGO!Fs%#9zb_mDh&Ujg0U6 z0fMwD=I+qHm~m zJ008_+BJ8b@Avfu;PuyCyURZ(1bsXq@81@Jw_bDeF8>W7xT|{Wjs90c@LFb->JPHI z_1Sx`_ssJ5QAvnZ>oPnUA zp*{X>{zM2~yZg4={OJ(9{+8Xh`Im;^-nm`3`8S8)(4L#0<=+v2JT6y)Z16V5e}P%` z4?spj;5`Je*7P524lF*=9nGs-KbL_X&#(~P^KgfUqpBxbaWT~83so@LXP%8VDMPm6 zYLoW;JEEI=>URBn6rQj2+UfcQ@TDg4#0aJi^$P)Pz>d{$-rM*+%ho*6F=j{ZG()pw z^B2em68dX8iRLfqlrJ{=K$-qN)n|1@7_#2GI*4iy zvDwERJ<6KCF*UP0KeY#=3wqroOHHKL;+YxzpF%foF|3VmbY!pE#Q&QH)6$6WJE93KRd{a(uJ(@h>2xsrx>{UTyLUa=A3 z#%iPB6Lq{1#eh2%f$4vP5*Dw2l@b=DzJQS^<9vrf#)T)XF@3Mmr}ragK9Q93nC?vND6Q$F67y0wC_wkYEJkWj)ggCl1~H`t;%g7ce*s`!?=X&0%r zUnw@ZvOG2mTPd~>aRM|>R5{37n6v^V#4pdA&9qY9#4nA#>^9^NEV<3%zle;w2!xDc zg_t_u!qhJY+J7S>&?j39XfxA^(9W>+v6+yl2al5pAdiy;A;Gg_5+cuz1tGzMViF<` zinZ>A5l7ocH?D82g^;|87DDpuSqRCCXCV|kIHtMCgJWAwo{6NsCl-O_y)Z0eNbH0@ z2-X7S&9587&}Tr}aEoj&oFY^wt$ctc?+zS;f;vM3n!+y;QcP8RUTst zQ{K#|A#@9&fQj<97mvL1^r0;`8Lq}?0eyB3_wTtF!9T117fAE~PM77$uM)&>)H# zFnn@j0BiioAzZgJ;Y>0Ko2js7f&S@xkwRo;)s#^m3W~rbHt?o=DSk#1MZ5}R5SIaw zjRvkVz*7)V%vIJ#;*ADjmur~iz}}7n`Ue2W0sSV5VQ$vZ)>0gL2E1TK-Q24-t|MZ^2>?iaB|Nya8=LzjToHVM$> zH&UQ4#vPn1K)1OB{aWHgE?)_`%!pjF67?$)2Wv5Grdj1aoK=8kZXCZ1k9ESQzY^1& zTwggsB;IZu%|v4|wO5}(5qd6jK%tV1x2 z4jE1#s&4`lRD)zM4-3bUhB(x=P+jA)Nn2>Ol^7#5u5Y&mW9S!dx77H&8mW@|K)5;D z0uJs@PGD%sKoBmwFQj@D9WPy+Urx?W;4E`;R&NUs>E)-OgE6mj>M}US256-+heWOP zMxTo#4s7uZ?jLCpadVP|#U;|Zhod1$bBcx`MLi%BREVfB_jjRMdSXt|{M9pSUr-rE zVOX^qtC>3TOj(jw4p4()Ei+TXodaxwN-|d0K7=RqT(kl!hD9o`THtEcMx1Y};Ekk8 zhljiPec@GvPgI_Mgp9YgXPhX14x@?3l>dk1>BGM1H$2RtnF=k1*a#{XCT7wzv+)@R zzmfYF8d2H?^{m$*m12eqiRmkl6J#(ILth?)))^VBuLC17=%LJ*gU}EI1Hs(m$_LWt{eG5%6ESin`8%(Et3@)a9%M>xRLL@Z_zlY2X z$wJRyHCmYy4hxtxO7(zJEo)x)fPW7D4dxGkgB{#v{xjHSS!<>xlSw#tH^NI5>{bl6 z-~XV*#agpiZe3Dbv{(DODUH=;;)?-(t-;G%#|S6|0a$UqUve6RK#pr+#Dmlwr(jmnQz5yA>R#g z7i6T!)TAK*lMcq0ylKz`VCHT*g%=__&Gq~{(SF!$h)v;cJ`P~>hW=jo2Dp-qLyH+X z5}npVi|N?|7*ut*nYfdYBhlEC3(L^!OXfzC!G|Y=6pkEF;*nogP5WVCwz7KjogfA} zzJD6_nY^b-zG*w=raX{}<@hwrA&k6~D+h0ZH=sG*l!DpBf!dQZPKpO?i%uqkComI- zZo<*-dwGTy(`iJ=ILVFw%r{phD>*rvssYb((%!^JrzX+)J?2uUOsPF#sY{Cd;o*z? z_|!+x8qv#hIVXok$SrAv6WIv4I9~GPWx2=I2-3tboaFH4zXr>r+POmZ48qA7THTw*} z)@}9^kV!{en0Z3Ze|}g3R(TVgxC-?agw5*&JbTvUxlmtHGr8PeW^)#}a`8R*P#l*JuP%(ZG`*uuF^~=OJHr z5=ElI2wrn667R~D;$cjKmppCQaD4nAjt8u;le~e6;s?_79~beL)sN}OKy$tNv-A(c z-@_xOLhmO*uM3+iz^4clJa^U9G3fXTI7g0uhl(=?a!9ynHCR-L%PWpWhPY0}vFN2Z zk`UrP2o*toBqhWZG%X2HT>YOx4rDEY`93f?42+3VCYq@CBVZ;EU0D|L07J0W+ck9x z45PiLh};i3UGnlraP-Sfq*lh`h|P-e?aa#f6vTi{i-(@O%DBL3?{WEjafte`pt`Mw za%0}%*S4qj6g_O+$$W!exw@1XMu)Ak~5FEB_; zd1X2VVlk;DumKXIS|U~Iv;^2m2qK${Vo`n{+NWKH>OX?~;Fd?$^``y>w#7-K1g{QZ zm9dn?v6Otx!p)oV=N%6RsL6{1SXg4+izAhOo3|P?l;yw0_N3FtNKT(O z@eRjAF1T^v~pia42TeX6N>zDG7^$> za~d-xEmf?IBb;rvL8GN4vZ9#`qX3o`uYZknn6l&buM;xHHJ%iE=3-lcbpc3Y1|}MW zS_z=Vl!4ngIm3t{UJRM*Boj5a+bQfRI^7&ThbGK*RxQKzI92>jli#y=-M?oy>v?O7J=dOX8mtc+hx9cTt zcP&J%){Yuzwa^zBx@xKmDl;N9%3bx)nz3Y*{z zxm%|;(6iMUnuNtct{6D>?Vi1A)P|0qSm}W#VxT)4W`x5*CpxD(YZ05IJDtc^V&~kG z5W9r1&e^am#KxR3sIs#r;S~2cTTz$UncYrlk8>)jPA|v=c_%t2IaAK&IgBIh#l#kv znwV2`*EuD3y|WHvfUX&LgR=pi8Mo}LM_DL*#y!qC4xSnJc;{FzYKw*uby&U`R82S= zDk%fFBTm_P;h^@38LTChE31|u-BIK_ET11k`r{-$dh~=_agH~5IIyCU{&-2x@BrzK z!3PVFaL3_s>6w5>qO3=QPC!_t({Z2VRGfCqMrRC7(T>}M#295HoOMV%9wt7)8Cim< zI-^M39&6GWbw+C5rnEEWRBGPFv!l&2-{6@$&s)$wiH6EqGw;)Mif(y78(De+1%4DJ9pG~iBq?l#P0r=4vkmjln0ZebfN>^2~^gz5wB zc?v6N653}FP1Vo& zh8r`+1B<)`c*FolQJ{OWb8=85~4`NGbLU2$X!a(ec%y;<#kp8g4wH;w?jP6e?a^qT=jiUxg1Hw&pM2XOJ+w@@w5MjDlR1-QN_>t{i=9z-Z@KD{A{D*sDXPnIBZlL z#kpr8imEt!anE$(kEY$FC$!sroE>;+iFTJ()b0?wT)Rt;s@*HbXw&X}VB;s5k53k)N=bRO^dr66+-RA|`eZFe^xfepYFK{kwSMCcT5ElY@c;)VME?7aimy{^V zeNmv?7prn#MCHCXqTKKwNx3gxQMr-Jk(B$Q!zj08c0}dA_a^?{{P zv2xM)H0Gt;`PW5CxUwqgxUw{@$U+jun-IEO&YZ{7omlC_gY}6mPsYlCJ$Cy_(b`C1 zaa?5uXy;OI7* zc`GFz0utYpy#L+UnJT)eBJTGUu}OkBT#^r>;Diwt>v57`;(It%P^vZm6OE8xuJ7xvyws$;s5*TxG(^ zNm#K~T?=m<(Ps8_;P)z?h65X%L}^k^N^#4^#aRf=5b-}zsUp8|-L9Y_Ef?L0Whl;w zVKCer`!rs^tvWa%$?eLO6Cc8d2>HC}jPNTzwTrUf_yxr;CA-#(UOue(Gn6c;l;;js z&OKtz+%Ch%V71vyp7YM2lUjgW&g4y12T|d{pu*L+PCW$1hQ;tHIMKt7>Tnu^^E2r9xpD|mLyEp8vTX5Db*u%m@yYX`d9;!8I;CiZ!oA>4KA;S1#_l52o3-oa0VQ2 zy7p_Hv~2I@{+D`FZ^RiIiJi6@Gk_^7J>7+0SNpcW^sF^8TsKLT$cIs_#jm65$Lz$iq z8@2bj#7RGgL)i}S3F+kMN>`Rk88KL}Qz~bNX0X}FVU{fyydKzLT6kGn3Xw@|l0t}b z3r=n!r9zZy6Cwk73n{hly$f?YK;i91s_z8j(qn~yh=QFtv}sml>*z|(FF5(5ExDLa zkF7=#5px(691NG(hmCH>NRZvtw>j6faBh9aR{7(Ccf=13Yl@;Wr`q(_Fve1j$9 z+LgX=V2m;E!QC7gWNSO>CZaBm z#A>_`iE-|*4flSy_;f@48i>&_ zEOFKz+7C3IntDCuv17z1EcY^G?n?^!Paa>dgVm@60jw!e!EE_ z#aFr9NtBu(5hcbJp_7sb zvZT%pK_Uw=$pCyP={*QcH>nJ&62&SG!VnyaZNmY|0sqZNrI^-p6nfEOl`>~dQ>*d#>O`B9s z%W8-tw9I`82c(*!!n}X@QpnMkmK@AP<={;y$grXL8~UTf|8^_eXCZMX@ZqR4gsNVw(B38dwxyV!`Q;yxLIS+P~F_+Bxrvam1yJh|2 z`LyaEm9A6$K{huQ>!$;osdXksk2MD8uR)2QKR$MAA@XvGEzdwy&WXe^pP}aiQU0$w z${Km+q(u|X z|AjR5BRx*@#W>>)OHL|LXga%cavNSq55iSgW8< zX)QVh4DPV*$|zOb({MVQKSYaoZhDI2btgTR#ye9*H(Qy+IadeoA2{jSCQR4G!{rPN z5pzXM-)qI4Ja~u50MwhsE8tF67Y>|^o2rasEY0kl^gLFrvo z;?7gTS<@Ltbh&U0u6a%|;977v6?qBQJi|3->LcWuL#}lfU6)+r0l8ISktGtYZq{kuNSF!cDHvh zCmcjdfa`Fp=)+F$&?A_*?zjn1?5yoI6IDKFbJO;FgHxVw2!wv#S+JF8#H3xPTt{LiO;kb0{r5rKjY$!kf-mjGPNhxZ!BsIT zSBRx>lE$d~6hfo@#ZU+IVxpHJv@pYNsrfIIs1m-*5=0+%`xM!iHN6Z*M_&w8EJ)k@ zS0+r>?_gQD`&s`JdQyqauLPfrRro7J@mE)1xLdt3Vv40oaYaA03Tu!EW2ji=Qd4i* zBU`NkD7B}4CAo$M!1l#3NDvmmQD7uUY=Z=S6hZz72xv1_B+Hm}ER6(aeIPXJ1110S zNVDh+^qB`1odL1xE!wfd64E|^La>ezt6UJFB58=n2+XdW+8!XJO8`2fkZz<9!X+RV zp<0X@Zn7Dph8wb>@q(;`%c>O|xD5kr_Jzi*rC}XHtzDy93k3~uayJ7CT5whc3Th5w zGYS+GXZxG~29|LVg^O!WmI|6Hx_K%n_6KOEsG#bpxt&zd&Z5&9DkyARr%}*6@7Cpw zShGq*mfG7*Q!>)PhNWzWR^B+JKYZTBlLl-2tjn#qlR4@D^f= z32oeo21K|+v(|;RelSQa*q~alLEP&Tto8;&%C@1-5O)N-hRlR*o?%<})Q8EoZnDjV z=_1>By`anKCfn3gb9>3QUTm_3Y~zlbVOvke>2`Vy+Y;he!{)k9<;sAC-SZBnAspRd z`(wd51}r2hWy~6SD1u_wQcKL5#th9`V9I)`uXVa%*1Az-*VZX9Yomt8V%E~MBvEUH zPE@pA@ca(E2qoht@d^{A+n{I!lVNNLi2GlG;)Ua-WbC(gEVdQAGluQ5cEk1=icM(C zx|*LQ+=ZtLc3|JG*yQBT9(;I57GIt71WJrHjcNKzMAea#+9SKPV|3CG#@kf>k4ebI z;H3Xc`p$;$#h5*Ih{3i8|Id!>)Ze02qe=eHk&;Y3Ph!i=Sqfh|T58d;DUxaO@Px2C z5JD=LkqeVac+x*C8BCBE-DUT;s>%ythcAIWmOc1eRP|3=Rh1-%ErUraSF?zq7L5O> zwLlqREi}PlOVDbgxw?Xx!6@2?QviGIj#03JYb3qnxG_WzUIbY?2K$eFW~`+ao5b0J zzd~`ay&4l+;q>s;xJXpbMD3D1x!xjq(z7&A;_ShnX`UJrdD62iPa*?}ZGO&(FZH$g zc>x-9yiEltG<`#lJc4O$&i@jtAZrd`;(s)Wp?y$xr}+iK4wo7Shmhi!@gPM>j0V2v zqqepAMMTVaMU+5aX@=v@g^YJ--~Yh|Ts2GI!|-id<-Z7daBi}sb$eTv{%27ptf5XIWgSNARy=?Kl$BE_k4vO+ z)b1S1p6BQ0}n_ndp za3QF&10*YeTYW)4>Ml60n~fA$V1b@f(EOeO;aOVXDi-KDt6DMGk+YxoChkd$Lb9bz zxcr9gVA1!W>4?K}zK&nj{$dI|z0sKT#O9Z`%*aJdpO*9{N6ylMzJu8swCo>82FnJG zcjKT*4XS}s8cFhG4WkAv3%f+asC%ujz<`n{>?3Ifw4O3|49KGGiI9bNQ#6kVo^)Zc zAVOFqkc2U?MOh?}lrgYH8Uwj*kc?sZGB(}n2cM#Y?qCJxYp6{703RWNVfZ=%9ft#a z6P>Cf$nKQ~_+eVN`3pSh9}75+mZ(?kKLd*%p)*8$fIR@KH8oKa%X60 zeI_F+bUr%nvrP0`rp#%nx?iV1A;;1?vxO=BE>5Z5-@TF&6_>`9rQ= z1m*`92%*MU7kL_5MSwye4c-DyA#rshC~Xx>YqKm%OT!@t+MX^gj@AiFEHy5%coVN~ zH4^O%Zq~Iok{BOiWhA+R^%=d+jQw5*nwL#W&5E5$0qp=jt|tW;!Jr*Ly_K@akeBAk z&a}`lB{IfiA!d?^i@3ogvlvb?i@_wb7)~;aI>}r{2?oiT=)hS_Qb)lCt>brm%vB z87WR&tu`|r$QxF6(oVAaV<*Ms2En7}H6E<_DF)x1QcphcVC171)I)l%r;tms8n~C2 zx7}5k0amVsP~&oXO)0AX^NY5XdH3WWYU zfXrp*boG@`;3j#4*=>{AW}IA;8RF@aCbP<56_J@_Fzse?=vzYB0(I&JTO6l5U`sGF zOMopH`(=XfifI03vqK9pfR}MhCBw@wU2(y|KNeUzRGc`)C=k}PJ>RKvNv0Gk zP^skKt*F-Gdr34X=r)i`pJ|?u?7jsD|5!lZA#uDz8-rr7Am#aQmpuE!V)}z(`dh{H zM~Vqp&~KVPtO{3V7b+tyv{rugg0ni88p}%I{oX<)t-h2})vr_)TU4!a)_DH(BrCrr zq-sq-)tVMnYa&!ho&!ey!pd`11p`9pl8al)$?0`GJrVamhsUHaO4aihk$Ys-R7KjX`Zph9ZVmq8YiR$5m>O(a2`?Zg#_$9Wja_uR zu{5^}2V9yA4l=^q35mq$v@z+g2ehRaiWSrF&)RKw#6R zw@U||U0Q^7S?I;T^cv8T%O4hO*2fhJLSA*pqG zyvZ{VhBGzLUu>=77y-2VYIuV58ngz6NKBtf%Le75?&2HVyqB(@fjD?-*54Bn#B&Aj z0zC);Jv)$ijBm4WkC$`9fhJKJkeDCCAd@f<7t=X(z(bV9AUHYC)k}#mkK<8KWt92k zA*mN=p}63ARG||j3yGk6C(vmRU!peD1sTkz(u*MVc>$>r76d%d>4nTMx)QP2nNkNH z4;dSZ4_%6-zHwL?q>p37dIO4yOqk^@X-sIZVC*id^(20in0RMq@TD8C&qyYM`ZFNas-_%95- zn#bTaX7ZjERx=sgrn$fZGBYeh<@^&*3Ua}TO2L|vUWS0|)GX=JfPND&j{u-9K@P(% zkB-yls9MSHX?E(^ur^rN!Xb~PWvn$>JuGGX7nowokRhH4w5Oz;OQoD7%AwDR?9yhZ zJ`hcbGEK^YrhJN|#BxKEJ~I(E6h1GS5=UiBN>5W>CMmHO*`&`*l}*ZXnNm14ghh-P z>$euE+FDCOOqRDJFgw*9EpJ;`-t)9P8i8RKBgH0tW~gq;`vyeHv?><#a{ir53mfdx zlBV9;iaErXKL}%*<;K%DoBqRadvM6m_&x+=D}NAF3mV1r9|LwtwGd*GjBjC*t^SE{ zjTGL{^3F5Vl=>$413mQr4M61N!_Y?PDteTmX}Fs%NYr1BMXOv&EdfBkuIf5H8~b&`z%S8xcLq}H5e=P+@+KG!~5gd_=IhE%sPJ@N_sl}zZn1j z0)mA#HgV5|TL(;B@c(1LaoG#6mc(%9BlZ;d-v~d$7vNrt|J3>7)Sm$Zoj4~c9=ke^GL?wDsB`|*fCiIQ zyE;FN`aV8&{&+Mc$~2;O6zY6uH05JbXBe-T?=P*Db1?IED2>4_Q`Sm+nx%c7tXdnA^>PhGiG$T%39=Kfn`g z#!^Ejc8s2bGh16sN?Lkd0u}BbE|a2{DnE#P%o#Iw2h|S-nTq)jNTHoSTrTB*3Q0ky zTQMZChdo>hJi$@^UYH1bQs>|)|A2P(z!AwgQXc<|JkYP?)0TADE!x3RX4)BMN-fjH zphsbdSN<_F#h!L2j>NNP4Kjss2&%@8{YYeeH>{fU_&Y`~#Mftd*nk$?XU`uUP#h_z zhmaGNM`yU(V>LKLsJ=I7AJLeABz9Ow%INzMlD3gK=i+R9WwRlP9d&f99JK{4H^k3h zw#yjB;V5K`nNl2#j6Du;^YU)u-0&|HOGBZI`hR4hQf?us7XCXg|YZe%@NB@4=ZLnC6a5)tNt>bx972eg-;!LP71T+8scC!!>un)6RO|F+SE zYi_pO5R)6LSX8c`i8zC$oq?}w=heo$HuPPc*M$REQCvgC_hf@fIUa>X*TE{J=AE?X zaJC-Ok*U6*W#&Nn9fE1Z|Q~{H1lR?&Vve685c*J;zs-MM3Lq001 z!>H7u>N``#S}x^H{FSDU&$ntjB45$@6Oh$tVvC&jWC+BY0#y}i>}v}WCWUh|B%vA-K3NgZHTK!dDuL zGj}0wY38oH74Mt5s}r#^b9d8X@Ov8iCx^agp{i1~agleN_!#*u*Q;+sO66XkWtO-`TekYvU5vvAUEH=I%yM zbNgF^=hWrXYt27}OrsM#8~s}o(^1}+p;I0ct|)yz%EB!I$mZPJxu8vb!1p@-0^-}G zPFUqfkbK|m@J@F7+fkHRM6qRFWtEf2Y~LR;_&5{y*#O2nqX_}T8Li0>d+vMi-){H| ziQ+v)$A97Js0HpaWV3TPqnOR5_B|7^k}fd7SsP;*h$M|Hb8TUZHVV#aIVx@SSCG zDz}*`;!AQzW)iYEGLvF4vUz!19iB;RLnf^anFNw)8$X4q1`ek>B6zOdsZsD()1=kOz${@W+V<`dTePvp??$ChsaIEvS;-Jb5A!5spQ z;eeb|sMis)p@R2H{2!nZHN8xyaAe>Pj{Zc|{GSAYIOMejN&No^5g9?WG<1VZ@tnl; z=derqxOm2c1;VpckfF< z?e=!}ws_XXWfVAS*Cht2H$aa(qfo|ndz0ANLUP8^wr15VvM~{KyBIA+OeNN zwx3M9E+d-`^qlsf+mIbE*GSm@6~eGeAz{y5AuJf#ICl9Zq}=}ndkC`r{&gY9*755h z$hPpG9fIV)e|!l3F~P|Yd?CSihE%->p!B~M`v)28ioRI-=`_2%5lq6c7lWTHzU=*C zL|^h{?-ojzop9Z#1LIdKwgLHZoCWbJ*5t}6)+@M{gPY&}v%sbi|Ml|wRsJ6KTj0z; z@-6U_&`ObSfeYCB7Px?|Z-EQg`WCpR4Zj60VC!4p8aezHxPYy1feW}h{1&)?{3x`3 z3tYg~x4<=R_$_b^4Zj7hVE8R?0bAb!7qIm$Z~k@g)XE=(8d-?NK_cT@<j4tsU6fV}$(>Ffk=g;I$0wFoO;GYe+jInN3zp`!B??tv)_m+)^CU)Ikmm}fvME%`VcP+9weuBR@BtX8-=Xah3 z0>(i=`|z|ab#JaW-`m;iCVJguyo^B&jKyXwk@97fvy!N66@>@?39p9Hn8Iio_U}$m z;mya(??);FDQQ8FSI5x>{mA5^)i}uLK~U?5Q9raT5U#doW-_3g`c@bhib)2h1zk2S*5*can#!#>+_4 zp&9vDwzfp#Pz|rR$gCR;S9^|?E+g%Rq{h2?f}uPZYpqe&A_tBaL=O%`E`LA0?Ya~QUq zf*!xc79j*|xqJ6v_;S;|OBwUwU^U1v<|<{3p&?`pzD70%S=2i+aq~q!^N~R|C?=L1 zoBtZ^ektVK-_6LJ_vB=JuLs}Viz@50PR1`w)W^=ZWLUyBSB**J(c z7vu&EM6XRtUJ~TLJBVa!qW6Y5iJ;i>Y4ti55wrfMgQzM)b{)6P_$l=JfXAoMe;EXC z(1pMHy%78ApH=c{zw_*1u$Yp%q$88Clz&#rgz6}TUzx6d1ZnyB3x$;*kELrq^KKMh zJ`BsZPF@7QK%7}aOvmK;lL)Fz6fWiy1!?l z8~zha|Grm(SHwczQnBBnob`~mWFa+^D;09Hc|0mnz!M0CY-g&tsi%;^cO?^t@PJej zpA#$Ys^Ki9!7VuWVl#KMl7%#OkuvF-PF!)`6iHD?*EHo!X{Lmv%M;iotj%EYwEF2( z>8j%7K&rHidG6t zemvDLP9CQI&rE{4e7sb(6Ggsg>y3!Nr2c;YN~kZz>K2Ms|Gv8@K3n2e{Q%%Z`7UA! z9&CZ&CFjpH)|au*9_uQ~X9l~lqOcD4{ip9ujCEt?T$+s^+>MVYYoO(2BRDKMUV*r= zSf`ChHORnruCVhPHE|BUi9c?#l$y0`%nQqvmF|p{x;vh}dv*qVoynz3_`Dp? zwBrWBy<(g2OxWvmoST$&ygtxaJb;b20Ss>Y4{*$iqWs%X@5NNnf04K)KY0$}MSH(+ zAo%)xwjc9|Tnx5H{-EFSmVVQ#`YqT(rjV__f((OhKbACiGYv|_u^gH+^KnF3uWY`8 z?XkgX(2d_Ua3U(6a2&|#)qNZA zvf42cx94Sz%iHu5c)dETDHh-c$Hq&k!OFPnzv+COm$K)zTRI+Nm)nCBrRsWki;|)Y z+m{`6efxr?v30_qn0gt;3_;4`#*$yvnEsh?W$-o^WNv7P%M!;tz8UM){{XpDMDSE; zg0!Xlxs3EkZ(I{}J5vQMKvU<_h?PtaRwgfu7J98I^x(2WBh?t|J8U7ixpY2A*|G>Z z0DC4q|Mkcz07{{3S_18mi5#RCunOs(Z=|$YzZC{#+H^2F+pe>Cjr{qTQwxr*4A)@=E z__fP>5l009`3@j|>=wo@7kZA6e;K`7HvhnyoXP7lAO!#h3k92e)GrZ-K z8IG+%CGd9U|HaZIV)#?v|Jv!G0lVf zJv4OeOy<#e4$^5n7k_f~v%Chh3gs?SN@SEKBrJWf2Crama*k% ztVc3yXLmy4AUQE2rRk~nrIT4(np7DiJVPtO+0yhso8$wO=d~3`~Ml@E;7H;)?|Wu)P9MWHd11%7stzqI_!nx*>a)2+RN-pKY22l@pg?1 zPcFUnCn}4NDh6*o70W}mtOYm#`Lpz63FueVfCsYra0D0cxhs$->x#8e%Z-%~%GiA1 ztAe?>)OU%W%HmS(R!$zc`_Tl|czTJ&d^wS|KLJq`+bVZy-Qv8Dj7>`2W>7ce@Q}y3 zNb!!krKu=0U8rZg%#(XExhyo3{sM^{Ng3bUT}EWrRYpN+m(eYtuW`#+>qd|e=;nS4 zKdIIMeu~M~Tk+Mg`U=!%7wUtJJW*$k!p5J-Gb0ufZ!*%tG)(MDK)lJw3p3tcpUoc6 z)gR(D_XCtqq$P>zK|Jwb64Ow>_eOkk^$lM0266O9g5D$$C|+h{s5^f%K9leF#;>tu zFSl+21Tq+J=O@v62S3Ty+wd8`*W#2ECk%WiaZ;^!@q=pKEZcO_*B$je z8ub<%^;Ue9Mkit^taQ8C=S`Mq!UQ*?&*M#&MCJonB`o8_J{Y(L>MHDbf}aBg0w3`v zi!FSc170QAa2la_EWv_W=s++fP?!*4xh-^Euw9@-@g{3wp~Jz+0#bj_D5_PLeKDZS=JKOf&iDPfR)B{COeST{0 z0yJW}@g2Y#nASNw$`K-sJBi-vc~fKfd)e$x$fba;C2P3U#EpEmJe%(Ckz1ll&)g^y zKsfeDHawNBNO%DO<(@fqXhs$>-@ALwSnx9xB@1$!70*^thTAA~J2!@XscPLGNw|)f*J!w)B zA%h5bzb_H+u1;FKaSkJ3?coDbMtG!6Jqx~*M$6Slkx%y8i3(!puAMM(+$rIOp2k0) z@pFjJht|{I1W#pAM0=+{>6$v-_%2#``>XhzuCWK`qN)jxpA$eW)F+bo^pKZ`sgN`U zyr+Kkq-T}3hq@KMj~g^#n?h;&D<(g&0W+_00Ar%v-_q1tCbrgiJPt#h7rg8q}NSYGb`0cg@u%H{{)i#G`?FD{ps@)}hm?xMID#7*Ci(9*KEl%DYt z_&bf=paa2=3w@=!4hYzOD^~WYcCe87e ztAkTWkGBk|iTke<3&=LzA4#1a(Vbo)QJTqM6ebh9rn}Mw$O^yNqNm!W{IM-1+X)hT zTE;|+Z3yDdCQM8ul9O4`eKu5m5_DS}>mbZiKCgEbn5X!}At^5nxgep?SM$9@dGnN6kBEYv-8+ zu#%wVGwJ0FILj<5o#ln48EDszcJM=k3B?Z-JstFo&C>!RY;XhlAy2$C(&1SzNO&O4;+Bh3-zkCEQUNlrUNu zcsnYyAJ1yBsP6b4=#;^oyRc}r*>bME>QDMT@_m3tv=yAbiM5;3+PU%{vR+aQJiiB@ zg!)I^grEP>?|AAXU*Lo2*_`YrI9Mkhj@fw%x{HHmdOKRai%!gd5y8&Dm53tkO0q+{ zu);}r5N>Ken0xaH52NusJ2dI$$Q0Nv=zv1dLf&?|x13&@hAvI!33mzvMx;(z*qILr zeM!Z>jyiB3j3RMVPxrVABvYk!QDllP=In$5&FgvKVqmzlY90niKAoN3p3T=G->HSk zETy;RieOM2hh>XKmqPF?z-0v|Q+b|CP@nXc&xVPm_j{$So6sFD%8Z>?%&5L9-9vIW z$BPD;(_eJ7srf+^X7aoaLPAgn;I9a$_y*AmNsw3mB{0`2z zE5Ac!jCPDu_p;LXuyr=Ms#_jzjyDT9WqgE971_)(?{lyr!Ok|am-Vp45%vo>r<1#jp6quVtSEc4LVcYl zYWRvN@Dd)2)GY_5oVV?FyW?{!7x-;f8f!RzycGX)Evx4tY_!6hRI|e7WqBuPWgY5^| zAeZu55ok0S0Sk*Y!0kYHUE2H8Z>J~wqt(It2jMPBePGt42P=kDYHmOJl3CV_C!61_ z*sj7ZHplI2QZz2KH>*FWKa+K+Q3xLUwH%P4AXt!dKt2bnWP6kQgZe{kqWStUHbw#E z$ENk#2_F#)*W$9C;$Mg=`{Qu$`fX}6rn2MG^k>8N&gusF73~f3lh8yZzlWR4qHd5^ zha2SLvRTayvdWc(yUXvlx2In*JrjFY94}DiQgs6c%3L}}43u{+^7vDl{?3CKG_t*Z z+6voi=p;VW_S(3OGPc*IV|#6|QO2&Edvn<+N4M8&Tr8;PjBc;tL##E31@)T`etUgK zI1N9FX^8#c$M{LLKF$x$^PiA!s`WSW^;)0ASLj+i299A2GsYzLHZf3hAhJxN@jQ4G zln3c&auE=p#WCp!h>zNsO9Z4(%a~;pC=a}tzW|k&ay81Z2W6<{mBZ$NGO`r47D@qT zK=4bk^guuFD7d^Uqd1#B~taRzXe)qV4*h=LOyj6e>pZoYx#7uD7vS*X}H$=J!ra}U~P&^1kvz#TO?BsFxHN9M?Hq;>jLI!Y1}iNXWk zKmfKLaJ53p=lc?-AT}}s9yomYoaTdJ#;W&i1Sh#qi z`6A&X09yM3-gvZF3Y_nt9hc#rbV9sIXmd9VH+a`yvTGxni+}=@ekZ-y7>q09Nqi_Hzjyl z`~x?Ipj!L`Hw9)|A~hpminV}wjXij2ngV6Lpr35?t@fMGg2u8H(y!42g65*>@50Gg z=jo?WpL5&wN#J1a>O*&=J`1eRDd^4rx%y0Iyz4J~5EZ&u@3cE>^r`U+RG^ilW#cUW zee7L3uj0&>x~AkkQ5kg=p650Q`N7O4hNM?}0scUYojRp|fFYVrUjn*$$!@R4@sT=c z6h8e;BBqy|xm_d;uSe|hnRxJ9&N}gc{@>1QU(%^C=&OJs@7g@Fj@N!9%plTUtczls z3P+ZEk$t@RWBgql*xh0x@#a5=Fny#c!MXaFzW)JJfe7i+fT-MvH&q!sigJJkmWCU`+kUeN<>Lc zUC#&;%hZ017H_8(gmPba6>~)d?O%z;ErF&GMSEEU?I#^*V`;f!+x-O6_KH}1EnAf{{(&^)w&l(Fe$^Gl5-*&fcpd$Tdn;k zg5vRH?dSNm?vjy%!3fUV28=89zZU*dDE+wiE@FNMm|*xuFjNX~@7HNVNJ4pMh~tIQ zd=)#r8Z;x``UmD>0pB3t+W>$rVf-`5+xrlZy#w(k!QNhoeq$e@yb6|V=1!THiP&Gb zv_*(kgrz}yL{R$A@a2_W{bF3eLw$?ax>NJyrvY70UhD4(X!5TvMCSz8p_klsX6;dY zz4btm%2V}7tl*nSAS%(@cZOY0ZK{$R?@>~*N2Z`cM&^?=_S8c(UjNkb!8l??Q~5PS zG-Opiv{i!xuB1F`DP<-|*otd?M zcCDXFb6+oFyf+}C`=O|@M`NzcyiC4kBW%zFi>=q<*c%o4AMyqZNCs-2e;LRs`SJ`L&7IiVo7*iV&t=u@xWhazra$ z4;{(mO)AJ73I{L|J`rzzdud=k}|IEL00a^aSc{ROyBPbHmP2d#OpE$h8P;&_$DYH_@*3d$>nA!%?uFd2%>YlCtJGu6 z$FpCSd*4;)iL1U&*YoN623;?p>zj1F zkgjjRg}qoZR*A)8=X0IuFYg65_DQw>#;D4Bjg#O@WB)07CIrYfq#fk67mMK**TnZe z7DFtz?`|Yki(%~M(|g~B$OUij)6msD*~ZiH$u>WOL)S#XgGy+4Wv{t+56)>ml(xZE z!_Uwte(LVkz84%|=X-lm*4!1KP1tbL;JvkcdOo6z0gA&29xxcSQ0`}8_{z-NSw%C_V}Kz(^+Z@q2<7(A(p(HTg{A-v>{F1@F2sS}!0l_8=?nOy54Qv`be+|O@U>C!E8;%!|wW}G{hp@H5 zGQ-x|uzUl?U_8GBt6`)K!+weWOWnZI$T*#@)W*KRW@Uw9*26E2d zWJdZp**t%=owGl~Lwrzns?iJ(lF^|EvIC*c4?VU<7S9MjIRrQ~m| zKh#W`t-5_RQ)X+KNlzjv`%KGBM_^nJ%MkS6Vzv^EyR-cWUhua9uzuzPC4Xk_VIW*) zJ@~D&^;3}a>~cwt{^B$w+4K#v2$1LMeObUW*aK7&1@^}I1T#- za~!JXZ$D^2KL=@HC{T+?tbVyU)@(O(rKLM=psT#lc2}&se!AjRA~8T`RL z;mhDhjN=|2@{bYL$iet}^SL zd&{bx6P){Xt4{i6#QnontFg<7a}D|da(voKYmlr!d0l~EVU;UT#;riG=GCzRK`%^J z*PkQ^a!S{q)ZSO1QLex}k*q&hzYaisv{=L)15lP9$ad*8=R-OyKOoPP$Fj?h)o5fq zEpA#Pd{b%?1O2+5$GzJ9Sd(=&<@_yk! zA$khvxT7fn{e>Oqo}lAB08;ltbP!$&;t(DD;YjLfL8pK;`Y%KerTFfteHi4pU6_oZ zOMBp|)RjGy;kzf^M$HPUv@e2W+3osFsX9sHQ)H&__?nC3qDR)XGL z^!JOnKOfb=ZYeeptEklc!wkw;ROmGSnD{Mf2DdMYN}21`yMdaus1UiXioahr@&~ez zUR6}-jNnQJqHlsfS4Smyi#l*?WAWGv!FTo$FI5hHppHsVI!7P}yg`e{<`15EKB2fO zdzLz2c}5+$3bS~eN5RjZPCV3I^M!QluM~2?8?t!Z?gfv22JukwGt^NDUZ{@#;GNHu zAl#dd+@YH9WF)>V)WP;v`!QJLc)s>t#2|$(T)jI1R~4@a{!5qWbhpcUtJ0ARfj`d z4MZ5=6hvN82d{n}6Y8x5?^1_rkX|76T0~wZ%OUZAF_qxV7GoHfq5-0@h`c`zJ}u#E zD#1Uf!y&E#;u?#HgL_GK!Uv3B3ASCy6n%&21JSpLxCAc+FU32i1EvqdOAz}UVjrHO z@fcGzGTmtH(!O59j=bcGTyjM&xp<`mFnDP*5UV-cuVMyeJBR+2?}vBDv04GANJm*;O`{7-^J*!1a~RK#pp*2Hx0!`$hH?Sj{z40 zCPE&q5Eo+rG2Da{6SF$O2g|`tf(Q$<-&F??-He1y*-}t>8IxXzk*AJI@Dz3Qi|QZ* zA%>fqVj5)c%Nb*+99*f6O7IPJ^at;Gg#;mno1DXDxD>o!z{8lF>Zk;_sl&w>MvQQF zR!y}O^t_TORb7;7B`7PzMX551o2NL&IGQn14sN`faYvwnNgZwujWCLvs5tPHf`57y zG1t3xU0(@)tq|9)>lww()D32XqjVd}!G~VMxEm_Ly4Q+>m~Mt{G#eeQ+gJ`Bc|8#} zR)TZXK}Jl(0ZiT8_{q)%v5xN%_X+?e z({kxwPu0M-$_XNbKO}fpyvgr`w->j-cy5xQE*jCh6Bm7N*u&tiNl?}%s9}eki!JzI zI|C+c4x(H&@Uam??~!n}vv*D|4p`eO#92CQoQW0Vyza1Zuwz{zpVuEY&h;zCp-{`A z>jTd23i&J_HqIMYjB~?b5zWEkuK?Xy3p_6KSaMva8iT0?*!j1 zc8T;aV@x~@des_lK@zry)#x*>bFKEA{pqqdAbNBlx9bgCS3g!r>)d;QC5hMG$`8`! zy<&SjNFJs5a5M{~{5Fkq)8XP&y5hWDq9miSZ(SY_}?>K`$p~Jy0!Q~6Be`CrTBU+ z@I~=Wx4?d(o9JNv9!$_ism{0ei-pM-Z z7bF`0j_7%X;5FLF>0qJFo{(t3o-q_a#hYx`2^^kAeuvgPn-I3JK>k}HiF9S50aM6c z0?d5aUG@@b&jMUNxG21KcFE;MlQ6_D^~k-c<~?RZ4T{Mk=@T?u_2dvzK)LM))~Q(d zG2~IfYr6+s70hK@mAtJAAhs&`5v>ZI#fDYMAG#{9on6xA)shzGUV()R5oC`#4#-w(rNJ$Y4xC zM2g*}!KJ0jVj-@&$XmYyKCVRhfdw-d=7tWhB)WGuV%o~_2_2_VHjUd9F-XQa)b?=# zwS7_$R2~8<>Rdu*P0q`#vzunUG?~T>Rd+wMkA4T8;bL_sAS0ldSqEEe>$FYlWAw$F zlKrx`PI%%6P!|hWF0GT~b*$j+JG80TiY$BWqDal4LA|3eE0-d}cGHoUNmR5H@uoCJ zG(`*OOi?6e((^-Iz553AE(ieSF@KzZ*(3Co@Cp5p@U`h&sr4K7Qep0Wz_b*q251+c?e$ay!Rtyt%GuU#@K_vj9$sVRY^ZR~{KxoW?G~TL`^+=!+ z-EDj^(eL9CMiKh(({tF}Av-NIc!JQCvl<#@S1*XZ>8;O!9pZD)mczB%Fu(3-U>(lE zlJGg!VR||POpik`MW~qdi`L6m^DMV!gF(=CMpfwJYV%GM4W?z2QU*Ga3JlPKp-B$d z38cNhf02&7G*T2ce7^eH&Z>^!q!rU39_~0Z3o|d45BS4*u_-`-9T{EAcqdW-i>~n`6yd@i zjGc#=!n$m=qJ4RTDGbpFwsb5!!&zHFxU1PLu6+vCTNTPC4$>u&9x&AWZKw+876gvg)ou)!Fs%?@t3F&2E}3?ywwGAx=n8W3%Irs z+HUFjDZIwXH%o8fBkl@ZFpVjM=)e!1iV^#x)2O2d>o@5; zZb*PrZ@8;rw}Qs#@G?hKXOkzj%PW0h)#bq_2^U^V+xcdj2>?6iLVzt$=(l7F^*~BJ z`MJ-^P|?vL6dDh|AepBW`u}1jF)m%9ED^&4TXdMxFuMs*5Ncw_-~0?o_~C}&f8e?{ zIt0JX>)RvlN$5GJ`If2nOv-G<`A(-*W?9FCpVr=&zLy!G)MMrgkpj$oC{ew$k-_M| z^P}QbTwcc`Zz`RBkY|#%MD&9^1Vj!zZOa$r&c?pVg z2>l?pAqe_5PQlh8q8rKT2O(6SJc8YFIQ<}kcph#Nu}(cG{U8$I97b>p{gLShk@(;> zMV?On@%4j*{as^IOB?E|*Z+s~gZNuk#lS6A+4h_lGOn<+3iB$BFMsvS@OZU%ygD+j z8a^mvm;h0a^1i4)IiC)b$RXMYeh-@W)yq?oTn7)|V0mAB9~APwl%E9_5MK~k=s@rY z!36_|(!3VBE;vh|APbG zc+v5uR)aUysdY$MMHCupvx>wAR*`(kDzbpuiD)nKFlYIw`=HLLas^9>z zZsY*5Hh2`yQUc0C#qms1^@3=+NP~ML@YC%ZHRgi1XG~8MjXc8woGEMaDu>*Wg*K|@ zqi$``NQR)u^@P;Tv+~ngQ|y&Qnp&gDOG`_RS%;b1j5&cx9k zuCMH1NK-E0fOHUErZU(*^8*_+c(1^v57Y(=d1~N=%fH0LaH2dlkf=Uno z{DX;!4JhR>N;#0|M@dU>TA`%C-bC!NcoW79&;lGcoP(dk7ZVM8V5WxImjD1KlvoPC z>+qA;?D6IZLPzd{eJUOMu~@!A<2mXhUoum@FE;Ys0%Vm!z{SOJtGIo$^XET8Qnwt{oR)B|S& z)Z8TFVj8zqrL)u;;zz4~KT@2{#j8CnmbspZR}12b*Pg(Flv->c8I!5lH?Vmn??>A@ z30*b&N(hBYm(g{B!%SHq8Ve8HAOM@kxmX@3G zktd7jX{(4%h_pAI^z!v$XV41eO_$GQ8#Df4T4eiO=nid^xjW#L+`Z}E&gAQHFTW3G z88-R^nA_6RxQo#qU);YBqQMM{G*=1x;d7xo1Dc1mGC$M#k=eT5rk$arY_zK~+Ew*a zErLb}v9KGL30BbJy5#P7faD-r)|)_clS9@XO7XM|8KoPk*y}MiH_902z$@^L)pG#C znORgbanzxDc^UZWrCb7Vtv#T#&5XBL$gJFD6G$b)E-Q3*nU=*QrR&m5`!nTp3$`kU z>#uLO`|EZ%CF_;?t04WAUYh801-1Q!5-;u#GiS#ka~QZo_GA~^df48|hP{;y`vHYy zKV%QzTi8@cZ^6JBXPo-7TU~D*DmllNNX=ix{>s>1;u?M}nsPEihuXd22I5X!>C-4T z`_Qv=r9JVWvZ`Y`*}3OUXXBn^vvLoG9b^-J(8Ol@KZadpvno7rZ>=!79MSvV)>W=^ zxL?Edp#0M%tTYcbnpgv5m6fIDXs9*fN(wGB2~xFBSQN6#*iOM=e>JSTGBiFUBpZ!J z-v|=UOg~pw;)&6Clk1*burxlxtB8^&JSvN4c|q4mLDB*!R%fs+g}+iM(ykQ4wiKgM z52VcML@V_$S1BDu6WMj(<67IO3hhebF{^m{rp5~>C-a^if2I2GUu?s)D+?!eBf?`);|0Y4<2FtB# zN>bWnsXg4Ly!0>>1t-%(xLp` zh$#-83r*NXCGNzz|G&;A4u(kP?1^5>yy}%_ON!#sq(BHYDLA4nICl@$ z#Ng)ec%XH`gKyb;=pNW^^K}OwZtE?P)DwhBk6)yV3bvg*CM2+vmc3k}w1517!ocw4 zuE%~tEq2`Tsopo}9f*sr3bd~6n#RXSkPlgy_5?l5(Ze2cY=KLyIx^JzjU^0$%Y@Dl zrU%t)F5KzM$lw{3^n^kCCh=#XeRlOOI}v0EH6ao{#IZ{Gc!XUk8sT9ydOD-=gu7BS z!oz6vbVlO=c%^8BhtcTijK=eESG4fl97ZEjXEZ&wYg=*NqmT8(t|0Et0Mf&{g1B!2 zNYCjCBEXiX`Z_WAb4Bu9Y?=1L3s^QUMx-HAn5%VWJAg-SO*j%fuI2Dr%rS-@Z19_zI+=nf*x?j(`TrY$k)VO>Bu z^68;-^GbS6GUF(X)B&M|%+R@ovY(lI6e>=;1z z75+qgAA#Q-ep~R{gx@;+AjKf@82)(t91hbVUJXCuZorTDNL%9c;S0{|Dx-OQG8>D% zC8K$sl!P1kGVhbn8(eB$jAnf#exvw7VvW4R7>dWvlKk-zH=^JLH4cjSF?32g!29{h&z1CKb?iyw+2%wUMd$rD@VgX+m!Fcdr1kKX`(gZQB;5_cHiDt;sQ zt;Y}5m4B>@hac<4^0918ldxjs=gNsD zl(ukTE7jwVQI}}>QKW_k)3X75Qr+ zcwY!6+baZL8G?B*48d20VDf9KBl~dco7{C3xw!+@&E=7-)1&bP>vTBYgk{I%_>V$$ zj`TWiB9WXM3UN)Z-o-f!RWZtd$0i(X#)K*Gr@!FG@XQ zep*>eI;Ldgr1zJwhe^e-1EOwH%FfQ=ZUt*R#<7mB^iq!u=M|;XO)Zmv>>3shVnLRY zq`jjbrW$;q=Odw{bZHadaQvVxsY;B>gH3n`4;duq;)YGIZceyHg3*Q`yjL zd)%n4K$gIP9k)H9#3}3!+hv{Wl1=MDESgbL_tzz~3-uAc52fWlt=snf;tZQ;_DK(1 z*JD?MXTo;06?TOmiQUI!nN~G>?&5+j<3rLY=c_-RuYRAi;{Pk>D?A*Me>fVQh1oEA z`}4!25f3KeiRlF)n46Li4E?0`G{yxn1ivT*bH6&?Hr;nTypT}Z!B47n0zZ&YI*~~3 zfwju0T!2#HOl|aSBdyQL+eTWSV;U+}1qJJGfjGBVpJS313RA><2kAlBggIE~y5MTT zg?tzDvrrYq3MjG()$!-^B!tIu^FZKZxt;j3U>C>%jd5p3@wRq9aRuIf2#bCIXWP9W#Gfl%i%`e?f_9VWxK9l) zbSk+&iN&Rwd)0Y=a_9y7(rcZf-fCf?CjN)C{owj}t%t%t4)esIy&d+)!Lo7o$LS31 zkBf))#~acrT+5Cu-ZL2wm3umJXU6=$Kk~mo2KHS~5!)OVOMgoyhNVno4-R2}d;?5i zV{3o?hEMgMHotqr8UA_mH2Z!1t_=&9ziEE=;*HK4`Smv*Y+M|Mvgf_?_w`!j4VQc8vQ)5I ze!YAau3y--cw?Bo8ZWn{knBG}>)}HOrSgw16~tC!?dyT_Dh%XRqtE2u4Y#hk^J6xP zU5n2rC}N|%6LC=yqeYSku2>Np?W1UiL*|5b(42}GNMfm-X49j2?e+fd6H=%L&TFze zo4wwn|Hj6(+2Guoi7jIcT>+YM&=^XWr=A5vV7><_w})aFMav-rO6tgg5Dk%$B_Um7 zf$Ex}AnSuN9F9G7wE93XRrI1sVv_INElGBdj#<=42KX7i4ow-CFfA{mVub;TVJe;R zIgW}7498o$^4CWu2L=SLHFZ$*v%xrJF!1by|hHTIZMn8O!X-BD!FJto3I9vOPD`+g8!ICfZj=?bQg z`abvpdQgGNvn!P-%qc9<_~j{-zL2QQc**h%&LX98aaYtm5STZ|E+ysMO9~2_N!r_C zA08vz(QG=cO?h$Bru>V9Vjo$Ymso23TA|jJ@f`jyNB$Scz&;3vfm%y|za<^}Ck$oq z_e}+o#`$D1xlkx({UnUGZHAW7WFoVa$P|;f@xtGQB^csc+@FC}Kun|@Zjhumah)~u zHMWOuV^lQCE+QujMX!e`rpjCWVk(OSiM2$Vehd5SX$47|*+Tz+oX!NN_6KT=W=nq)w^45yBzqFXtIjr!&zQ=Ie zFtG1behxowPfoD@-i9j=$m-#aY!JC@u((Y#bSD}<0z+D_Edv#B;$1eyFP5V(intF>#{Mi3i#1v9$eSXViVWWrjls|wH8nNff$0e|U|OlO zk>7YcIF}U^x7THk_(x!(tz?cEmSd~uskPeUQLEkd2WX(shXO(w1K#7!y$TN81+&(j zHj7`10N57)mybg(}I`!-%p6=^wr#GO&u!g+Z+AH?tv8Z?LL>5MqGiu%~ zRJi!V`ZHh$d9yWWoCYfHd<8X~X`F+;X+MejA#~f~eu*!e>qpxN*?JW>*f5;D2Jv7* zE*}>i$!gmzRHpcy_A{x^IorqegImIyLtag8eq#^v-adp@hh}M7dxQBFj8|Ai#`%Op z-Q<@|f!5wsnSW?U?)z;!a$hmIi3d684*BE>D)kO!v#rNjUxUrIo(`LBJ+QtO+GqZD zr%B|{kg_Ru*ldFjv5Kf7^#VR^uUopu|WHDXL6tTkZeH9-l0-N_Vx1gh+2ij519;|26zY&L3=$hOp-%9X#`Su4- zJ3x4cp3S`k;XTa@8UANbA(I`MH}A1VxjA3O$>5ir=Dk>)G7&4j|*sK(S4- z5^Pt9Q@)n$Qn;CZ7b8I7rX&w*i$Pf-E<&nH^X7YRW&|9~H3HZLpI3;BfOET3_$Mkj zFMJOppjoa|MfW4j&*Mnnxp`eVn>K(Z%_(4>1@` z^ci(PndOHCa=^Pvh6f*eK@c)IekLS*&x9kqaDF18?AII6zmRRh9r76f;f{c41)N=; zorb;8^4S>(4HYQ8vnLM;$fu~Hm8nO^n@<29MQE9lFwdP>-;)4P$Ex{STN0~ML3I(_A2?2I3eSDp^Z%OjUq#;ipPaEZ*X1tBBJ+yt-O*I}| z!)ax!<1m%Mx-N?Nw5uaq{(%aJ9$<6fUWDUd+gqdvhwU=y(CYZ1>ui&>eT$S?vZ1L* z+Mf9X6qav7tqc(MSRcg3E8gUA%EcQWg2x-b+zl){sNgirGk+Lb~HJfO@#&8&G>uS?8#=uea`5w>*KWZYLP9aMI>>x1c4<4UyL<6MyaVEzhat(2 zM{%)PXqUAdmh}oC9j2^6v}IlOiNlt)yo9orJIeZe#Auh5H6g~XeNd{!R$BEpNRaN8 zD$nx22MC?%7lO16xmxW@6f;^r?_ln6aN_*9x z>@en*;I+HEG)(+LAW=;GLUd6#jN!xhAJi|gdp{3&DacGRA8f;Pw-|0LX$n|(i)okw z7MskzM^na3+1H3L1q=-rh2s0HAh+|q;Hhv6QJeIoXPlUDVf z3U4g-_*J>xt9o%a0QbeK-a8lYw$04*e8;4s)Qz9HpfZ4Ic-QndxYupCy?4jpN z^nkvdix3p2`7C_n&1d58u6PsKBp0xe+`~X1ooBz#<@XL;>^=vMi#MPbP6D|>?i-Q`tr=A5O66J?GTalJ53P$7mz8QY)Xh4zE~Pu(l%$%>Xd&KYyv2Bv zXfPR~j@>PG6EK-kyvbq!lc@rMi3lbgyITSN0#hP1>DkrVi@(qSAnl)Md=ojheR!Xu zy7TWr-(JPGZL(3Dj|I;o-uMEBZsWHw36M*8TOX>g;8mCX#3#AAq{tJ8j$S`;r~UGR z!25N4Ik;Sg=|bcs79m>ANE0Qkt@^a4yB6wL-`OD8eG*A)0*)P2dq&!+-VwKq)?kCd z=3J&MZyV7d>e!v`1}v)l&>y$HoNTPGj08LN*k4*-b}Q)VdY`fBV4 zUtgR7EiWnT-^MR(&*p=#p*zF=K~W-2T~2c$XM>6ZGoFFnP_(L^4Ua6JNEN$N)Y0Az zwfQ7tr@HEo97^Aaw$bQ z*56}X(;z#kTvDd5O!u~TcRpQ~=iKZhaiEh()WEjxf-lNB?+D8JdyL6;SxHfN1lD3-b&8uQ>b^bT%3l!fQy!&0*QvS0Ll~@_KqyCg!!P#^zp>R00gEus9I~VhQiHGYjHhI;IyNoji9|Bz)KdL zr^y(lzA;xp?>RCPOUXsZ98AwEji+f7q3{$DW&%pc!FUn+p6wv-*$8!CiK1&4)INmy zN`;n*+BdMOIE|Kt%q3+xDLBCYbots;)X`Awj&}JP-#l#jYnVWHU7$_QlU<|0}&pHZx$;g7VAiuz*zhy+q^V zNE)-O1NI5PdXmi)(6?g|0vCfrz42{K?rk5&po&BNaP3|o$srKWN$H1HRO?S=6z*m9 zo7SAEZm=}F9k04z;WdjR4Huny+FCem;qef3kfcqDVJR0T3sMWOUXu0GBqr9)JmRWh zxD{e+I4%HJNTHa_Xf|0%G1&x%DLI)>?tMRIO^cpn6UUL{L?+pU08w&sNKr~M3Y?p> zZcb#-8=pm;{Y<>^IeuZ`5a)gz?2^qqB9m<_ha4Vm(1`MEiO+!QYoO?{UMYcf?!(1z zKKL>R1Ren8u1Ge41aA6bX$C3v&|Sbgy?pSSaLj}#$tGJ3wsW;?249vRBvM0m6GD{T z<;6u?37cZF$wuvn$|_4#hDcO#Fe>XDZ$25LAaU))ESyiF6K9pP`CV?}!C>~MuBIxv>C!Elvpk;y_wi+PtN&~lA`MdK0zPZtD7$Sw$mYy>hc zr=jam#PNTOH?PE(lEVBZmL~^;*M8b*J__CpXT+wWLsGp*gOLBY6C05ia4wZwO~j8U zQnjzMT#!I((GS6|=gZe?y$;{tyGSsHP_ZW@7i_KT0SAEgkpwWtU>amu{m~&a7JL?g>Sm%e+SGU_6Xp4g zk$8P zku|8#0*MYA3M>iTb>Z-CDU&9FQ61-l&np!cZJg45t6Enh0l;V{N@~-?np}p$I^aPy z`FL29?Aj@4{$~nMo%pK|%+{e5DQ%tCBUsY>oTMoM2c`KD1lp!ew)P=LJeA<_i)m-~ zeh4(N^*lo3O&-GZK>F%#z>6XPjO@YI3%dcY?FMj|b|m_$ZorEp07>)}5x}uvz_hOF z2D|_O>>k=BeJm#~HP}L%cskKM3tkYkc#{=vd>eZ(D9LTTOq1cK^>X~luAQYiYw`2s zUr&Pj#Ds%{K%?BGk?>Y!KlktrhmGZCkQlwxTuyqaHQf=i{bruuk&%}Wv6wA?>9YHM6<&a zJ&v7;EojCG@ZB;v;d4T5g}#Xu>+ptbZ}frB6V`jlg;ujJxC)vY z7Ns4Yx1HAKI*oGx@^LK1=rj2@nyhLVjyE}`yO7#xTcpvZbZw+imn>*+OT%vaKKkE{ zk#@mxpq5tNgNqA})o7s?Wb`6{qq{SJ5OhGi$+^^3GNz{)Eo#x^G2xM?zhdI;l1B?< zGpe8FDi~=Ut>6itV)c)>jwTJG8qu6uFOk~mB#88yHJhL#nuZM81ziv$*2tUF4U0KH zFxLkm0ewzy=MiQ-LnemAaC#+J!q5|w=XGpO8M6H;P`!QEtHBB!i#NHfP80;S>n6=BU8hBOmA0P@?FTS$}#pALt>Bm#T zH4`pG>pT>!eg;<*!FVUvTf&sJTACgVo{SQzPi9R-6Xe}PW5EXy>LRyS9bAOvu83p< z`vStMHTkVi(u%A2xR~M#)_#$*do7M|%-PP(wUyv;3hy_02O9`JpfH(^+Eykn1uprk z>y>zuYl8s~P6qnwrTu0#G%`S_N9pFw-FN*xIs}BCJrphX|E^{cvxM3#rOsx7mBMdH z6O`J`QW^+O`8zgY>ELErY3S(+F^QLhToERBn%v}O>jw3fB>@snUiHqYUOP%JM)K0y zcl%=&^o0-vG9_D-j1IQ?`p7z=BXx#)2uZFgN~21CZ~c+BuX=4?_437{Op~J|hoWZ? zB-65&%CyhI>1K4&8w;94u}dqJ#{FE@d(pX&rLmoR3(w>y?LY_P-(&Ebzz=UPMQ4HD z>$Kso1DppLY>6epTi~Yd0=z!xz;|$7gh%l&MJ(QSU5PJsBDnM&{06|I_@98E>El|V z1K+`a)`bt|L+oL|hh1FJ>+l#`PJ{FB9N>?{H=6IKfKMG;4V+m>bR2#SCqm-y!8eLe zpdae=b;5CLZ=#96X!cp-i zH~Cr7)~PO?a@Slq>sO#L$}WNnLN4oxGC&$5vXnvvznCC$Y5AT~h#}p@i3!)gkh1t- ziP*Bhnyc>L{C-zte;~M1(FVh9p1&@*?8~f%AE9hQ6tc_M;j7D<>xTK5EUzC(GP29C z*$unhp#P!0Zj)Y|`F^~~BZ@{AS35Gy%ehdPmVi+jL{=$4w3dy`ovlU|S37c^ku}uh z5jl^@V)p?)7nz$JjV!KqAc+S^A+b9APlC`%4A&hux*SWIK9w?C_AG zBdfEhOU64sBiU(x@!Zi-av0``^d_u(m?D~`h-Q%;k){bg*>_y0F@>%!T^;FCG3SF6 z!u>!r1>3Lo>;qkLastMP>W%bkFUo*bs=eq$Q?Q+j6uLUsL^{_X1%ng}Y5`-(u4S|q z!kxrCMuXAWme(e}tTBiH^L|Lq&IoAWLt;Aq z4bE@ZB3bH2<`=SW{MeX{uOc2EuT!Rf7VFQ8K#NCuH-l~zQ~COFAOd8NU31}dEmf#j z6Y1rNQU-qkDB_b@@}|K{#TQoNano5$&U{C#UKG8VPb(W2W|vR^ZS{EbHAoChH!Y_1 zf{o_pgoQwi!*X~Z2d-~cgeY#}3CvGJL6zN4iV146#9(>4ta(|A1B=wZmGNqDDZm^Z zd>|-&Mev2h2rJX(v)IlDH)g9QK>rG!-!qrOG8=EcQratIF*Lwa&ZYd~+~4DnTTJ<@ z=l%u`ZalTEcD$S3V#2IOJMGvn9t|_AmrlTTG-i4jh+#gDl@mUb-hm|;IgvACR-0nu zuhA_&EbR2oogo2X=D6!FOpIAf%oJc_Q$(Jjq`0grY~!Pd4-+|<+81jZxIUo#_4Jc4 z7T7Qt3;gIXa~QT0^lYT7AQ>wUuGfUMq+9Y{smR~3B6w>xfz7S#`Fdo{D8o|GeMq%D zZVKFzO2rRfox`SLCyj7`pezo@JjkIzHJWsN1JXUY2X3>ENm08}g7GhZO;t@V_*~)4 z8mv*}V3&*}nEvi@>s)_uk-`Q{(ZL3TKw<05Y6lw%u29&p>2)x0CzQnc)m+Hs@T+;t zvjC&S*s6{DK;-;TM|sveQt&C$1i+IT}RiGfrI#=+Zm?VDuwO zq`WARg8sV*U9mxqu4Kx7HU-l1Bh2e8h{)~u3K97b91$Y&R(i3>un9;vK+P@P08s(2 z!%`U2vow!QIeZ`@4LCv~(xXHqOCoZr1hk0=c0C~x;ey*iM0z@jNG`MWq9_ef8(q_9 z9-4-P9lA0N34>P}0a|=_QbfUe8YCfL9Th+!ieB!pev;i*SRDI(M(q$s}^^=Q+HDD^0;NIe|4}Z^2UxHB_@2mX?XQ>>%_uz~6l+6<5+i>O- zH|5)UFu}w_fjB&N1icq8W#VAPF5b8uk+F+te3jpHym1G=xcU4VzuB}1#7z{9r!JF$ z)YGbrpG%8$90JnnGFb*>{k-C-AYFNJ7MxXRFSfDE3ycqYUlLgb>;xwT9GehukQ6qq za<}alOFmSJ;bPSWPdThDdA}c|VPNibY@bv9;N0=} zngK@^cZ|_HXi3Dt(g`K{`y7H9z!Op3-6qVy^2D$i)||~Lzc%*)>}4^csu)?rCI6^7T5>G;<8v3{ zi&0jeJDv#j_UNLwW=B^Yql^ANQ(rbUY~f|F)J;tW%MOZev|$dbYq$*W{2I{ESpq7*^GG;-0kwbdx&6q#sw#d_Fx@4vfm}$2~ zp01b)yZB7IMQ(b))Evw$a?|Tf-ND=zd3xCOstXA>%G1~=ZwR4oshh?|d3^|V8|CS0 z(P(8+u5hC~ZKuxRI$;9ye!@&7=by~gOgk{b5VLnnrVg~ase_z+ltx&`&CpcAl<+m6 z`NMTkN`Kgl18JOLCtgFOqs%%WmGNBzq$-d&H;)3+Q4Blr93s`=t(h9g*2xENzICXy z*Uy(?s{CpF{L}g=^wt00xPIckh@7A{)l{ZJ+rJ_e-EgS&bK@Uu{oGXYM-Q`pj$&?( zVG?gHVI92%>mb)p3SD5`9G^R#3*&gWZo*I>Hf!~{34-l%YR1F$b9~v<-TFCh>Ibc# zfPv5mR@eHuxnunt>smkS2}FvlpOS)GKQ|tt{8@jjo?s*%j8$O^0bX)4hJO zEQeV?lk!+@i+@0%6smtk6vYc*~<#cnnQVyN?Y*{Joa$1w+bWE1hF}HqVIepZ@ z>!%JCT|dWh{(RY=*3UnkKY3*RUwi)i!>*rWcKO5x*Bw7WPK7~XNx+0Veok~AKPTMr zb3*sf8%yCKcctU!CM=Vi=ibfr5T{goNy6i&T~|`Lrv{8$A*bA`IknDgwu61jt(sFj zbxNbTkZ{$U!l~1(np5HR%oI+YZq=Nsx>a)ur%tzOPKBq=DLZ3UST$Y4x#MJV{sKL9 zvMejCn)X16!{OHtUM5FnnH-HQlka#i%j6YA+Klygv)PRG_b~^rnp#R+`9KFnCN>KJ zqL*XF#H+czcrTx;L5)_f#k;ro;NFkWt(T`~ku}BJ+(euI1wkJHlVn)1Fsz^eoy^=s zBfhF(W1LfZRv-=EA_SUY?e<5v%CziNCpd;iB#sA z3t`i_C#&pONSLSU1{bKQb;?3@w@KNVdm(%d(VyjVX@6$&DZFD{hERvpd%10Ego}0kO&e%`P$EUO;D(x*9rZFLQn04{!*dGbwdA3q3b)Lzfx#E_&B-E zYcW5rz_HTcZqt||IP;tFCTmT75G?JYF<6eKbK-y>*0a`NT0#WOWM{tX@&DP z$N0M*%{y6i5-ZvHJHgk!JH;F8D{MC(1vV#c3X{B4q`)4`Kd6kRDQs)t>efP`*xrS! zJRhG;QOq6qJMf4Oi!`$13~%f~`DJ$axSIv^S z)-5K20YCxPZ^bL1E+7`!4Jg9~vjJs*pwh;WrY?|Lg z??!lW(O}lke}TJ+5qL+;h#8(gmfj(Fhs=-}oWGqbe?Pqarr(>t2tHcaAi+3@3}`76 z@!CIuoI#xEXj{|rLZoqBTO1VIJ_#9*opdxPFaCW1T?^^1*pKmDkFS$wz5v5~D`0&8 zM8Zw@Aan$t3I8iF^%A9-5}qlq!2>bu)05MDuN=TJ z(U&tS#@;QfUa0qJsj(vRbpedth*+ByI==^cUHI}GO8uXOPhtXMeHYT(dPD0KEaAz5OW10ZCR%u`G{@-4;}vef}RXyW{EFuaW-U zkEHeBwuuER%`18y?-OCObXm7*(D+yhj=h~ykm-IW7@3wciC%tVU%!efu40Kqwvc!v zUSF?DK(LG2arw_FZ{UP0FT&Dvhl-e4Z!e5zQW9R~iA+n-hqdT-0;nX0GpF%=lzBXE zBbOJ{kMo2VI~IJnKgZlwHU#w9i4uSUzI>yO6<->Q2Z^Y}m21Su6P{>yqn|E9g9Jot zi>bx(LNCx>kL=e@Rj5Z4g`saNWK8%43J| ze9FGS$43$~V4tGQIa!D2nJ!7t_fWR*hsaooda>x75O$5%^23$Jy@_@J&z76LgG4 zIZc9~I2Qk5{q$Rp0l@g9eqZa+boGt;Yg><{Yi;XP!5uF`aOWrJdz(hO`7iVkn<4ko z!$j!$F+CbB-TYU2m=yCr9bdjx7%HYmoAji05zj6yNSJ%Fke1B^w)>DXl5GXvY21b} z1VPsNDU8cHLReR;I@)AaL(BzR^U$v7ZI9Rc1l*~G>-o}N4Xn3Eobqvra0%4-ZI~oLe<{lzl zvgty9QM}7`&9)lCl)IJI-C0)8#tK7XQer#`38OT=2eN3|y}RJ&@6J<@s&+BScMN4d z9&-IK^=y0REIx{OOuG3W7)~a;Jb@7;bvHfSgZG31d@50|bO+jkxz>dH9_N!i@=&_PUTau^zAerZyJ`)d~2|`=z!$x@=dYIJ_#5Xc} zEcUaVyR7Sb^9wZ$=iEi($Hp%SLwAq=h8iJZM(PS|RiM}sW}1$IG~Pw6H(9=2SBW&1BpAM`HMs+V66Cn3y06>=G$_i} zHI6o~n7DM82A6Z4BiGE0Rfx8~Xt!AnroK z8z@m(c`lK5j*NALO|S}cJK6;M5xb)a-edt`6WjoR+EX}O8^n=rM;kEO!EM0Q4{ie% zV#PMNgB^E}S*dXTGjlU$+&`mE6LXc9&=W5yDzl|7ZTlOuK z=!iaSXzIqMjUis?2GO4#Z!*rzz2h`QRx3!`316`F4yKiAy`9OhpFWQ?u0}s4nrVP? ziDm|$e4?4fr;upo@PXDRDiHFa2#MN&#uAO~pr_btLZTM?cII15UOU-`szFywk(pUw z8K;PMBY08_Z!+s*av5^IS?<-6yS#-A9KM&y?&5L)WA|7fvwpfhn}PKLygLA+5byoK zPR$@3tW_doHWD{e+VWkz=7acm+xWqj&(jph4>1;1*W<}J&|V{BkPV4S6lzeG?yOEt|ZenrC{ii;(3RD1vZZMxcs@PgDbtfX-|vUY z7ewkSFK$6(3WYD$Q|OH>-W(wMT7iS-fWZU!T6sdS_aPY4{=mL>qP5N!o~Rixl~x*U z14q8a=>UP|7Uu&>UEl!j+hwu~CJ$!;cdQ%-~es%NVm?nXG)x%|U|~-QUfaUgUwZpeSM8a=@(H zb}PCOueI6pa|c|PzMUOhcD{GAgR|1t@1ip&&YR&xx!F15e>eR(`opfFB)sdIhtbWh zIe`v#4Yl#woDbJS=$jd2gm>K1HE`$XKH{Ev5UstQt=J=N7iqPhcD33+)LT;F_prhk zQ9a_jg+7cZY;Z9{(5gx77(og^kqR6t=**KilzQqIOg)SI?ZRb85%ddxb}iuX%<5g! zcoC0|v#ruD!}M)or%itZJN8BH+eq?;C|2ZhZEE_;DHZ5+&CS} zB|cBUhn&RW#uF8QcZvY4H&w-`T8s?<2u8J`7#qQSTn}2d9<*!(32ZPM;oAt`2JpDy zhi`+1f$*3SeZtVX8KX%lHZ1=nNWfCHKG1Vn(^IiL=;^C43KGpCrU}-H)%bv{a&^F3 z!Nq|uQPJT_1WD|nZH3g#$2Am*l@G$lT;yejw!!48aa(gdjFx(l6ia>Z%%M-Jh{p?k zQZ-y_ti^L*?A7{F?g||PO4ln-CS_+)VB)JMQ-O9nzW9b#y)m<9at-FYf;1#w?{$#8 z!(D4iXqFP1Wi4(xaJ2$o1->$FJO+_ku3i8mA%}f2%uQe*gWmkq3~~uG>!35QJ2W%> zQ!I4hD%XQy;d^r@avJsEB#763em3K$F~`jyqpxG)xQXH`dsXDl7$9qsS(@t|?yMFn zrhrk0di$tcx_+|pQIvkHmqU!dL<(V2Tk#iTO_xng01M{b7_1oP`_V@>2fwn2bke2i z4>RpWJcikYCPljfnh#SSc#x_09y)c@RT3<*g4lDXyDEty{7~wn2q@=D2)Y3PXY?ar z%h_r3dk-4y&@ogvXgF{=XtI$(GoiZ#fY;z~p#d6>q2nNV7vuLEI|Af0~k8krzpE~^5n6&-WRevkUSz`vPdFYv?XJFqg2pGQ)$G&HSsgE68qOl0al;u$3 zo`W&5y~h7TV@ka2@Cle)zalp7?d7&qpp1CX2KTA`L8#0r$h&UL$>i=W!P2l9Js{HZFtE%^E z?|xm~Rn^@|IuH_yuIlcjo23#1&6cn;f~egcArJ%%i59+&sFW3h&Zsj(fM|5E2La>4 zj599dz9A~(f(sz-BFN%~yR$kv!vFg{_ultjE!`kG^ZWcipUEd(@4a)+z4zR6&pmfL z_gst>6}}kvgnV4V&x;%Lued<}!Ap1}m7}nDO*S-HR$eI2k79rMHK>yoSSbwi4*lw4 z`N$mf0FqAmys1C0jWgZ(lEN|A{{;eBfn4*aaRmQy!dPC8;s$~OkH9FOTAqb`a0jOO ze%3_1!JV4s`_wx(UYZ{u-*C6_Mzvy)i`Q41e%pz>6f%6h@Ij(TMMmrJc?0tcs}Y8A z=ZAJAl8wgmcYN5FGaps6p&bA?EsfVVAZqhN2!`tf%pmwSh-t&e{UY?|9bAor3vyH- zmtF>EH={YpzGD^7n%aL>f-D!2)sI4|y>TQTOgSCpXV(Zk@T|rMkz4&};L7;A$xjWG zVRj-H$Lcoj7MHYCh|G6ofS^k-VxVNw|8|qDp@6>JLEcxO~Dh}M1!0GYDdP{NQYqjS~FH${2 z@lPp=sHhaR9|Ol2jrUM*EEH}iUT(Ax@<&UGVYoYkSBz6*vm?{nw(p#iWoi`z0nkLT z1w6Tr)FrlwVhRw`PMvpd<(4f)hJIW^QbA8!wU6P)l=YBMSyQ^Km*DX5PAn(vFonFY z_8LUd9K|BIVlpOwusXuH9tu&ao3+P#2=ync4NruHN)Qy@Zk+#$o9_KmU8w@V)v@AAnLzUb!xT?~j9;QB6xPASDeMQcuXy2{6#$KS+t(k9HZJMf`ES8IQ3M!$H(hqsnxM_bYPXQ9T+N3gFzTc;4!PXNT~S7O!Q`~)dFFvxAQ7t&C^+vVaIFH`f zZW5_-vO-pvqN@#nt16X zpu|NJkE~K9#E9H#kUKmb#pks__OwRcrlD&IcCr$Q?55aUUWdFhM71v$W-fRgM|j7V9w1bcFkN~sGo?c!~jy^ zFzbRMrhXCw5-N*XO`v)}`(a{FZt}+BwQgLO(piJ={c?-qHdk(LkYClYUMlALB?KSyaC+Iv2!%G&uqTUqS_zQ1B|s6ov6abzTDcxjeQPK zHFU`PbHT;NJ_l+Vy4?GuK(V*Ue6HbMGj5vpz9rBREa=PF@FT!vU^PbZDC-A%H4g}5Lj<+Uk{&6++ zCXPhk;G-^u^(-GN9G83AUj~0L9EC9yJPBi^YWWQ2|9mOUq~aU@389Ksdjd|YNHnY> zj#~gY7pvli=zj(!LssLnc&xnmUTk3#0Co|;PoY$Rh2>(l026M8VdBJxLc}y2hSj_S zL%|p+T97;0=>(cQvuB!ouD7Zdu~!$KLHTKB$cbdV%DQrBa22Z zLl*pJEmwnR)`zNE8AzM2BOz$#w`Jnv47_#l{p*{e5y}oZi{Y@fT%Jw$HvZxM|(Nvv^ zg6okht@%?~N?Jc*=|kP;ia9CguC8}3;_i7lNLMp$oyx%$sHBJU4Zzy77e5R2jYwN! zNzhtbWrm#2FgaDxCYa^?S%grXma!&ca|!*8NCMjRL#>}5&C8kaY~m9 z$G%7*EMik-mM!gl*c3VAeb>BZz2BHu+?2(%Q9i&r z>=rP#SOC_XyT_<37I64t0p-O4)-M(SVYf%SmJq6bk&q)8qHMojiZ=9m%IWE*KDC@q zQ$!(vo1IXd9srWmsaptq-tO_+JHn<#G0#BfJ#(TR$EoAZI3Qt35!m7a2=hgU!1DtgO)4hC}PmM!6-} zYLN4>g^f@amA<^E5o?{~R6bL7KNoGF=e3YmU4a&;KZmW7br(Bdrav=ey!&7f4R5-RG(PgBpqsY8}rD(qbbpXsuj zW2V4dz0zxmq#8?qhAP=cl4U#}Bx_wN=A2&6Q6qK}C)nIELwd?7W&hoHz5#WEyX$3j zGn>@>BrnlR;ly)j(Gi|;`>$89S5~ChN8>Th?5VKGBUybF>>=;&ALn1`lkZ#x*oSD4H~7a?TbNJ!8w4v|d;*rLi3U zQL!R%mD}g(^RXx|i^c%(g4yZK0E{XwROhMSI24eMJubb9+H|%WD^X^vF@j%g=yG5Y zKQOb7v#sEcSQUXF%96B|> zFGEKr?{m<=w4A}p2kF27pWs9?*kr#+8_qnS`j{#(*=wqy1onFPVfSM_M}`c)#QgY+ zfIwl@jzRW1Jy(q`cu%KO`#owFxAmb$3WYi}w<-#U_#3{2w!hJJX-S z7q}S+cJ**2;4t?mgS>BHV62XQdjEk}nZ(y|ZbRE!#UnexB1|Z-hkw@pQtfjE?Q5WI z^z>+P!feAvn*!SC9~F^L(8jfdLm?!qL1Q6RfDM<59h=SK1V`=jKygMMn>t5a??k=d z6)`dR47yUPXo?Gz|D#bXBqZ$F`k{c=F|FdA#fkC@(HL;P15=>X)bpZpZ=3`sQngni z)ufY{U7zCbY}UzCwl9@|otPS6CQdjLL~=WF#w8Vs{`hRxry_+L$Ogt;`Mv^Ftw|3Y ze>nAqJwCQnwjHU z;m6RPZ%{6d!`ccKcsbu9qFTo&p-kAFfn6YBpll8pn1sdZSAk<{b;QBin;Of;Bd-Sj zjldt=2i-V~1<|2>HIC4ng16yjtSvi5@6}uQEP0QiVH|XzofHa=wRk80EkKE)_saYD zEP1K#@;T@|em|c$U!_oSJ)QT4rvsEMdie+VEP4CoIq04IJ$@gw_ez;qxcgZc@{lyj z9JBEdKq=cd&*L~7l4>DNVz`|1MD$XOL;&ce15o>TCM^(*-5!;f!viU>4 zYPQ9c>E-tbiZ#Ua@<9o1KEOx3`6E7XyXF3up|RWwWx$K5e+;0OA26zuJ}6k zc6aYo6mQhKP=3$J@jajxvp(h&Wph&Vdpq(8v|%apwL1oJfq*fih>fMJYj?;zq1TFP zR-o&OaMX>=Q?18CCA)~+M~sbFDvFgD1ySv7dZRhWeXFb>;ZvWoJYDOx4{ zGFyHp%2~pJ{+nV&11|b_Dwu&KlSr4q8b>WlXT59ndDq_g@MEjLIz49+*tPohUhjd& zKlku7Y|%AAyHO6He^MZ@K2fkt4s} zk{HOszX!V|=9kBu!vt19S?M|Rr#$|}@3bU#^L3LCTlc)bTVk}{rSQOd_g%5UiC#+t zxQdlLN>3Fl^-}M8Ig+E0zvhU8bIa#|Kqd2Ss2`+^sDwf-p%>_w3q>lc{4^CJ*J79^ z$17;sc3AGX?r}JT=S^t$vBAk=%&Oml)F%SEbY^Tgv4!f3tOnbSo@+aJU5E8H0dc6O z=5p))EQFPzhXCYDn$1DX{1uMOko8A#X7GFi*X_%sTIw3~2nx&!(glIHTjG>m!=-wB9Mz~@>wxh#i{muPQ9r| zw+O7%HTfQ~wQ_sbd63DDXJcDbXD1-3Spc?@kocJ zQ%|Po*90>Qp*Fyf&kzPic~XP`Lq05>UtF-=gmb`6CJwkM zcws1AJbRN0DIf$8AML=54k#f2=-~iPRyd=hQ7liu!IRRuo6%UMXru#qB2<^e05X^z z`IHiaH;U+7ZW5h2{VP(X)aIt|<6SE&z5JMuhqM&d zZ){w)gAuk`=YvMW6pqH-Ml+I9u93-FD-|7EgX_dZwAZ@WtO2rG8lf-7IAzCmWb#d} z;Z@f+51!&~I-Xh_Lp|UOvg_i?+k|t2vXN#z*@<;Og|&i20gAakc;fCTfB`G@0wV6_ zW!g1(V8Q%T>CgWM{lVEmf6$}*19YlFPKeV+bGP;fxPC3vAK>CdRSo}B{lU;U`U4(0 z(8`Wd&JKf5QTG7?L8@I4LDU>iQv?VE8RZ)woBp6*^#bBwhXM}&I(&4Cp!$P?(mrs~ zqYpHzN4E&7KhOuEOKb9jsy|SVZV@QMYw|;?7Z_TTA69Sb;cILv1>GNfy<2}E%Yd$r z&NA!%K+qKr_#0{xc^{qZ(*1#;EA|OrgBC=Oc2A{0==mS)4=AG0e$a_Qhkz@#^rvpz zJ!sr{Yy`3Rs_hGke1=ibC9udL6!{FJq~!vyT!J0s1dS-|22Z}z*-Rs|T91inmZkU@r^r5dDt=;`}Q<=>1Auw(!FqFvpgDsn%j*5ts((xgXPs;#TQG z^VfL8uKplaV)<%`&nz1*&iExe_0jc@$a+L9@*T|hZ-y+b6!SnXy1YadS708+=aS2yC7#wabVx&C@5VA~=yDB(c_2BUq2li+g~LJG6(W`ou@!d^0}pS38Dgi& z68~-r-;ED?wb*Oq~5Tqik_7OH7US#WK5P%L_1kVsC+(djsY-n|Nh zVLU6rIbcGDz3;1#XxNRfqASv^*lcAMa9cT9fG_Llo`ZMjfbrGs_sG@2*{ z>DnFBzs6Fv?X3c)B{eR|suXRSm3x;(I1 z$Eh7{9;eWj)tcokke}#$18JvFHYQqsJw6nOWHN&gD1(tbGZT==VKjn8O0iR2AvGFy zJtYaMI)j<&M__Rz{->CxhKBr4F1(Z50J0m4c~(B=?18^)WhDjk zr=u8=24QOxb!8gEM(CrR9- zlq4kC7(Za2Jq&EWXTVrT zc(U;n3{6H~RYo70+DkDLM(kyMOQjFZ>}6$#8ak^34V|@thR)g;WrH9kV*B`3uJ(b? zQOcC^5r8$o+fZTA0yjGBG@jVourp3Jk>~mS;~bccFrj@kK--Q-9|1MB;mE|-+Qp`#;*YlBV!gWFn zMj}mkO_03{JM!UZB=Rz zxkLuPA~#qohi48Lm+&Pwknb7D_bR&}`r?;c5k8CYfce8jIs#t}5#{V2$F$-1Fehgp z^L>hbw@;N#mdpsemfUdr#C6oc(0rE{5LEHkW+XI@bBvtuDf;!ILq6)}WzoTP^8K|h zvbFT1G`QebO%zq48EtJc17XPbCg3(HUEEfj8p5!? zhYl*?@ka<=aC`aL3oDgOaogBYTpX%Qbio3`;!%5&lV6hf16`01vmaT&6^xrWu8M{f z1>OD{M(uL28M6q*VOoUoB4$d6JD@rIb?P_(uM-Xr)UT$oo8~ZtaTtz>3gB?z2{?Qb zcFgjHs9P``E-DTeg~J0O4i5wz9%$ik@yT!)w?AtR3&DoNuz(43xDyKqoj|D^goHTU ziG=VXI9!BVB*Wo>fWtTiGq&06y+vjJ0%{@hcCg+1wDiR{fU%;0l`jyCa8s#JH;f_F z_K>~mTF3htqNzUANr=rVV801qR^!!3F(KN@1m_;|qnKc-3Kc999Ic>U|0FtU8jaUK zh3GsNEsCa2b7Aw&gy({wN+^g5bZ#_N%V7HOF%)^kg;;%h=B3SK=_g8VKT?@ktd2 z$T2_ZcyWCJt}0?o_Fc=qeH+oSR&YxNjyD9V5{3(4`Y2ZEmlQcuLA+UsmrKpwAn|>A zTB2#P?gUxIso{8OeKANtWy}PScU!Ta#x$>E16}h%{vLXzy?vTdZ{QCIWBIRUhij-MHmvGQ<6)JCj%xA*KUOi>aDLCsUp%e((j^=kz;2}3QWm0Q};?-+dg^qj(K+ zAlHZkxt5reqY$dF3N2ysZlQA!*-^Y(vJ(Mf<1sX0aI`KLPm-i}EfYC~+#nJ%mv+PS zlMo_wg+h;?$rq4(iL#UTql=Wrl!%%Fk1(;Yv|MRZvXa0uwXKVJOeZGgI0-Q(0lpM> zrREM$vqp;jL>cP3K)Gv3I850y^CFQg&RweR2q~1@A zTH~qci~a_qh5pB)I65%o9fnSaTKYrO(vZu0e)iGZPJG|a+ZN6*PFFv6?KGeKCLF<$ z53jm!)3&vLwdta%TW-7b;A=kdz}(O@pZq4A;U8VGPZZTXuZC-<^81nUUeX8`&ITK(u_nQoBM@pu)j7D$!~i|?K?QRe`4@wan|Q&uF>4Sf zN*}(b;$@7NzY(Ih$D0JUflebxu%k!|D+0{8vJ4AY4F*M#IDjOhvMhd+HxAy-KZc!- ztJbQdqx+#3tjMXL=2qwh1B9Ie_I<>fhEr!O(8=9m6|C_{fpwM#M(2Oe#MG~M*BwZ` zAI!Vz4@^^-rzu`C%~c@e??C6CGf|@Q7#yRi>E+3wHDy;VdvwoP2pN4vbj+T=layFv zPIRxpBo2pS_ag7x;-eL2Bge`DHK;+souaNGxZ+q8~!Xg;La4Eb>#-&O2CNw3&SY1~a+1 zcS#QhV{eC8rtFQc!T1~7wi(O&9W0;f+ki0&lpkG(k6VEPXQN)6_!43@FdkB82E_kd z4!(!BBg$^jIh;-;9eAc%sP92MWkx$Vd4B=ZS1eidp{qKOC>$8a)dv`6V7!VI$-&7- zUEF36${VimvH27v86-Fcw5%bdk`7Tt+glE4L&VVuak5g*VJ!qsx-K;oVj4+Dxza#Eu_-d6z(c;vCGerPd@CXsy&u#D{ftkgex1FOQ9u` zAcHYM7PurkdErkM z#CmljLaucDM-a*Fp}}*$w=x93q`lXpj@;g$%(68FA900=hiZRK?5y|qD@?k(AWmuT zD`L5iCO#r@%Bf;1V3DgWp8{CbcRY>$oJJgLlEZdmoxmm z{0PK5-;eA0M1uO3P@92XYQa#4~V;J0KE-%hrmJpnkV_!0lHOR4|h>UxEsh zpuT{cB0+s&3Br1Tp8+MPFDN~k(HHQ>+=&>Sp5oLRoGI1*Rf<)Tji}u(wdm~y-`!GZ+9=!yLWtDMynBG9 z-J%6)!^1YOT$8jhfs*uF(w_YOVrly=2eaBBZMVd7<4(m*aW*4azkE3o{s-#4ph{tH zU73syO!Z)H>T8%(C5Pc@!` zB&%uf6(C*}8Ax30Yn}(q%#QzDK8N=F86R&j2CmWO)A7yit8@5_HHn4sz%z)2GH~7q z)f@RolqEWHg2XQWBNS}?9r%fQYkq)sM_nE(L)F{7Fsg(hNE**UJTrFvy;$+i=3-6) zGs#98uu^pXCA57g;@z_0qcJ+a8sDSh2angIV4W8G3-3p~lr{e)h|T=35Ds(W`oH1n z)c+H|Yaht0^^QX3sv9q43zk^I7t4l;@y#`)~Pu(B3No z49oXtLqvV&y7GZ#Vj73zenMsW{$f-q8UVw1zMoI5+#E?P()rxQ>HPO;(pB^xo8=SN zpfGdnEwYEa;Wg57QgRYVx9#ugMJo2TlXodQ;6Eh2^-quIOI&ZPwm_=uE3hZ-7wo4vtmA zv+DJ*#Dc3)RazNF(Rz}5(i0C>UP}6^t}PLG4ilzz5+R`@Tm<%L=j*uh zgN~J|9ENO?*H?>(oVp(Ofndy-($Ozrbl=DPH??65Jdl3cXJ|r=J^nrzdmIH874m6z z&zTAwio%wsg6;89u*}UYxY_-%1XSb9EWFvBI-V; zHIaNRGI*9ycxNPeuX)Gmf-y_rY~K(;v2(%Mstj9nw4+6b8}aw_-fomSLE`P1fr9E9 zm#w#Z+_pS1lxWu?I-ZOnmdvv)ZYWO^cU&9@?Vc{0lI0XkH+xl8?`5O|yOvl8M$KVBJ%e@K_N3 z1nJxKMM|40H=0TKiw7=XtME60zse@>l~7)obCL2?}+rpp2 z-dFIcZjJPQfLCQ)poXUn7Sb*_+?z-_%C$`$Cr)j3@1m7O}Gc*I0qkMI!5_`tYD2LolT<9Dd8HqLc#2L?G zk|(-$HGhsz^e{H;Nz)a0&2|n*0C%txg~@25l3+{No4Dk{n`jM=e(k>6edt1PvvGUH zp9Gb-6Wh6lq?eMUrwK`*!htr$OnX=Ps}Sxzmm%_2bwX=aNP{}oNTdsnzO$7-Q&D>W z{{}|o3{N2u37^}#ctYLr^Vy*EZnm6*2$g&Alh0zVuy>j+%mcRRtY*X!xtpDnt@|QI z77FFNh&QX~$fw}OTd^^=bQ-&wzaX#E)%C6X-sz_1b}Ys3$_q>c2nhv){tNI*x!@^! zSRBA&hCq5*Kh{myH!PN1 zB?ICcdFPxYCg#tZ`A(bZ3r80Sz1`Y#31V*BN$}>avjf&h`eI3Cn`$Dmrpo>i)kGWk zSCJ?62CBj^FRCgg$(xj2$SiNnP*}p$9t&H|@?GweQ2#T}PwCSUtj00u0RvSZ>N{|u z6Bra5E8##-xZ2^*ft56a$wb`(2V+7(8einF`ohNXddnt^_0SF@-& zcDMyiQ!I2@xFKgs36=>Gnd1F8N@!-1UDSt9Xu58UG)86kf08;Zl)CxPNmFrI{-wkmc z7Q0WL4?pH{nLq1`u`z+(CV)X5L8e&;6GPrU0gKV?%9ZuUkw)t+3Akh{bV~vTj88{f zR97hU=npiU6mC<%ZE6yBE^v1_71DMU7hb2mi&uj5X99Ya74`PvLtUxxe3lf) z`k+LyO!fl#qLrTAOb`^Sui|@B-s5s=y9r5;Sw&qQ<^8_7OBgoHHit9%;%XoB?*YFJ zz79MW{nB8DJm3$@HOZmDwCAkG3jv?GeEjK_V5ncg^L@J$qa_k*1={&M<)Fb*VmpA65Y?w1`ECw8 z@;$B(AMqV4jx<2C8p6p28ChQs)+s=J4PJM(1RhSv*0N|=^ja-plR?C_9TBm35Q>Nk zU6_3%hs>*RS>}ISy8}y+xUNru`kO?6?o{Lv%Th!JCsh?t5}-lZ*jHdgA1V#SooyF$ zPS`}Z1+1haCM*C%3mj0BomFTyK0ybFh^wtw0$~5nURl+Ps&`eZ=)Pwhm@q_VaGj(v zElrN?)P>@CPh>3gW$->XBf|#RH)JX&gbHrstR}?~2B(SwdWf3LVe!GiEq6BV!E|st zBi2oESIoUP@(d?M1+;&x0y`y1eyOasv}DDR=EYAjzYiJ~2v_3b!d9%5bFt|Mu-bvH zLgp%HEP9=dOs}Iag=&-v06Rp`jsOT1&0qsUWSb6CmsKLN>I3s@eddI={zH*v$|W^5n$qxLm(7 zZ*MIBPlvkpZ@?*!P^0>wuVUvwf#^%}a?r(p#s|oYk(BPwUcy(oiU;e*I97{~wMfY- z!zTicUX{yuG~b4?PAlZ?0Yp&N!vEr}gsp5|i9|-fVKtaoT`N~fxWR0(d}YZ?(Y%!K z(%*b3V?8U1>r$ho^~S+ZL()326^hGNhB5ZAG17vD!XCwBEapcE-LP^MQhdT2R*1wK zR`^uL&RU}@01tBNbLopbjv~n1Hb)TxVzPmw2p`L8$Sd>E zJ^;1VH62Cx5%nuG29`=nJ~rKeF3QgyCz{Cb*g~7R?!=G|P-!dc=-?EviX*1G*mD~^ zAIgKE#6ndkm{=@9Z3*3yV2pkv{0~o-B7%$upZ|V&R?`O z_(a|^uc{Yfuqtq2fjuvaUR>Y>-+U`A&;dt?<5~5UsolxOB@%BnUIfg`ii40aNZ47F#@?%$*Ap zZ1e++5!y%p9R2Cq0O?la7@StJ=WId8b_=kf?%bTVc;~1L(m<)X39sOcqCuzA?$pd7V{W1C5Vku+IMR5vMrSn;4mz zN_!WeZ&K46?@|9%I_uITeW6Dq7a?8D4&ZOCa*RqO?ToCR8mKt_S89BPGm@S9Wu@SZ zB*%Hua6ALRxZ{kh!Y)6o#Xf7OpAAv3nv@y#slL{V9Eto;&*I-r=TO0oKdCuqW@H_% zx07;!gjG23f{aqkGS2MpvvH$DOFzTwyJS58lO^fDm&=@u{ddnY-G8$&clO`x2L1kf zgW#h7mY(k@u-x?2P~-G(#y zLxT$1ff^?&U!kxxsy07`yJd6j_D7M=$I<*TpwEsMG zf3Qlb(cUG>gGA!)jr*1AQb!Dpg}5xX*|-b14@0$KXfYPw^?@rCkfrSa3c)_6jxMD6 zz#O9&U{r?dBVpWCjTY5~?|2)6Tb+Co2XK)80`}>|17+w_oKW&^aGxMxxxJW?OOh`A zPKSdQu&0>ZhxTk8#OC4*w;0{tNo@G}w710XWBhQ?0=yZR_&`Q)Gxit>rhf-k@;>Kr)|Jc#gDjaCAbzK~lUzmt?4;i`+C*UtFaCt5dt&oLS|78AN&) zwomk;2P8QY4()TZ;ZR%@!<5kV&LJsj+gRQ2cYAjg-F~h^_~{I|ZU?O3_I1FDZoUJC zQ~Sb1%(3k}#=i0~!w(Fgsc0K{>^j1hr2aBYMt0?)_gkP%fi8E4{e{J?J7A5sJp2i5 z9~PLkh5QK4rVY*Ra+mAq`+_i!ZfnDSvaE1-ep|E_MZ3V4v;Zc5VfBtZHc!y$7 zextkGygtocl|Qz2LmI++X#Tj`UcMaX9y&$a%fVT+;^7^0=U3T^YcRJQL|AUlPY88f zbB}db0h?5#^(HG#@4rfD{s{L7yv*b) z?pospSoF=|Ks~&HZYeeybITP+f8?v~sD8x*gUHwn7K27BJ#E1JIMO{_d~qZG5fUHc zk63=pt!OwN=o2JtlpUFSz2)6 zvud*l?iMY$TR_eh#G>GC#`|W3LvX2a3tHhmk<4nZ=l>_OPeo=+9Wq;5L}q<>H<>Lx z37PGR(;~B{6!ND+W>0ZXZI#(mAsD9uxRlJ6+*7*BY+J4(v!{v7o(`EkjWT=s(lQIj zX{U+Io~~u~bdYm8Vo_#K!~1Cnhs;9HK3S2~-uC}bWS@%24t9v_;36XH!@G&>;FA#9 zt~f0sdqyFDCPelO_smw2Jriu7jR>m!WGI#@VbCZv_1J&cJAYAq3x}+$Mi)DPwi#cp<1%fABA<#ggrEp>&k95xFBZGuF z)WbWb%KrPcJGidXSHLkaEDge3)F8PfcwBAyRH*qvez5YwmH>Bfp>Zdch`I2ft|RGD z6LB|K7@3=ae@7cWiR}aH2l!i<$l>5*$sL3dcpZLp>}V15uOuOG#?jR}& z$IIOOVEIm3fnZr}8|SnVj}hib3I{#hYET!Tq+nEIgwclZyY>bMtjp-Twc3c`lm z;V7#9b0Ewor#O>^F$xx&7)rrls4N~Q*YjFbN`WAs45K-DIfXAM_5t|zV<>zxz?NEs zW@+>;$$+#22WC||icUE31f%>sux@!sDLhYcDb>U)$0aw&Z-B=xY+RQg(FE7bdDQa^%lfl zS3v0A#~C_u7=n9M@TB)9tT4}ijmySTkjbo{DGsSbTDf1l4j>)@ZYgUWb(A4VwL6^MqDj_&fwJFyJy z7tT45k5kUnGPIMBdz3<tGcrH=R64Lx5pjT`DRkfEhX|4H37Ti@K%`cz6=0BvixgFI#Y!_}rHlUyni91%Q@;n@?nyNihdaYcrHa<+8;hc7cmFpbvVA6;oUcr%Ogg zrN_0>WfG@RGLK>b2F7t`pV7a}M!rr*cyV@J9zzKku_(%A*n&vx3vO}`frK359^&Sw z-^(@nRrp-xu0o&o5`L<|(}P`$ez)S8AuZTt>9|K!) zvod68T?;6(K!P> z<0T85eu6rr1zw`C>1I}^&v0z;DUOpH*hrJ}hds5LaFNATn`?9qPDbc2?EPtcY`k&n zL^i@?8x9+UXzbu$Z|w(cW47Cip(3`c@X&)pl?VaYt6}j1K-TRw+`` z?o90wR89h?II$_lUoBa#U=e_3CRO7DfM%)&FS=^7s2T_8*oB2><+c}71#q^G(1ibM z;CkyXs2Y=(keEZ&Y2$FE_(U=e^;WVnqN&Fyq$1l&%WJy+}_vGUcHt#SBbPkvVm zx=Bk_IJk*{v*Kn}xGA+(|AOLX=)J*I$Oblr7XN(KtRKI*sJ7I>&^Lti^^eOEEWtj)P&&^CX)3D1_dv z2H91=3FX$WZ^i43cuH2-8hmqrfdzwZ7>w8%kI0=+%(}Wj-xNSeReyag_D<2BS%QSD zFoaS{$XY`Ppo**t3X>lguTwsf>YN*nOjr#HO7i+KOxKnbS8Tr!T`W_!@2!7>)h8dS zw8=V*fxE*0&WCHK4`lP?0oF$>a?p-MZbyB<$q8?C#bT2kt->;pk|-Wk>K7uqc1^CJ z+=L$(NXjNWm6D(uAot+e*$a?fSS0>#*j5a~$3ZyZ&}|Z!QTU0(x}{8DN6IPzowOUr z`SD0(3RhLj%XkdyA2@L1RO2W$4NFDdxD+Fz#1#TuX05<(LRUe?sanNMw2GOq5Y%r9 zpuDc14W27+P<&IarjAaOF3iA)FNmP%T0BOKF@ih>o=hG?p*$GjiSs}VIDthi)*nWj zti}>6eZWHm#1kXEgovPq#fdJ&8ehirBMgKGTPF1bG<(IW{yZ*+M3WUZrA!gTB@?`p z!IHxd(A7j|P3#T=hfTb6h`AR;mMfZ;Vo0)+ii9X{K#=qb$fz(dQ2zwFS1?;aF;;*O&GCv z3B>;jBlawT_}egIwiWTG>?yWDu&bc)cVWa_E28fYZ37NdL=t=cTX6N6dMOl3AFB5! zDE^|D9pzsXvx5PPVs>yKjJY%m!k9h}$SlQxmbMhFj_V3e`pg@I5;O|j_rs;+wlWsN z*7+=f?riuuM6Exc3fdqQu+p`wsB2Sx8)Se^QH8+VtOhGuEGS~Yw!k>4$>n_YKe5bx z0idGwd50Ur(jibn+T76gRlhfOIQsE8&!p^rhXB zj&)0#z>cJK3M%a<*<(G!^jNT5W5>OU_ti{BOBfIEXdcZFg>U|F$w!*N-V}r4TEsMm z<1Kw%i_of%u3EP^5Pwoymw1cn$QG^3L%2^yj}vdv6sdJ{V2Bm~>K$Ew;vBH0m3FAz z5N5qw*N&Fmp@BkiTD4k-23i!eLj#2|m)1aGOuuX!xSVB_P|a%Ph7q|Z!H&gIJGiho zY6lw@NA2K43+mF0XhHQkv8XPd2x(W%!zHw9C9JwlPD|iB8-58r2=6t~DhyRc6H)2v zj8;({CvbyS5Mt6)K}oC+h(1nYlI&|yy!xN*T9jeQ#q}{RlfgtgNC`UEe0HRSu+EgI zkM%4Ji)v}3kNpX~uaD!A^Rb@$DVfhnx#=!Q^+FUlGp?uOAc4LOQ%d-B!Dw8t=|o7u zxwV8_b-Z(r(5_bVcGBDAe?ZCk3Oek7P-?+DRAlnAhYgbsP_9 z(d9Ij541T~E`9R-AT^lf_TAK=n@Osn5zN)G2Y(a#Cb#D%9D(qy*pm}Dyf8-RVPeMa zCgl6oZWewMe}|EkS_oGZBMmIU7Z22bf{{3GSTRsQ-U;%X*RdmX#&BQH7!-Ys=NWW$ zta^^=4jgVYZwB`xSTz?vqkLB8iddq&umC&zK6<)qF~{vKdnz8TP>dz|Z&_@-gYdlyXRGuv)Zos0Wt33)SG9{(CZb`ZA z)M9bCh2Pp}z+V5>(Ga`AEBb}`AZq*59Q%Ms%6e3!g(Co8c%w4=S4lqP#oiby!nlg0;=i7LDxRABM}=Y?1@D4CXae$dj4@V2zHD~o7NFGG{U-Z{=| z$e{?hJ!)xju?mh8Htxav$VVCb1?_nzad%Oi-jty&a5r(QtLRguh@)7Hj_byYRRL+dCBN~u7yRC~g6n-~d;R-HyJdqVX0R8vqMUeb)g#=b zc;hmScZu4eebBRo%nj%=JM>ARA+y99@J1}j#?v*J?DIzVNR10A zT`25_hi6cTUXW~qtfwIBq5jryNbR)W)k3?zq|9Fb-qDUsaM5Wp)P%bBf1Ru=RL&z| z8_>FAy4fh=mgxK1X}qI_M!%oFwWa4{o56*!k$;vpBfVZsYi^~;V_iBy*I!iU-2>9Qoh^AZb_Noj+BFa3v&)WIei?;i~Z$T+9kajX|z%ZwfI)1P~{H zn0MgGtzt&=xadbwg7qP6VZvwzu7`1ZPfUEXkG)1@{mqas3>Kg}YWqaggoF!xCUj_r z4u1IB!>l9g&1IwQTWD5$c#3{qZE?r|F6)K1SKG&`t%}irmaxm%FhLyK=CN3H_`K8| zHyZov??W^wEPmh?jXs=}KVLt$*58FNmDR}gE6ZO3kHdOk)%8d+w22rdZ6qZDu~m;@ zuXughyOQ-a08U+{(u=LUIGmK$FHxfvM@lWh_DFB*cWdkxs=`#5ElyvRbkjUWlTg)! zH=cae#0`7RYHFL9y6WdYrLrYu?#gQDLjjcSP(!~MK-GpFhNJez4+K!EA&nc-V$?($ z8q#9aJPPV-F}3r=M8}L46XO4@QleKf*(_CCXx(g7;@!!b9M|63E^N=`(SKUk1*b-gTP7F#~^P02iKV_}B|H zvarkmjS+-!%m7c;n8GsyG|Sdh^l;lEw2hf1W-4(Xh{0oGyayZr78Q9d4PEHQHpKAI zDYJkGl|Cudm9cyV{H356ES>?kNrVA&;u0#K0lzu`lWq+jo7bo;wSjF5723eg&@dLy zz>d){)}w}1$68_B^lPiyDvnwao%}GiuUhkbAAUp%EemH_kis3928BB?4GMQ)3WTfV zVs#t^u#2#Ap%-H|$P zP|~O|G0yV)QG*FjpL~Vww~gD#9N(0OSpd%3AH%tNiN_oatgK-o4jQjNl)lERCjI11 z@1t3|oWq3%4cF?o!#hkBk>%rq_!x7nSotiFq!SF%je zC{Nf8ioxJYYyKh>1YV=&U!b3!e!fIMU#6dX_48Hwd5wNv&*!sXvHy=?7MzChVa*>< zp#nrf*eAl7UgS>c6AuEV@nQU+C*pe<7%6m*Z^gSe$!!vNp-EMjW0j~Cv>SY|bQ@ud2k(Zq)#(RHiy|`l4&|>rj$02S zkuS8#h1bCCQK;kE4G?}OZ!3Rl^uz_5>w{S2@wR^+qV75+Rq=Qm^xl_(h`Y{^x={pg z!`>YVB(AX2DA-$&^N{#v6YE`^>0l~}qKSeAa_g4`G-wh|NMv>OCzPEojEGHa`XW&i zBBV@8B@pfNrak)63%IkYjx{bw2abFx`l?@45r_2kV3L$XbN#(r=c+I zQU|)Ekz|;o2HWY zJn0;=ehyiaLv~QNii5a0qX5mNG!Ur_r2%JPCXJk*M$V*>3)091X@r}#S{sYoLZxom z-8f#Q%t<5f78+{rrv2dva0Fv#oM8&|vTA=z$gbq_q$E4jDcRT*GOUREtcV*{#Qmlh z4>-{-*>K)xlCb?G;22po4%<({4w48-wo>|kI>|PzHzbr)Ra#^mpFnigia^J2dQL!~ zlrfY9&ll(4o zG;ibQKod4^e0pXUZ^YWDFqK^YKHiM}L$BAP8(j*8+Ha1!f zzM=ZwgU2qb@lk#q&_4UD#(TR!XLsDBmui^Yzz>)}zHFUS zl_)7;;0|Bv7X1N7uvjha?fGnP|7hAO%NBNH?E#v*6><^2@%eHyAnWc?VhsYwv>h1Y(*L4H2M$AJ~HK3IHg z*^Oaw{*J6(mk5#5`=UItp?xxbVpvktvJ)tMBoRH7$BE(x0^9W zz}|=Joq_7K8t+1gOnJPE@h#RUDJ&A}(Gt6N`1!_o-)kZmFWBsPw~2z!L2shKF|6v} z5ffgQ)u22yz`t3dI*6(SmCbrqxk~0>|1UF4!NKwoTb9*eOY(z?Yj=M%hL0n}U)AMQklPZqksF1SxDE25x9bf(AA+ z12;4zK?B>Df%`P5{uVT(LStk%g&}a!`!69O4H76SI%?YRQ;m^Rwl|zXt*nJArAO}N zw&m1!qEQOE_q(FLXfYMm7WdgwCn}Qd9UCN&VFHQzJ9$7C0;6eFH(AIgwPX-7Bw3olwYlq86#3g*$R0&ExKVXI z=w0fjcew+p(J!_<+2*4WTxSx&j+^UU4LYsHKO1I-9~JKFm;@xB^l~XNpgJ1tRWah7iPM*A{P|#-p(Sv3ToVHFhCXYq&9R_ z#AmcYRS_?0gRYcD9&9-5#s?-&48E^^(fV1HNJ-M4Ob%%+uP}A6WmrlV z++MnXMyC_K7wlvb2hnT)Jz2$0`Yt?S5=`*0S=@Z)7 z^a<^3`h<2ieL}l5eE~1s^t%l3RMY1G@7qW=(17g&yv_w&)vg@2tM4FcOS=k?E~Z4C1xwgrw19x}-7 zV;HOPFoG!m(LSSrsE^kBUtLBymmpA$avt&1R;!K!ns(YDKW_J-l|yl@4IqyMkX{&hflJZ6 z;aS&C=x_u}CscnWlGTl+y%Taa(vZ;h&hpbLWNSY}U2_!=`)85l*cmYAJ;iGL2-M@? z?SVlm;S4%2-A{x%ujfmS8Gn}q9hRyT-6K_Al&pFft*(E*N{()v{PeOb)ufCo*KYP8 z&H{N&NiSD2dk?um?=KW+$Y1Nr4SUzf=1gw68UN%~=xo*6HqI5m)CZ$vlPMgvtwqJ^ z4uj`HU z!JWZwoZZ*Xt-JCzZj{hEisy{no`djno8*IMen68UW23=1t1hA!h zlg9l^loyTrSA3Yp{c6*=e*!2QH<481{yE-s#0@%1EEw=3& z(6+x0mV39MhOEXl6^!i!>twd=B zv=TEWKr2yNq1V?6y+$kOh7)K7m1(P1And7X1;dlJrf0xQHax8rLhVkm5A9B0#O$Ko zDXoBZXS4#^T}yN7t`$f@SFI2PEJa-2XayX-D!BQBIDnSLW9HQ7}HGU&Nc6v)re|Lf)HAdp}_iuwQOn<)^ zd||%$yWk5WkII0Fr-zm-D5`<($y$|jP!0P{HT)NFSq<`(anc*ncfrid^|m5itMOYP zwATYx7B8|hN*Nfw&jTBCt$^=;EdcQScT#*c7AS{%>O z&ql2VgXJb+yc5T~6VbdAS>CaU!Szh8p1An0Mu5C`@DY%4(8ipE$z8L7BNc3q|L{aC zf7@7LWx^UN5<0qaXzwz>B)ym%hr}ML>qQ-0?LNr@mm6qkaJNNk@Rp~zJs#aVkM79YvX*rmCd@@k|iF1xmw?Gz#V zuFUT;UYe^q94Wy5y#mDC%3z{ z+GW&eD!yxOH6r5QgVQr*Ea0Yg+$6k;P2PiraFjO}%f;v4#ulR1Pp$;+x0w&DNeX4S zJhcZ>I*DIR$9L}9N}qVEFk?bqcFgWb|K2yrzEgDlcj@9suyR0i=5leNXf9{ZwvnWx z#Mj?Fni^vJOtaiaThZN7>>W1c%RspotO4ai;Oq)}Pw~jJ>`3H0hBp_0DY^K_mh#KN z=fF2%F7CZ(3#5Xc0aL}az41TLEfmrK|L8h^Zv_~a_gdb>&1jJC1V8HAC?W8GpQ+se zR4_GLdpBNku)VH74`Do^yg3GT@nGXi@P}pZ$QoeOUWy;9ej#wiwzg#8c4UwXpThA^ ze^&Dyh!w^??4dAmRs(fL&$@*hrIWLg%G`B22akH&k-M@F4pQy6lHmeg1QZ z`{MDlYR3|c(=Uh}EAIkfm<8Fzsl;I3u1z7Vgl9$L9O2_6u!o~?=+~wQ9PT}}$jxe4 zb)OBAgrV|Q--1tg+Nk@5wdma*atohx_E^?}mlFzbjYjLtpv*%va7miNT^l&)em#uE zaq>A?XT@Oo8v$4fpZGqmj{wDGdjAD14djHI7UVRLFCwyW8Vlr;b)uQdQ91^_sKWQ{ z_0>Fm+g=YdaXPp{RMr#r^k!v$ZzAMH^|O98RQkNH#ArY&9ipm2eaL6y1*kaHq&A)a z?!dIxmL zu7;%``0EY{vM}`%0>$ZSimmWj<&Rm_{ydIl7iTv^bwxJekIG>lf3?5D4~ccDJxMG) z(YX7uC=`4ZZo~>ZFtFM_fjd?)bl_d~m1nT1eI^>*FBkR~O*DQXn(urW(Xdw{(J*#W z*#N<6pu>%M*PIi%qF-#0m}I_I0xL;Ve~m}UaxvUh+8;VJ5@-$&Zch;OUdY!*?21Ml zoXQ}iy?M&En_d~kx$lDa8_4HDTn*yp)yU2*nO)q{pxbX?L*95v@MNT+4=DLLRxfnM ziaNX2hF{sD^MY#_lhT2J5sN8R;vkt{q})z9P8PS&5nr<1Hd zlTVzKRtPvgt}w8&t5D$jU*TkOwsKz8V_tgA3vNeNL4D>WZ(jP%%Yb<)Xs`d+r z5bU7xS?DPE)1fmD+`&@XdXtNbVBl+P+GDJ?r9-IkFs(KF>|?1DP@e7o`Sa{~vo_0w32^oj-bS=FL9R(@1Yd zlC2p#mORmDBzqLd*f^20LV-ZoC8Z!?hctFfCX5qGGo3*|%T^qcMrRs8V9rkHs0@L(6{bR1Wy{$U2S>p)Hk!|(*u=}apvIwhgN+)iXhsAkM;xZ?-IQzt$l&L9cfjOzH1#nKv#5HuVc8B1qx#=@> zhIx)-@=!~|VL=|}7Q_cr^Bj!hfm_*M>KPy!b)sLjf9e$FqM>jF$#V;SwiVsNtz?Ga z!-$1*Km`TC1t&sA@a%hoj<9*lb+igjY!mY=h0FO3Lope31a0g}%;E_tyGGMhjBE10 zL=hLzz2$u7`S|i5cEG-m(E*g+g*fQQkKhL#d5?TUjd^~9^;z6?tK$5Wm7Ud|55SWF zH)Ut%)l~H?U}F9h4ZIKYCkS90+A!+R?GzI=fgBqBZ&veJji1R3Hu?q;2vy5 z{E+hqiTWXrBLw%wkdD)c%QqFO6Qz+5Uv)P!z8q~g1e4gCp!@0qFe&Y18uOFeHwX|K zVSX5{J_0Ef!(mk^jr|0~b#M?^)#^#~?J!fJF%5V!0OPRqC3U99SuW!Aka!;tuh<>; zMTQbs0eMPA4Z}Pg*qGAfPo6gEz;%{ZTD*}-c?UeBz4mFCPeY&gE+18jr z&5oq4K&P5S?={-!*Iu@Nld@HQBDIyIykd~hor|Fuv4!1M_ciVF&;H6^0do5)e-XTX z8nW{DJ=X_Pd0*ksF#qZ=8y3d=W&6|`ij+p$NPTKYu9>`l75MdafNr%sQVvI))8e=v zj=u0tsSufN*IUHNL1h=W&kHwV(8=cFYQJA@^*mQJ?pVgvxN*?soeDJID*8Coq7FL7 zhTNU7*rwVo3Trc@OdO&?PYPngwL4WnE{YJSmtIINRtpYsexbHntM_sMOogI_kE3xQ zbiIYl>JJg3-g)=~Ea-j{tpyDh<3SplEC+||EX+=YngPg=`RoM>HV3^hs6Z|tJRsZt znSC%g-Wo{KNwdx&M{sc!LcSNIdvv4Iyzhc$^KLuzEKDMM`GJu%x!4M`hXD1Pbq-fa zg79n;AgRiWP+2=wN~`1vRK=Mqs-mXts45w(pHxl1)w^#0vvc$daWlHPnwF8iK)iz9Ua9WD%7VO1Gtu-{wnwBN}HHn~!CJ z%}4UrD@^><&lB`Vwa_cq23j<2FJ0$yc{M|sk5{hyTq1?YW^YOv6h)$|wmmT2 zFH^nHY-bsPt>*wH(o0;n1S-|9RoSB}pz-!&v_GKm8aApS%kg z#d1^<`&7_IQqG<$0C&<@JQ~LWhiLD77-wNT60G86tVIjU0_6({3*%N$W|_95bgl-* z%Wr|HPNdG2=%{mHYAwa^>s%?NbHVT}buOr0TJyrnew)t4t)OJ(Z?TLC`}MBQ1>hQ; ztH-BvT@2(6bgpYV=v*H{&k9yQjoII>zxJHap%OkFDy4KN+uz}p+*+eUbr7KUQ-@0Z z&gf97(t|{Y>S^gv7lt~E$z`;x(xFsKU%5d`Q|`AbJMD^4n~waDBBT_w)_~|A*L~`_ ztD0@72=xq1!$uKSuQyVJUa^rP^!Hvv?!p4O?#S)Emu-3idhSDt&7zu_odCtAr={4u z3$4~kv3aP+igK!^gAcl?i(fmr`X|s%+%Mso zFf9KEZhOGGNXA?L>4zG@DIX0`=3t^HqSsH<{ZBS+b^i~9lTJNjQHl)c6>o5-fN%1!-^%!8iWB&exlSX%>WmrWV!Ur*rq#5PtPSG#|s+wl|}NAa{^xn;>6Dng2)1 zv}U!xcJd5PMmPpUSBuzBx0Zcf>NiUrf2a8uuHkl2>6?JVkb9?2BPu<%Ks%`PHaZgf z(RE0WNQrI70&*{ldPqKjiRV&R2XZV!F z2b%i8^vx*pvAe)KSgB53;8(2KV0pGPi~s5s^KRoS4L&!Ig3x0D+GrmD2Ry_qfAdf%@1p=~Q)!CY21u&nAs zvAJLEOvZEkD;D|oxorD_f7_z|YlS{CP(G-Z^Iy=RT)**!e9{$*_h_fxXJQS_*Dost ztmebC)IK}9sI)-&eFSdS`i3y+x$772|J~Q0+ct3SQ=dL@{h8a!$L!BN53Us|wZ*e; z!$B0|-{K0D-J6}(SoZcw{7()HGTKLp!tuz%lF0>gRI}>yDv=gD+q)K z_b%T{T4F(w+7nv8apAB*i{no5`mMfoasoD6)@p^jZ$_)H7Zx9A@xtK!9u!W%p#QtH z81~A}0?lO&|C*)JZ)7jBwW!%Pga+~_VwcbE9o$$r__z8V5)Qxe5r7B3aPZHpKJp{X zthnjb?OROicJ^EexSsm|Tz2zj^fX}ri(PhHd$M~MeZEYaPeO7rb*eE_H1P`Rh8u@C z)7@c}AtQ6QCc4wNp(14DS<`f=UwtkAiW?4a@$40jyQ>LRy!hy_J`|NGZntqUIEjmI z3%FSO%jYkQ<6vi)UOTk7S`5MCsB5p0@_!%}PlV~CUbAs^f`0N{TsXu(+>;7GX5#Ml ziTTI0pmpQK83Zx@V)(<1jtgx(%p&XZ^fDZqD`0XK zTjE*dJaS)J;@Kj)W|k;e=*lngBo|#hOFU;oS7eDtXy^(qaTZ6HvBbk$bagFp$q%lj zz!EzrTr+_s9=(ApA6Vjw6CF`q8JSHb+o}KJ!2DkaeZ4IiM z+_|3eJ-8ih3ov7G*?2&O43_Ukz=#Co^48+es&e+UoI9+=OnazTIk*iPlw{zz)?eN=&JYRF`PE8PAzlS+y#Eli=s_sJWo_NX-W|RI07u z{%U^%)!vC}Zxb|Y)81TO*O|EV_b9(x=P&9y|2yiu4G}ZGhzd8RWCrMSo5uapCfyTW z?)HQ!lvI_PJ57s@TL5Dq1kDQqYL4{@54RPIRdC+AUJB3SVcecX1 z8KxP{?$%-5k_O`qsfLnRcOljYs^oZ~gL~~%co_^PwHKssNu9*``F4ECN$@wX$7jyO z*EuJxWzBka5+QdX*DYv?KFQfawn=0Q&XdeQ$vad5i2Eg^auV=cxIM(xRJVJ8a=RPt zOSx@AzeFV0o9kYUt#!@y+BRGx2rukef@O zbmcF}*O`T+mD!hBNC&MfKOt*?AIlozCv0uvCt_{pCu$AzgIA(6SvxjAH;-k#tR018 zmIGt|q}h+>W8-vZcA6RO4gx0voG89w!NroB#!YZZPUFHY3o;fK(=xLIdl4|ZxU9$} z5@-7H7}5F?vd!Y;dME>u;<0kJM&npgzS{%$x?l`1<-5l&GV0HToWwPB!#wmA?2?V) z`xzXZ#@#J5Sa>nWVFVAK1s-l(03M_j3VzMksy8MppjSV}liF)=TT<+N)VAR)wnDmU zzZ~9{=WN9A2h4q}vAw}D>D5T%PNc#7K82i3RiZg%se*T5BE>^?s`}j!SYLaA?x>9@uvTYf~Nz*;!eW=hEceQIuG{S{1BL^@~xbP)7#;j@%p`{y5p@$KGFM!JHG}ZG)tps8y;eRq3 zXm?+5nU^YS7nu6LkZx@cxJ~%3c{|7@TvodxhD+1hyvzZFi(56L7?4p$Vb)eNuHTtq zO~Y$yny@5!iP%cMcBPkiM5^CPz4m^$47ES;A+fR@k*@Y3v6d~Q(Xfzo_debVZgrng zOBt`(YAO~Q+iGTom*5ywj$YGQ9_uwT{K>*_*GMyL@o5r4bKT zZ^ccu>_oiwv`wz{4JQ$n_h4gI;Uut3;%M(^?KXs3zH96er;K}9c$oCe&0e%*TeKvj z9YLr+8m@}r`WB)Y!)~-l$&8NosB0>gT~HjeRU_NL{MEkZz~&WA=PrU^-Khf z;)1IJvQ#+h%Fk8%s~8<)jf>$1+1L<9T|7E?GKU7(?}7WL99#t^0xkmcGlp0U#!##Z z*Vm9rtCF38w6!cPp&pncLY8`E-j5MR6_zQ@T00Dk-+l<9f(~Y2mPEimTqY)d`H|Y; z)?s$?VVWr5A(m#`@h~LmogQ^m3Ug9ZC=04-aN+wGB)a?MSon10r^?$R$|SC7OYKI4 zWk+Hl0z<89P;SY$4@B0~VSsKEw^9VnN|ri9b5amj4eKx&Vbj!RJB*<$rttDD>*N zSm6ouTp{4c3^SLN?LQ6HXSKAiLk!B3=a69dhsc$}XVEhI-Pk+Yy@hsfzTKN`_x7}T zhueHA1NJM9ZKvZ)*LaWa=#6~QXY)HzpnBWFvkR!KpS9ZDulqWwOi5AMAuY2Wd$w{1p>IUFZPmfNZ5i= zj=~+7rS+vlu&41V%7WaC%(oiX!edrXfj*1})2r9>3o{JX;jK^(8&@Hab5qa;|;?Ol|IR=qWo5Ro5}f{ zl^!uUue8#m_GU59GTv>Uq@DK2{45qewxqiONTZSn+oqlNsDy2WB{mh$9+v2txv8L? zE4C7WAsr+G>t`wnpfM#u%LC0#4`@g+-Rq>sDmcH0dY){LRp(K%WK?dAgHSN1aW&$W zCeQ{WAZNPhq{oZBcF`UOfgx7VRO7%Khu?#ET{o`d!#&kJ)q{!kd$@rSzn!wiwdsA7m@5tiG8 zAplRRAX|zIQ${qOr+MPbPP$Y%4~sl@`d|j@Mss#)j!Vv7<{7)Mb3oSa?;J2-JDmdt zaU(yC86s(Im#Tk^?sYId(GF|~5bBgiwiUZ{=sl2UFt%oAc0&L3hpYg&eeR$=ae*xI z?6)(n_u=To`{%@JP!V=D+9Ejuwg>I(&WEoF0-1aMS)^L)v|&3*_DY`>T6R2A!x z*5%&;6LE#B4i721M248x{|WQ_M5)?};@P`j=pSU_6CH7FRu zS0epb1)b&1v+~#1l<*xmZ#kQW)%Wo9EHqlX1YZe-O-#RyM~TPa9Yb@6r(gGTcu&Rx z=-dSMYk|1%@GP2Nn^z44_J7_u6R@E3Qz1od=c{ma=-Qhfsu4+J>B=D7Wo- zy9Y8UQ9RE{_fgOb&-Sj^=@q+gMH+c6OtpB@pG1;Lz!V`5koMU<<@do5A&p0*?e6J| z*+9MU!pgjzn!XdKVvYKfNy$ARH$;LEGKXSmXCBV^OE6po&bT0r(W;;3cqPI#A}FV0 z_g7&A$-;=_B{iG0`;iT9QrM~TXE5WCu?jnlM&lYZjPhd_7WQcC1Xzcv$O(Ec?T}60 z?WE(lJ}Qs0V>h_+f)+U)JJXSzu&=7fnSg{XKoWp;(`VUYG za6tB;$c(gw^<%=T?*=$CN7F5WmYH=C_sgK1*ojk~>1ff+q(MuS(t8<&V|etSZCz0K z9E6%3gE0CM9MCru@m4YoD7HcL-pAV3emguDo#PWlc6>5}6>BVqTbPuv1u}CDeu`Uw z1{{=2h`(Zo8|aTPIV8xOJ*nOvE5Cy~Eof*JdwK{JR-Wt}vIeM%)Qhd)deW8>YfKmEX(qx#`s=Q3vTO^CF(&Gu$5`%LYzqg|KT zn_62Xswnm*Aa1fZfg3MH<3LH}Udd66CEtgf>`O=c(lLV_IM8@Cd)`)@YQ)w zI=&GLL&UE4BLtQJQ!;d^VN1KE2hQSHJ5+F7sg7paV~!6WI~rnS?{|!W`xk@9VWM_Y zB>Cx^Ky2wG*7X1K(t$mm=ks>mwa|7nlQKF~`=2lBH&Cm1*wzBU1~C!=73PG|YP5&g z(^tJ7L3Xr$22Rhg*Glq9d?774x<}M7AM1xuiq_8p77pQrMJB?3F8p@*wcHCd>TKYL zM_?;inwk){^C<$M%T5%)8<>j}cd7x>w*1$-qV=(kKNOK5YEEzkX2faN6lq}66SQN}#PjfC4iTKSqesAyilrj3=6o3# zW+WZmeHRF*Xj%Rys1vsB>KjRvATptGH{neP52!O=>7K+zF-g;xK2|Ul@f`XLYvtzMcp>MdNAqSF{)x;qr`wlO5X3bgHgM;Fe&;PRXu8wDn#qe zZUS0og*TQqk69>%1J=p?d+1RSyjJU9yh1j_D(%x18seYmrD@#U65bQ=pFdGy^ZSCar z%@h=>=l9t0=^gZTBNPvALS6nUSlVN}Xrm0;d)`O`Q*WUUHEqRt(~p!0QruHfMrip| z;?o08da&{l3Yxe~Dbhn2u8KP_z(KbcSv`n>iNadicNJsYCI&8NW+j0m-jEV6e`+1W zqVj{w_MkAWOGr`sJ_ic<$G$<|*q3(Fn=3;sQrgx7ACxO`e-`zErYi0m>F!tVo9M<{ zL&U$CZW!dI`!jUs?Yz8hlO;Fd50#N4cW2{Uywt!P4tk;(upP3q)dD!6xNj}=WJ1qd z3mqf$)V0tepSaDVMO>x+>ptd6H~BOUjCH3N(pXyza2@(D_=lclS6O7mmFE4Jsi~y zOgq`)ZAooe2K%mNu=t8%upkbE!3KE3#OQ0M{QQMOUJ~H(mz3M%ueh$8 z43eo1gQUw}ZG+?q;4jTVSnT-(ImnX=(~oasC<-+mRO4wkaN8DmMaIPg=UV}uFY(Jb z&0ngy`_y7Ilq?QpeQH4(1G*@ASfja)M?d6&V^V4FnnioGbLWwCW)$a1g zHpX-pm1NBtcA^AL7OpXW6|EMF)6~*?skd~?;&c?le8gS2PH88XBCx0(!;*_e4ojR2 z7p~)wG;ShjQY3?g8kK|{ZY^9Vfaa!04GCy{SQo+ShLavhh14iNsJh>foyK$!i~F{X z@ufGNLx;&%eqY#_l`!qRzrzgLd1&5h+z+O=RQU3cy(My5M!x7bdAHl&^;a-vrK_0Ka(uS5~&W{E;4Dz1Zb5=CGbi%T$PU&phZR3!TAU!Wt4S7zuTwD3+U zhZ!vRs6ge<;vw=`EF3tjg6%kac4M+*ZvkwJy(KjLIr4EHKIkJ5qOYfS01E{#yb*^S z`?yem!G80coxK1!TQS&gnzJ)>pM-|lh6WmO(xa6n$W$l2y@DqXDBTK`5jYgbnRda~ zSV8Q`{EY2#a^y6*i-V==75n66kLL8P5oih|0&UOxg$X!3AYH7w>ga14Vr1qT=AntW zZRSPv4Pl#PRxAve=T7MYKT?doL$@a&oLCC&ySJ zk(0Voerbs%XbJYDr4YrFmSZbIM$B6=9=kMIKe70A$xOXK4mtQ2{ zJT(0a499i>TvV_N;JRDr-EN;Wk$@V%-OfNbj12D^|sJ#_PoAJd1 za_MbAA{UMVX_R57{~?gTfa8^amAM8Dr- z%V^u!B59o9u|-kgevK`tkRDs4Kx@Yq276X2tbc568$%*kFOhBSb*b?EqX91 zv<)VD#KB;4lNxc>(AWa~@}YUlL-Us3>3Iu>mY1oa zWdthbHVz{P^e}Q7z8HH>u5`oUjyWg$#vFUpo44RV%xFt(q;JjGqCo4%mXo2W4)QeB z=<=GeWn0_4Wn|5~Wfbbh$!%kcq;Z1B7FFBMW6Nmgc?&9}#}+Bj+OdVf-q<2U@sBM6 z&|`~4|80&fEWpNN3&wyuIV2R?1{Y33z(($L27}8#tsh)Co$2*WXSSYx*f+R@rmO## z$Cg;&qv%fm4Q)=Z>^@9(rer#VJG%2xRDn;wc8DFVZaD)#mp{{4gz78&Y25%#LNyp%*2v+DyUbxb zbE#A<%6cpESeU&KW!mtF6^T9y)t!VPJ?J(;krwsQtJyV_kzqEKpG zgv-3L;4aHWB^^7hjYI%6x>?- z>GP-OPl3(+;MQM$`zsT3(_dlF42kDqQN>B2B00wnVTl)KQ-Xn=Mj-G%;OC%P<(A{g z`s9M_&-1{tByfr)35?;vCONi%;|96uaxix~^{8vP?ux@n%cXN%&vIsOX7-?*k-7vc zy9xulni`Z0*13u51cF$c*-pTxU z1S2;CngA1rF?QhSm`9uC(j(odnYk7jJjxK*A{buR_F?;I6XM;bjc7CH(FO<96y;I5 zb#$3)qYLUDnFAr_K@boFe5UKn{Qx`maGV>VOYwjv4g89KLIJL2|JajfZ&#zw;3Z__zf!^>HV78ii-Tj> zb&Sg@b_;691+Tje0#xk5CMtgdc5e&dX45BOGlngtwhh!0KZ?tPyaIwvO(kGb!Ij_& z4${PMqy|^plSMp8A%}gUuT5FylsbwOlH)+-+m#SP6w`|Yflo_-vV&(wrw>se=p7fl z+p&w@U^PkktWOi`9k}o`->$}bnGwKQWpyu4HEi?G(c|+l_(0+{5Mp#(ReH$x)^{H?VhjPL*yJ576zn@5zD$ ztnW1J1dtqb&*2uWuX=v!&}h=W7BwCq4T@LX$DDB9h6Q31T(s~jXR9-vd?8*{Jwvy1 za89++2K80eqB3&}t%9_Q!J_fd)&ryh?-pBu$4alj6_Zk4cO=O9x^7XWd_yb#7DG$MGSh$bHYZQaa$PeW#Q3cGzoJqzgzlai1s7+ zgrALh7&Dqo>YOKop;o^>BzazI9gv3I$Zjq1v_o|R@XofU+$q346M%f8pUj{t<`eltEk=$i=5 zC@5%29q=Dr9;*K(0(V~puTg&sobr6&#HB`^X^f}QxQtyr8-q!M;kYnh)R|57>o}h* zAV^~-;)_3ZM#uPn#rV9O--<6F=0b-`ZL=>muqKnnP!SWTG+SnY{U8*`K~A5l_<~3~ zMpOxO9FxNf;T&G}WlTyqNSQH*{6#ncF_4k&xt=Zyg`l%=78-jIeDq+VhA!r5XMj5_ zsS0bs61HlaklIGtYP%St`2|N2))9oUc?bv1Cz5~ZH=E1&CujL*>j;Z1h?W`J^?O8Tftl=y0BU-!!kXC+5=!H zabUwZa>iTHW4Z)Z()1x&rcLxCmm7k+ zJ;g^^uv$dYS06>(uoMRu@oxhXF5=(m;_61r%$37p_Xc5}b)Qi`E4;(1ef?%_dPrI4 zfjlv3A4`p^rsNj0hA*&&pFTToWX2p=<*6OqcbT%vqny!g>d>8XfaK zw}ti2HmnWueq0Ox?}3lTc^)8WfS2MUq!2nwB~jGn7W%ss`rj$^Th^o3zU?BfH>#w@ zvR9$qsITWz#}PiclqY>o#%ZGi4N~8*1`EnZDjP6Aa1z2ABq8h+#MI1V;o=6V1wh;b zkT3uwt{KNvwJXzL#oIyMjeJ|&)dm;8K0dzmKXn7>Lo0fZ$3!iRpQRKLAOz6?wP2`Uq6O z8f;6IMMtVErJE|I+c3BT?(ni_~`oFlmdZMW;L}E(OARRYc7B0eJ$<$3pm^S2Z3f+3|zx>QD?8mtsW%v-cHHEojsq)yxKJ`Pze^RI*vY9 zokslDNazL?y`VcRJ$5%=hHkFM6g6)Y`Sa4+yVymkd?cgc>TY~}n9+zpT16X;wgwih z4PdQcN0-d+QQg4SKwxQliM^lPcN}MsZe&FtvBv`=ZBZrSX;kw^TR)~ z#P(64>=RpWzv-LPOQgR_g(O!t5}$-bW8`mW)(I$qFs>AXEl)CE;|5^C{XuYbCRPgFjpMf>rU^RupxxfspO}z!dC9FXjbrucFpSU3L9t@tB zv$%4y7%_r4l-*zNrlS{*9y-!+*mS_K##Q^VHP(Yy=Ihs^E_1OkTzJqTI2X5j(vixi zKpfoNiY!}mT$6StVP-U5#5Lqd*~HZZT%(UHhv#@tLV^+qN<_9T#uLooM*rjvW`*LJ2T7Y@pVY{KC|E55Ez!j^!p??!W{Zl*j&^rI_S{Af@a6kk9>L zpZg0!w~mJ0SN)Zrol}^R@RDOsf9JQxaPuY;$|vbtR$L2Ea$2|u*U(lEm;F2 zw_?RO)1X7KYzR7n5Kj_QDd|)Wy&F4hN5)_{4;HuWLZNT?>$ne@2M1`B^an zfqRj!a{!$aT>$-%fz?;z?td;Z1&QE!^ZESw2dH9Gu6Z!M_39ALnblR0jG9yt*F>%_p62BrkA`Qp z_iG56!X}x&{IPIpa|=QYjm&f)?uNseDXghVa<4^!6ScQ9$ESe8SArfg|IS}rzU;t` zlk9MQfBCWtAMqkI6Wsd%oS24#p%YK6Kzk+lqD%K*v7--;=RB)cn`mmt#KGbp!+GKU zE3TNBg(H9P+0UCew|KD~!i|DYwdxo>5ROzYLG4G2`|Z$pytc38)nCF6Ff5Y}6(4Pc zs|VpJo&m?R>8QYQDIJq=ROuLn<1#unYon+ChSX??>>^Z8%wCCVZ(AkEW0C;0loqtD zU5Bw#bTlzqyQftE;V)rJ6nbvLE!fx@7_|8h>&BQuW8Q>62pO)8cX6-j4RB-e#dfeH zDugEON41+w7NhCdk9rCELh_gW(d8FSa4V|4nMQrhg&j!lOe`e{iA*YPa%9tVa5+(&Ri+VXf=u8e_-HL%Kl@X1olJXLT-7{t~hRuKk z(u*J|6vs^k3w}~Uw+Jpa@*l$f$VuqLfi8?cBk{tQK@cQ>iA6aX3a%^9+H&sLZh}aX zxGI=yq*GN#N?G|3l2;LpI!ig%s^^aFZfv2Eq=a=m#@4u!AUco)#WXFvbY#LWm4nPs zu~?W6NnoASm#P#DB96HpzXnIc;lfuyn6Hin*pJY4xE~QngKoT29fq6#xUP=XEX|9m zLd+9}Wfh4=r21k@m7$hiN*GhNQzbr(EZqP2{n*fThpA8oVtnVM*YT0w5Oj?9!fq36 zaZ|@ksqrjj)q3uLB2BB%vHpZr+7HO{5m6wJHnp!VVv*sGAe$1!gi(7Lf|M`PKb5V$ z3SO&rBY_hjO){#c#F@ieKp-fe5hv(CW&Cf5k<4u9Pyr(32t?fiM^a0yO;g#Vxd?S; zN?n}x_g2wF`*7J5S}10^VuV@_S*_o-GvjbL65+cRKZof_pSPkCXS9&v| zh45@B&|kGN`e%v$dSpcvI#au&GiD;R(+okWW(Kz)M)9CRKXqrva`?)MJM5Ybbab{v zv81BEXoKj%oj9|ka+3y)4Nj)iC1u!V5(DsdY;X#56;i+QLX^ICGm_GT6%EFYmY!{Z z^uod5*&*RsJKSPg+M>{GE7$fldz3OAm>1KB{*GW?5msXZc=YS&ExXU>5iG@NrAzKd zry5+_seI+TFWu_Ia(}mQf46Xdx8{C%e#-6!_Z#eT*6zJvP8{7c(JSmCtc}m5dy9ic z9Ro9tm9E&m*OV-MDz2AdcCRz}4O`#ncP4MNd&Ym01TqYqQ-ro)i5ODFbXJ^eXj=>$ zI9Rjeej`jP8%u7hPaVAyGA61%`9BJ~{^BQg5i zSg;%CT$6EQ(T7-l#IXA$*0_%4CW~;R4v8A9z2XUh%lhOE2xQoW8DJoAMOS>OOYGj8 z(Amx-2>(ox)YWq#jks4~im29J2i}1)gt`hHfVbH44#Im{%bOkR$9%*cNsLsm@hJ>b zV?!9%t_E=oHGZh!!$^fB)PZS%+K1k!2a(P!|&x& zkK*<7mYl|+TD+$+kPcQbpN4_LU}c3v4RB6I187-p2eS&NiLeZCXa>Yhy#T~m$*J8O z3|xj&lB4i(y+I4(R67+gc(m0S5l&)+xLfL@J)^br&>X%bGzyIBW$bw2Z!uPL%MUi( z60jBO`6s$-|Iili=%&$HXbO@!RyzY98KQlurohZ`Ut^$TFvZ{lL`9PA;sE3puC2#o%)mD(AJA|65@!FDY)d7B z+JWVmalE)Uev(mVH^+_#MWd`)#2^I3cX=M_W1{%%==cc8wMApfxpEg#JR0wuXbE`Z z8ZTJg+e%P*G~Q;Fkc=TZ9dWB*D>N5}#2_RV-q{Uj*tcK;uN#<|^TE|z@OQSpnO1$V z8RM&0B(sA5g&1x1jSccl#=lcQKin`chkPDth|h&$x6uU@PRHWz7;TB40e;{=LqNdb zk}&3jToN`y4enx5?(>aCOpmxl1SmJf3@iZ8tM{cO7o@>6I8;-yrhv0(%)(Gh%qmf} z&a6__yK9oXdI7R63q!WgR6fdW$i|v4L+mFMO{p%HMOmPERe8+&w2z6)Y*C*fV;#z$ z#@NYza1P2@lX9Mhazeth662>>@jOOw#NHr-F#LAQH&+IetLI&&xN|_z!-~2F3`0t;QGmbsAs5Clzv*V=1T^ z@nUZ(6kfI_xIW1cwR*YZF6PDYmj_US9Dj{E(~&n|4#BRR)wqet)tRWf`%MVXwh~so zS8l5i}{ahjP`5m8IR*%FF;*sZ{s***zQWj7~NB~3DQX)a6!Bl~k{3&tSQ2DEe zg%Gb03P>^_%b&rAJjh=xyYvG|1zoU}v?lVteV6T(w7|(Y0LsjaI$6V7M!zY8T9o$SevlFX`QqLZVIi`KK@+o?~*`t?Ww$ZkB z;l=xXc^Y-{d>eq=-v(g5)kuqQSnOiDa?ntUNn_qEayTwI6mYYzChKd6{Sz~ARvYaV z-w)6F${_2&;s@<@_IRm8Qr1LgUn$?tHGV5_1PmDxUAOR86l%eSiAxOV*g<~Ku|x6= zH7xmBjj(*fjfi|Bji`L1jhK96jX1u+>OWEN1gjswCtmm(dI**_Pv_d0Ec|9_A4OM+ ziVjw@Pl3)h0u)*MjpBq?v{1tF`sz0P)EQB8GaOCwso zy@+-0pwXua(x(d1r?T#ANQQlqjbR55e;7TxVUQ&7?4u{3Jym+TvmM zIinM2C$h8Hi_Tt7~wu5$4#Tn{Xkuxdcgl2lVL zy!>L$gRg5{p_AJ<<)iN1IXh8LgFDNgaIeqV$?`w)JR-a10JPN)qY{4qN9gC+X$O7; z-muu8>+CPPN8mN;LLgj{hLV6yK=G|q4`HDh5ION#;F?ZO5|<*WvvTO&q$SS39eOKjixc*Ut5Q?6#~~dZ)s3MToN!?$e1%2um1f6Rm>OT< zQheJ=1bu*%Gplby#zsBqFRf8e`Mrf#p-8;y!`@t&fc&@|^E<57R^N;Os6;F%((sW4 zr;dCiu~v3{4&rJ}3EF95c{D*vWHW0WO~S#(BM=Za8?brLleTRYYMdrOf3+H?E4;SK zHDLdqk=v@+m;unJQ}CGUPet_cssdR6(J1Q7mFhI8Fv^p|q)FJ_4idr00}kT2x`ZbK z7%n>a?hj(qO=q#}oc;+2_Qz z?V1v@7S%IR_;8)HOdRctsvQbdK?&*`i$Kr9enlYgT%+g3hIt{Rg%S7zow-|qi8(+fjLp-DK4rGII zI~AA@V($d~GL>ll43$_I?@4v@fZYWg-}*tVD?_PN^-*B#P^!E7d-&#tgY~;{{$NhY ze%yS)$$%~l6e+y3`c9Ao{=%!l&2ckWza5B;Q`k9z_1ox%K%)EGbf19?;l79N-=jO+ zco>80^x#dsfnfdn2n*xf$2=~^JQ~nq7-t3PP)XD+;Pa~NIjDxb2M@U-U(5yq#cZ<& zsIlTuvs=Dt9EYGkUVHSQyE&5^4$mc;u$m0B&P^CR4kk}RRQ{WZ;CgCiZ!W3t@#!qT z2%LlS!TNupXb0!hVHZ8X%aK-pI*Mr1*PtA!3I|uOLx;WN=kjA@?$Jdpk%!)S+a@QBkRoqCjI3@3Gn}Q zUip%~^6fbJSfzSRt5n5GD(Lc+O0fDaFc4w`?p4aG&ud#TH>!`nh^0+l>AHQTyVua9 z^FrIozTIE;Xd69W*UGhVh+^$Nbyig=$2{@ZGMH2Iz`NKP{qs(ui(`lC1s>P(oGM!M0Mt@i2Cr-Up}1 zs9@mL=ui=k|J7jtC55wgd;%Jt9FGxGkze1aE=FaZH(Koy2S(Ko72*E*pdg$&2nej$qa6xq(?6g|X zLVdFQ&=@DX_n;hk!<9u+2i6*J{9BB4_EnB>IO*fK$GiztC7mm(BBwK;DQgBr7qzWC z4)|d#f>_HGrWe_v0(+AIL*6(s%*5KM0u=lPToF@+vq# zh+BC$FP4hHF!n-7o)a8NZ(6lC?fM@G9l~=tf#RX|UL=m)L-97pnbUGQ$uwYm8y!~Q z&s=3DD$!)Fdtue?FP)7-+Sys3V$sBNNWtcI{46fYFinhs^%$cq3_G#wo#01S>V`l- z5?E|*Wq5P5V1Ww#r2-SO=GJ9(k{}MZk^^4GpPvv(rJ7$tK8yJPc8(UC+uEb}Q}#v4 z%>X|_{E-%Z<=W2WU6hh;JyuCQq8z)7_q}xfa!F%j@LJes+j0*I4=klZb~-n`unc<} zIM4|i&!93WXC^y|ZD_E}YUK+Ux9wOniwY__Ey!nvC!!#wW)m)V=~pR_$7GvV?ajMB zy#bRoi^%5wF&c|G)YhcIJSyR_*I-*%#0vvs$qtOJ+5j6+nszbQ0|#oi2uet6wWW~X z=1YB}cFHa`^GvWg#9yy4nGN_Tv)J4s=$l&Tm6r$`DZ;mxu5-D(nxV|cE7yH4kwUx= ztkaYtDuTsU?aaXR8z7B12y~WJ&!?=yqQqW2Pthi@INfwv>Ug{~ylM~c!YFbg1QuKR z1PH6QORH@xtc0QbY#_a<`CAcGuk2h_m%TP9Ws7&(`b7Zz=NF{E0U%DeiF-=P}cgKJx>J= zRK1@-MlX!xMMyl`V+F@C7fZ#=D$IyktHrqTJcgcB+$j&z@6$@)jq6vcg5xohq1C4{ zyKb#}zX#e~O!Qh95Tkxf{Z{L>P@4|HYhdIeRhxNr+D!b`f0DyPkG@8WuBpa8nqp*pGH?JTd1E#*XyUB zL;IsQL%lc-qg;P%M&W?nu0yu#mu=IR{CfRT7p+}`bM?z8QxCO`qrX}GvSaB$;SS}> z-w(aF#VCHB?=YBX{qp!tuk}kQSaj8P2Br(5Uxu`PxmIk%M86D*e%U5FuuOSe+40ZE zXNeOaH$+uj-w)k+t!e{5{eIi3;`*|>oU%bFTWrwA;sLPI2~ZV}&onK0p;bl5iyyFU zACeb=E_CtxKOg-al@~2lFEYLSV;nb^gUGmsK=lsmlsJ;S=kb73owHJ5v-*!(ofFUd zMV-S1%s~eF)j10bW%{|36!vIBftTVeMEpdrOX3W52mK9i6eg*^sVlj5O7u6}xuyP= zqW*?$INxd`YusCTIVVcJo>m6n8m+9))5>JEkrS)dYGX#0X0YnG&^(!1@UHI#+!eMtbo4BnE#Q zhI6nuh3m(y1u5LF^X7UosxHXjKeeD%qmG-oUZ-l6p_xNzj$Z~2iCEd_8<_4x3EEaR z*3spMq)Tgc8%9zw|Evn3YF3p*pA3pU>V)*kz7sd6!t|^| zpX`M(0QO0Bhu%&~pG@IGJ^EyleG)ITsXi$)C6dFg+)9SFy*>%xnm);06}1Bpsk|P@ zt(lT$yr|b9-$cff*;0$=Dkg6Kx8_P}n$(9WT%W+%-}c!l>2)|Kk6zcKdL1kRWT$!A z(H$=Rz3nv7tsi*G1Jj>GQK|f@^+D)w>nihCtPD=QbB$2x9@x}af{6JQYBX4Wa_27c zt5vAQ`jP3UW8Y3)+VKrAwzDS-_^&X)B(Ow)GX$Z);qE{H=E6_x-WM3N13%-*z8>;3 zEJ|>Vz!rY)g$Mj>lb_RT_P%ffkZT0k^x_%;cfI&1J>N99< z`dzW(wlcfLrnX5}`>I9zj#=BP1#AMj`jt%BA1wP}FX&J%cdMeUruqD@Z>55Cg%XHX z+Wk~4)%qkx<(-s3vCKX@wy3mY`F%uH2_vogDb}u3xaRk0JB3*~v|MOI|I0U9{kCuU5Y7+mu&Y*=}4+ zx{v$u?MxGBB!)!Hor#e`Eev6jb?D zJHP88K^5rgu#M7uupS&b4(oIlRISN$bUOJ%Zym|EJI^iVe&0G$zq98(Kakz|pL1n# zJYzQd;b=Piq3f#svaSE?+P?2mb8|CrCAt~(LR|jd70S%@n8lJA!Ua8C0Vod}+JMni zpBeJ4US-sc1wqFH4L6Rdh~;4D>QdqU#{QI?BQOK6#5!~eYXpX=ZhG;~h`P*2m!+&_ z;^_^X#Wl-!xmdh00<*`s@<&%#V~J5y9~Q5W;I0&~=!$48xex^`Cs|1$a~@UwUkn@S zx=Iu#d7V|G&YOT((NTBhD>z?LeH_j^)ZV%o_AuyDW;gP!NIp8r@q0D?8FgMIe836u z1c#dt?;<|ngnsHvh?f~3a6&vj?3O6{t0YABcN5}u%C-{m7t$pg zm3xJYfiB|Hwr+&ztX8h~Axc$RDWc47iYPOl22lCl9c)YPX)UY)a3s3}%iw1;-mY

x3&v7Rd0bQ(ZD(5lCJM3r^qra~NgDF{7wCeK(xEaK}rr zFd0+*W1PVE`y2F6s2hHG7VBzClt5qtf9OpCC}k()AJYoq5Az4gDY<`>z!&-&kGx7g z5q$l4y!_ zj!?)vd+zgc`zV)|pplz!y*^mL`crI)*D?nMD%OQEt5~__&k{wy4uAonB@PF9lyYf_ z$C>GxS)x`#SAL0y)9LD2;;CJ_B1;@s=?X6Kv^ibI5|8W9)wRTpBe<3VOL^s*2`up- zFJSq=5>Kha)e~6a=}x#JfhC?RhbtIZ;)!v%jKC5X)Zpq0Eb-(KB4Xi(U611qo^2iYi9_z5UG=|??-x3c~=YNT&T^jdX+>tIFBa za_+DeH+ji?L4qaU%^G!PGc(-W3a}Q3-5@$;2tx^u0_zlvUE6(_gL@ali@B(W?)qgw zL!8e`7E{~cEYLC=>P(!8K3huA=H&E@JsP;BNvo`I`<#nBjOMj2@yLJAeuJga6&f5?% zdD15+BF``9@3yc_dvaJu@3|lmSK0d zd#vTby_#k0o~!nk?`(zH4AYE8AAX$+9y0@Ug5cKVfTtpNKWYPt@APPt4lPPuv>j zCxNS2SvxU5H;u#kJnuX_VUX5;T0DG)zBMe=C$kA>DdCV*v0m*Ml|Fn({qU^qRb`b z8tO6Qs79PAzZgX4Nqc_*5^jRupPutMQ4~trVpTVyf0603RE5Ix0s-`Q3y7&(Bwqloudehgx&ZlUg6FCP!L>lz5#%eSTm1KU6?tnXhU=J z9EQ`3EIZRnaD<(5A3SDw8D`#tk=F&ouY*nfVhk|x^BSb)5_ExHpJxgOxt*QeW(26u zqL4r~#T~en96ETxCFw8{-b@78ld~hI_L^a2;i6S82fNtebR-g&`CckGeF-3`(D;@V z4ts1eX`E@>ZuR#D`q-45ofR*Tv<5Ak@k$gKJYt)%d1XOz? zo_)a8OJ)>>7r~&x^)xu-f=E|@NoNKbb(SL8GJX zwUR)wug*L88oIgm1c9O>^Xk2Z=AyMf6>tt_D|nT^4qe?1^sIF8tjy&o5k?9-mv!xY z)vjU82yBngHh@YnlCACSqvbEqiBaGBwJ!o{RYB$uk4qu@Be@E9L{Pi@Z%OPIqv z^RYg7Z|zA6AFY0#58hY1Na4do$>V%*r}lJ(4+1=~1y@8f>Q4q}{A{cC93Y7~OFkR5 z*{c046Tkyh3=J;}9SS30&zPtI3QkqPs{NrC&+_DkCv+Q6MSKX|r{JS`=Oy4ByTv4@ex&`DVpQu)WZTr`!Ed+9jv`xplF|`wV<17w|yOyvd})WE$DFVt%A$;nQwv7 zJ~zjipL%cgiAHW*T}}Tp;Hbeu&Oy$Bk1%JnJA@l%^8k?L5wS*t(ah#09-yfJ&-MV% zSAa`Bz>)$~J-`bT;5i=Pg$hvf054L2LmuEy6yS0X@TUrJg$H=C0{o!|c*$D8OBLXG z9>&WQ;7Skhas@a@K#q^3y+Q#lBOp(}VFmaj0)`2=3NDNVU_uhqGMjaNX0L%A5eyCr z3eLHek{Wzw^^2&p*<{PPA;^l&=SE^1`hwVy zZV+437sMuYgV?UVAU3WW#1{4iv6>ZV;Q*4Z&GLxMuq*bwDzK_MS(h%&H;HDar8i1J85 zVTY8FL%-Ukn)nI{V-H#%_9h7z9R&=fu_ZE3p$J!P&skU@o`lO0 z8NGC|7jo{Jko^d8mMG0dqz}$6QILx?hnupcDL-A%ooAOQQADgDj3T7TAGj$qn*4#A zLZT@@AE3#LC5i@-4+x_yXz~YccK#-R;AU@c@&|5q?)Su4BYgK1P|Z4F2>#=~4i7??Z90S=Ur04fl8I&l4ePtmYv`%^zU7S0_;1?)e(IT>@o(AyzW=Pw z_&)?ZEE-*{e74{2rAtT9-`DO5Bce1kGM%572GqMqE{MTSL^x_p8TsXQkyAT}JdR)y z$8xTD1^E%~Z0(2w4jAx>Ca7Lg<@sbIa50&JW7FROOOH*pqi~B5^Yw?qt>{AnY&~Cy z*KWVxLM8u5XY!$*6NY%gtRpsRsV>H+hO7J3ld+t`khzhv-4`>)hw%uhvhxeQJZ^=J z@YT0mr?haWQTJG5<-#|Rlp*p?bnIZ@WMBNP{`lRDzw#DRp_Rtn*4X1rL+tog{|!l* z)m!jO{md;(FZ#1rmS?d`yJflN4_fJ$5MWH>TkF!ewml7q zR2YII;aHyCNDF)fx|y}Z!)Wl^5LSvCg9C?N09ULyY}VcuZ@~vL8m=FLvS8%9dOk4C z!sk>SLwuJ~-i>t7+}q*AMz?8LBT%g6d#5 zqf{}s&{rC@*g0L7HD1L&CiQ?>PzcFbV;K;N`oe!AJI#axA|A8s%(6^Dnl`0b5lXv+R* zx|Z)!c1w)_mtY)}S0x)W3SUJ{MvsO@o%qqj(THJ5f?v&yfnk-M$8aFp69_#WBrvcd z)02^Bs$c`v2y8=InO0g(T&G6SQT)-UPAi-^8ikchG6Su8rN zYsZWNGy*MiLV<%`Ix3<{W)AO^?cl3(&dzeknlwUaQAtixGlN#)9HbGlaA*Y+PMH2w z*;unua;ygqayhf`9U%3Xq5i3`MS+0Yg=X=4rAE)psfzlOzWELMS;)IcjopUnK)ht; z3gSgs$!l3%W<0q@>zSr?Z2`~L!o)X@&IDmS^A zVgKrdAJvhtWW-2jH4c-PZY%&9%dGY(AfuU8W{Tc`xwRR<+gtL_kQaIz9|MAs`+4J& z@;xIrUxisv%g8-5ccpUWuLrs|)6Ebkm#=mS%4GFNNY}`1ul`)VTh+Jvzmgm4&pe@q z+m2ayAF82DwFL{GfEz2nv?uyOWE%-7u^Zm0?1>ryDC~hOW$w)}vwvc%CE4%5`XSM+ zt1}Zirge3$=iV;73Fwg5%)m1c4_iaFk>|$AVU9`-MggP#Ge083RL3`k*f8a1g>Uxt zR1FUIPu*cw{|n_btV6e~I332x+&8ZMcgePYe zU~p=4xbOqC(l}NEeHoBi=Gr`RC8>fW{cZ3hLMd&B~?)Qu)R z+De0h%=|Yx2YKnc_>%G%B)w`SQ7tmS#LRW5n;S4VwLy7c61=_fT5b3LiF*(DsH*&J z{M>tI?qnu~OeRbcNF@Y93L&8dL_$>rL}?)jRb=TBO;Cg|5G+_$P!JIj5dk}hb#?7) zi49qM*+oznyVzYj{-5u2?vzm6eSh!!{@;Ayne&|bJ-yy@WHYTHh!LJXIfrLbyXyRP z(!Ln4EH!*M2;T{`zSUX!cg_XOgJ+nJDg`~9OxfgByYU(eD==&V|%crXHkD)MBnS^pF;g}BKq-;|GlZ-ts>yb>D>+OTDRK7oFv3J2l}mJoCqCBPo!nH z3Z!N4@_yQa_hlW%A0!7JkPPRhG^tO3db8^!G(OV6AwU@(152#Dne{}HAna``Da7>! zu;R%n>W*i$v2DCq+G02Qi{g5bE}V&GPFbW)DjqM^T{-cRAwG0Yts3Iu-XeHKOpjGu zAUcV$I!GRbf?`$dg=JqOM>^bk9LDxJT`#+6 zNNyAY)HEyoT+2EcRH*^3HmA58_0_KQU0Qv#fkJQ-$Fae}FZ9?(#!+SPP4>BRy7`K6 z{*;wICt2^-&sI_vsLeEi_LMgA9Q|Mp$fRT(MijEdy_9n~>gL+8$lLRUUHKZ@3 zLOMekH_DpJkNABHax`S3kuHALSFG@G7?Xco8c0-Z0Yg|w% z9)g(}lTOo~c0=EYA&g`+Q~#}0q~dmV!|;evaf012Cc<>U!SWG0#+qJ{-MzitP#3Y( zI-pz|V`Gj=pf4%o6zZsU!@03er3=?p$XNrz?A4X7Ge(qYTDcm-*%x5dw9s8cY}1)@ zJuiAL8WpXK*l}q)c+3jMdU7Tfi+sVNt__zWc>RQWteR^T(RrIVIJZ)1Q3 z^qBKDG3UJPJ4mJTFm~Ri9cn?2HgBVM^zEqgHu_$}=tbsj-y@)-%-bH+f#7{PEo-L9 zVYfU!h*c69k=IWE@Sc0j9%Py)zGIpfjiUC#icIs8FzAk%=8+y_jwchc$QC3yWnS7^6rxoES0ybzn#mQyY@<2wcHvt^{rl5Y8-m%s4X-f#zXt zxIAS^wi$=dD{2e>jd<4ha4;p_^2T|ws>Z|Oae8TTGZI^bLSk##1%nkh=)f>(X1$^t zC3j(Z>r^M5Yt~Ok2v!=mg*9Q;$~EeU-%;k>YYJsP9LbYq&T5ufaLAJdgT^1=Un5!@ zUuX}cDZ*hcS+&Dz!BNeTmH8sqsPLj#jMKSC>X|NW9m}$2llWi%6FiR)95|D$7n-mI zqzGZFzliBv$DNQ;EEb3jtNzzmU$AH4L$m4_9f~#jJ(i$zh_Q!frK5u2WmnW%nH$Rd z8*iH9EqI^vm!Sde2P&!Yj8V#awy zKIi2{l#4LMgF<)-FuY!hO#HhPAN*o&#^0Bn-&dU9SDoM2VtPS+3*+UK z-Rf8^9=Ov-?Ftj**j-^i$cQ+|e&>3~D1Z123HxUy&Pp*F9ieyeofx z*c(2CL`p{~0y;{O*-?tPIx=QRqK0<|1syfrIBMF|>Uy8ZsFOfsbgLN{k;ot3-h*>Q zR;hs}aWE)C_Q8@Qj6|5Id5?0fW7YSCq;l|0;$Q_grq*AM;}u&iOhzOoR$5pGOJE{g zYcE9SC|+Rbe+(}qPOZHVouhbhq!jcNQbD3tXfGPxm2No|C2oloOyi-! zt%!M0N=elgD=n;*7S<|?b#0TBnKy@60Szb1iwK(OqSxIz!=?!3rbJ8+T~b&@7d1Y(_d z9XEA}9=!1FNlV>|2l*tvG09xjASaJInN4p)@YQqJ&bZY&h?FJmDZ3Rb*e$*x_XK1y z_7iy2@LCY z|G*D=@j??4iE%>?&gsVNEMEG`#t% z$ek^6y9=>Di560J(QFH^=hP1ePb=DN9tt#JQOZ@~4Xc)jVqZi{R7=oPrXC7}#3B`< ziKJm)b0U5*F~q8o#3It+FiK4lBWo*045Gj&M1D+7y5lINSSpM|J|58zC8~<5a6hEi zg>$1}#IsYgyiSKSGn2z`_ z^!+N2Wk}4;`XPeOV`Ie$*p#8*`eCtm;ke{;vq^sIw_=L5Z-&n~pRf{zi02dRCD{ED zuRKo9>>B`IL?aFwl0*B-I%$oDsVCxOkCuz&O4fwi5Ng;uo)eA{Ej1*en@w25 zb@iEpTA`H%0qhaNk=gV<3`)>$h=oEgGO-vrN+LP>PA@r=?b2!MJZq+m8YF@WDsPUm zBz>`(tP*>xz^t%(xbma>)cO;ZADyLE&sSw=DUct}sDjV+ewc~f&v(BIml0Ey!e^HC za1pe01Y9AJ2^CJ{H4iUs@YETnWEE(?ki8$nCdcg3Kc6Wg<4@OpjC2z)d%?wq!E(zg z4fobo=wEQ-;^RYQZj^M4!JP@fhdT5R@RQ_Nz;Sxm;L-B~Vj2udi1_(P|9paU0JQS%6@93aYwD=BZ&?+8+7Fz1)T9KZpMr7*&ZNPJjbN`c&pz(WOu@r<%k< zDb|E3UaXa6xGD~2C2g?bD`Aw|jZ@*n60{2}FjGY2Qz;`+>=6jEpFIVm0Ey`>lB-)&8@$ zI=ygC)Y*TIi8>b5j+I{F#FlyTB)E;Hb(#~Wvg3~BI(s@-wOSiTlWOX;*+n`tv#0A{ zXh=BIjvmf{qxj}?65d`j^*@Gocwh|cZiI!l4s~DBBO@Fql+L54NKX2Hh*VDU^l3*C zG{F%xP6R!g1$-69BII2&b($k7!qVv?euh)ZQA|TY3v~0SM~*6gzw@BocJBYvNX(sD z1PNGv{xBl22Vv#T%dzO4aJIt9i!3KE`h5=LW5r}aPUZC0X3_GsAg3FWtyMlob74VF z;o*kS99WQ(d$=K2eWZwB)n@_XbC|~gT=mNA^Sfaz+0`oC0LGWv*j}>$`G1%x4xDIH z&zc3{%xBD)dO9ACQlxg2BBP@ec^qY=s4yI@iNYfFss#xd7fIi6S!Y>kEl@NKk6zrP z$vE{G#XXvq$0+Wx={a>8K2!cLN(z=FO3(rY3vUCm{Oh?iuCG13%zSl3nTf{f$TAa+ z&r!-uG%80aGqKS)TB&JOERHNSvBlyjr6#sq99e2`I8T?FIFuS+@wjjaK#g$9f|P$p z`9CW+NO3HvAjL69_7!0rnO!l)D^e3Ir-DZMAl;2wS}F?%hPpP~9O*FsPpPpIco2ct zSKz*OSu`ZU_NrafZrSHBD5}E@Vv&KNjujF!tSWyd+I_WEyo%jJEaQZde7A{LHWzR- zA@ySr+MkXrOX4tW-V4@~u`h*_3F1)+_SEIqtjA|`cpzvdR(DFRG&$T+3hzqsm}e1u z?F(0YRWo2GxtnlrpQ{soa^%<#*NMM)jt6D8o9qXsE6RPo!Y>?{~)+Px0e;=gD{kejT4~ z_f0KL1Mz!uQDK^=;%iW^_&sfNaURsTMHjdG$~U6%GpDBIWXA?S4WB&La{VAK*lm~X zt;Kh592sv$0!Vvqss8Y@9oic!XAHBZ5Zljo{cZ9&P{Ttg81*+niScQitLY?M9e}eR zsg3g_H%zUeiS8TZye{V7{L^WE4-E+`d0Ga75YwonuM&LV>Ss}JqJ%UKYmhVRE{%n zA@LPk2=c@4{6pU#uJLbZV4~tr z6Q3)d8!sqsMT-MiR=53rV`+yY_34 z^k$PtZF^$;Ly3xE6E~Yp5)V5rCYY!d31;GEyfIF1*1;gGoEYOt_YSCJ^hMjy%CHUb z1Ihb{#Gk8yF=AIubQYS#e`}pAIf-Marsy1UZNxl7vdFcr9%acsnm&rHjpl8}71;(`q)j6n5~Pk<%& zIi=l-Pk=8;a6SQ+!rA92okD-T096LRlZB$4 zPFc@JsI?dRj6SPI#l3cstPfy+n15-_IQHdAZ~hV3uK6cmcJt4`oaE+TaE9F*}N;eRMzYa(037#_Yax<05g*Z?MagS_o)t2d1|du{cm z^A@kIUY|(RunISUVS%LabqO~C&UCxU4ATxJ)8yJN$+QN!M~itT_j5rJPfzCTP-o)( zXzfWNJV|HR`r8q?*G;}1X=wCPkw7PR-lopPvW|9=1Siz>+!~|XCal}><`~`NueL{v!Arp2!t~Y;Y9cGa1vJU<24~n*~j~Nv}k1T+dan%{ezDbd{`fp4LvnR?C1@> z_eExRc1>%cvk)}oZC9h?klmW*fbu($+aV7Y$Qkd#J~GN_nukG1TJ3!KKQVy8khgLL zEcw0j{q8y`OT0owR-2&261FeVI|m=)bWgVI`U?<+!e&4quDG}rw=GYMDc+q5U-pclST~&*u<(Kmy?m`@RmW3f1d&pXSXQ|5= z@L@gFUI={|Hf#)Tte=hy1f@M}Y#-g8BuFrAA?FfNvC>? z#3MzW(2x*8f816iN<$%Cd(-1|W9|25G<4Hi?Yv>F_WJvNzgLH0H&nt#VJGC9PtISZ z>#Gg0N#u8JEJR80AzSrrcic#-*l#xsh;iYLxJcXRcZXeI>LU#V_mRX!SrF@^41?z% zbgZk2kbC^fNT*I!06d(BeG%g>P9AQ+xKLWO`fH$w$G15ceByjk+{StwLbOYK9!Dix zGBPlY3zof~CWF6xDb@JxD8D+-WzC1H7PcO(G$*cgbKN zO7Cnwo+A^~1rN`i<9lH_TF00+twdl}{T;BLamWbe!a8zI9Bu5|9pxVO zw2fOK1uATq-X;_$@5Mn0t}daIAPT78+c*^M^w!29z<{qU)1<(!E-zC1{9ch0;NTyRz#D!!Yh3l2WUL)X39{$8l{I6mH}^ zH}Y~Bx!{Y(4s)b!!ha9`C*XfE-bg|_5)8PD@q(F4e&ZYVJgJ<4yQ>t9Axnq1u``N7 zvRUkDyd9}1XRVlI>#z%>qk)p3Lk!q1Pv`(X7trGnD?Ir{%Q(vgiT)S$V z--&1jpiC4)w;1Al3k~HLhKZiiHsTte$hbmyYsFRE(G}+jyRL(YpAR_iv{E#Hu;;y-R{QWISz9#=h1$=mo?%vFyCc@z*=sVSwB zrKg8iFR>u=O9Fn(zFmAm%f(lY>dH=RMcJe#7FT!|V(+uPbWL#MKHN>9wVT0c91^t* zD1Y=l0m~JVa#&aKx%k{sn7jCxa~(b&?BKWAl6buQv2|Nw$6=8~TdIw>gJu74d3Blu zbppoeM2l&-rRkNqiKVKMUL{DcG<=_AO`OG5zja;KQLs855!pury~*T2T7mnb+LI$pIkaUh>PT^ z@k~{nG+yZAq>+|B+KnN%2k5G+!Q+k2D9&5)fr|&s{Bc2V8g0nX+=HQ9`b4pc`@a8- zff(Nc1IAU{SUg2S827&&Ld5^eTKRvoi_J4)QY(^=g2)g8k&&3##R0^`rkr>*}yt7C=9{IoJF}=7GyAJm_4MD}18 zBoL1m0d@AoN3!RC9zyHviTK|tdwh~TJ|}w+lXzrLqCcK;#!ECJ&cfM~DA|Ju5L#tV zB*v0GKFOZ{4UePQ6Y+MJZf|j-Hazz)yaUxs_j@Is^{+lmhkXzk3EDXM#`YKr=RDnq z{I(wBcI`*uuK$4#(k8{L1-ePX0<26FaA)|&(g_i@G)vMH$gi{kri-Iw{K!aHbg{3% zU+mZL7yCDMVje{N~K#LO*PV&t{ z4Ha$UFxT1~8z*7&7$1_{whqP<5+B_h$Ct*t8}CKRhP>305n7*b5h*tt^*$d%>5QYX zs=;xz7n?M&h51avF$d6bGJy8V06I>7V+YXK?SX55T&Vhlt`-Vo#@3h*KGV7U@7V z1`7g-)WuXxW-shd>!mx?9yq$B6;tLMafvdaS4aPk%7 z$_ESg0L#gwvhRvKX{XDV8;&M+HQH5thts1$)7C3)cG`3wc0azbJ!J`^>#=J&&Er{+ zWBQLzy^i!>fB*I0`X4?Nj~&ZO)RFpySjLs@OOmsNV2z>|OyswR{I*+$Po}vRDeL0s zEBJ@0~ zgd3$zLJ^9r1-y^nGSvMXUn7=5eT5Tc6(}`cw(>-*=(@Mnu)NIK%1emD*BGh6wn10p z0|>ClYFLfUfcjfdu@kL^TSR>`E4kJ1moNz)G6L>6vwjOAWfwjSXSnfa8UkB243!2q zSL1~T%>1!;S<=bRWgpD$QQ!TZjZHUU6kdwONK7s8al%9 zcBbYGti?KpQd=?B!fLSk1ZwDx>6#8aeLy>C`SBgmd`J-RRdxcEm7S78Pn1;PCr&GhTNIHLfVJCQ@YkH@K)REyWLBFr0JDw*l?kLM1SCph-2!N3Q zTD?YGI%fM^w!YOE9V>ue-QP-b+#4>-4pzlml`kUM@!E26 zroYWmt+s*HHvTqN#s12zuquM52K$#NF6@Z?CsB{zN}+2t4QOGTSoj7ZPoU?8P9 z2STM>QIg`vySR<_wGt==p+MiN*jrqMw{nZq{dh_DFgpR*3HSq6#lMQ%H?+Wa7t1); zV+%3V@afPz_4`rQ`d)(9vZdl1BO$e#sD^G)0ZiDC_E6uW@1K#Ks-6=KM<4112JfX& z>V3oad073U5pS6Z7*ZOYWHj)o)Jmz}wt5_evYdUpv=IU6iGN=%YiG%bL!^ONj@lw? zGSbMDSeRD2iu7by&$}@_krkLg8@&#IE2+7%5S}RqFxi!vGR{V{;fMfN2E(I6rd0YA zzgJI5FnldR{gov*-|9K#=&zyFQx_iw@V@%N7-gW!i#ARe6hq4s#O3{R*r4oN*l_Fh zKrhyVxZsCriC!4Q)`4CbWP2B$CQB<9z|qeNh@$m2TI&KhXDRI_yi4*YTlI-z2@fKd zCi+uKy{=&BE5rklcQt(m1(`pkq@$~#N!CWv{K;w4Ww0=>POp_v*wB}BqnqRPc7;2-$<3FVMc+w;*LF3n)V?nII_HnTRTMQcX?S_v0I8bg2q*RsGZ?E5}7(k2Q?nC!>qJi z8=y}MK`xA>>4AGA4%uv$Ik&ApPpSVCobf?zV6g0YzZ=Lq7xRz zF>dt$;)C^h?CoPb)%vWA^wn|&`LAe$u@TIx7n z`&~G?!y%8fPuQbVJ*0io1DOFxXtNOgt=y(I2*l#WZ-t}Lc9ytszCFRw7y}I~@t=wx zZP>;kW1*84k1OT0nb^bGb4@Vt6EhP_Yq)r(G``_<+(zddEB9DJ1I3lLj!WJ_>PyAW@uJ-%7%WGL5JgT7 z&YG*&BQj7kyPFpW?gztI_DyIl8CvOD%VV~qB3o}iVjV5mu&7Sb*Nw9SB5 zyBq$j^|1yozBrqL?(jJcH++|fg!S7y=QrHg$&vVk^|QF67rq-Zfm_pYykZf(2y2HV z7lssTctdr#c6jmK^8phWs)8}ngR-@SkdlsWripHqid$>y=@`C~#*IP|cUG{t?a{2& zh5E+WI+_hUY#%xfc5t1m)mV)Jz&-w_q=%IC)*DcVFq^~NS7PCOSi$^dBGlpXe(2*z zVo@`!1Z z>6GsRDCf5-yq!g7B6s`ephIhARuOfGaq3|Xu?rsK5IagDVp9_K6XX-L2w6P`hc495@cZ0lu=BRQf6v6mPZhbOaV)GWG>>5u^U5zD&EyH(vdrKEN0?Cm~ z>;W-$2~gM7xY5n~u*iVpn2x^0(8j)<6w8_bQeMQ0E8sR3o-qNbz?lNGNh&}$?AKso zOEtcfq3J7}r()-OS8>yS5$xDj!FqX}=>LJhZw=Q3kv{0=9hyAog?B`r#%g;JlCTlU z8nz}C2u+dHEhi0`gmuwG?0Xe*<3B|R!i9A!|Al5GM>d(5G?8(g6)8#+0H)%ftam7igiujnzWGVkcrr zEz=CzabCL?4^!cVyJ8Evh4h?-$= ziT(O>WGpDAW)^)bmOa?ROfq#i^aNiCil_Jvv6i~B+>FQ87%tYfoNon)gxmDVa?20M zUgtzNUNnLv^;m7R7oGw0kWjSVBO*cT_}oi|7i4 z?wE+KPjrRisU=`Z5T>Hgvb-dwLS_9#-vz8xQj$u*GN55$zLJ3ERQ|#x3#Wy*VBLc9 zW9cQmv9~aho9q!RAyHnk^+*oNN%jM#hLn#C8$h;}OMH$7%FbX%pP)B@CW= zA>SK@PDDmyZ0Jj9INOKWjVKen%g%y3eM9`cIKoO*{_VHP-#@Afm&1;}xGdJ<3izZg zzPBXxjuYSe>R1+Uk67$7>GCJOFWhk~i*JT4noZ|QJk}NV2!|ZKxL%v3{ef9}j9I5x ziNB5dzxWUQ7qeseFJ{N`KPC|albp1P(UatE$b*5|gx1T2L$J~;auK<3BwBH%<>B^^ zi`Y;5uk4SH*iS#I{n+$~39CLVZzps!!(!7+q}F+lj}1DZp+6h4XV`U~nrkkrLB(r0 zDHZ2dW&Kp!;xIF#;thTnl2LK5H~J*X(Ze>I7QsiRh4vwi1=CwNBAg~MKEy@Ubbh{`kkr*}fK|U5l^_jYtgJu;ozL`LAt_u~!(bV}4fi@faKicmED!Qy zowS<5=dhQE9uxg)M#Vl8%|PMn(n3h=O_WUh%g?y~qQ)n0oKuzm=C(7B_r2L=Kz_I) zM5<1Hq{I)F7DZwz_0d*MsE@dm66;+TBG6?GFxc314JIFjN>-&DZM5RC+VPI|!q(ae ztvuNn_9U^DClT#{tCcTNeRnHwt_^z=K*CT)ElgD3)5@Qyeq4@sm6QxQ8XiPC&Cl=k z;&G#1_In4|b@{*N{r2y(-~H(^Z(U9PgY&#UZJD+IF~40`Q+ERnPPek`iCUI}7E#+C zZ9Bb{?YJ1*?pWJ#+I9wN^!)s+x*5mqKjq8EAjnr2J~!@fw;ZYxL0+k!>e-WVLseLi zcC9Q!kg#P660z+F(!P~#2okX^f<$aPf@Grkn4b?RR$aZ|?kWhPWqOe*l9%({{OZuZ zBZ6eLvJ64OmMKWYwj)S(E87qxVp{}>*meZb{eoOkTZwX*!F5%i{)xxcPW&t~K@@Vz z5Q`Y-#&A=F{*|kYym579r5U{LNS`jn=|8_4y83pg@akwr#m6Qdq7>J1CTJN}Q!PI} zvWtA2xfLHAU?P2(&PFT(LUZ*T*awznf@!5*=oJ@Yq5e4ZxZfd zg_;{aew0zQgyVJ*NLRZ6M&4vB;PTM}87o=9>v1gkJj=9syl0sMyaZqBby;OU?AII{>0$A%{b!d_a|a4FIj7e8n1$GF2;6_Sn>PU zZFbu0XbA0MLTG=i5aN#vfm$&kXdVe6Ga5oxObFS>3L)Xh5U3Rsg60Up?`-e+Q>*U5 zluGYoU?iV?NOfP=l^1PUf!xaOJJ?bb6DpO%JX2f zm0SOd@;onEpx$wqGvBv-3 z$hyJR%Y`>|(jnl64pK&Y#PKJdGH<|l*pS)h^~^bz&>M~_bd@~ITy&0p$WNwB!DcBN zY0Xj)(wimI(GW>HgiSF$CUt7aSC55o`dcQRh0pUGU{HT{cb-fp#RSQTm3PS^+t%Os znN6Z!*at)Fd0RLVbyd8h+c}&?hyQI+y&Hazfvrs9e0D?DWSLxuD-O+LM&))xBR*%; z{;!_HMV(frk+;6|sJg3;;Zpd700)g7KFE-x5BN*K|8pw3cI(=+Tki@S1d}ED`M`B} zd@Fw$6ahn(;5UEl!iG5uW;Ke9k~Pqq4D<10Rm~Ts!qGRCf8zL)hTwNLc*g>OmbB`^fuzZ_$yN4G@O(v zCH6Pr_d)zGh8o_`97T+87>QjZoD(x!{p$bTZ!0TBFo~E>?3vOb#a2~N`_$QkzJAg;&}S2q&P4*$kUPrE>L78GO)IgmJoxQKWK@pfVhaX;}#V%kD+RY4pbyq$j_5x}TxAeD5(f~giIdNf5F3dXoFnnQk@~k2U!u=>i-Rxr2e&N_o5{A{i3Il!oO4PJ9jra&*F0>tO@6e zJbxhu7EA1=5$hL={vP6*C8FL*Y$3iw{CtUo{&Dlhu$SRyN8GQEr9QBC)n_m(!yNz;p4<2g*c~?pip>lo z-*AI1L%YgEfA%RFNdY@VxDl3fTkL^Y|4X>%>uFcng( z=;wChudo?Xt7-Nm*r{-}P8qq73@j0B1AY0G9_Iu#uJ(=AgAB#U`_&i z0N=tP1<{sCVR=0ba@fqAdA^v^8@(uDqejR_OmHzy$|0F39~GT?R)s@ zWCd6dYzSCanso$Qr~ZW#7h+aPvwtC#CGIC!f5kIB0ylv)k54E3{`Kz0q;Z(x$QTP!IrE@J}yyb}ypHW+7-Jp-1h zY~wQ8eA#*rE^T8C&AzaHMCi7$iQ%}5zl{&*E`F@)5i(y4#beYi`QAoxsOn)`rRjZ7CWNTfLf2)lCro>^V zOKQLs#wb(LUU>>04{z;NLJ%-#U|5v-nOpSy0L&1GaifGK0GaTS@zE%k4$ zajhvOBo)lBt~NF^ye{tEZs}{TH<>G7Szuck!Z^{>U)OLhs^CiRl>9^9;vCvnSY|$ zH(=|GDswiOij#Uf(40rsHcrxWpm_$}<;6+KI>B5(vkI{7#tG&^nhlSWvNX~>pJp{+ zJHRd>J0nh{9%XK(&8E1|P*+Bo+nHt;gJINY-c6eu;_{7-s>XbfalbKc4@Nr^&8O+^ zuW?e3CYmj@xd-f7qsjb|>G=_)E>+ijohjg$?bv>R8 zm9*~S72IliT#{qmyi#_zn(?$b5bS_)lbKALFeXU1)ZhS6b6ByTN|5CW7H0;r^>g$kaoS}rKaXv>tTjV=i~bsZB=J$lM9KZPL7wps>Hg1A&ifg z7@%2hAGeZ$N9^%es^7(s*gJ`dd$vT^%p+8*c5U)HlaVK8&(nC40bO z|0H_`?!@eUx_dSLcC(O7O2LQmET7f^rpvGKdpr-T@zz1wj8BlZr^fo8u}w>mHnP?_ zL_doXq>Y>eCgrRLm>bM+OL`6g^Q)PtVbN|3a^&N`K5H-e$;g2_DNPE1$aYNvG?*}Y^>S_{Y?AbZYg zAbXhXb?aQR$I13vOUa%hJ76`FJxlhfwUTT%+1J)(WG|8ZWL-h_8rg5wI!LC}TM3fy zhHC@m{4_!8pY7U2b|^tgdAw^InLkll=y=z5va&>JG2>k~kyU|pR0*yfWTO)2yEBa> z*F9vXB}zM(;<}e?Rw8>}*8^a9%P?_|XQ`R)ddw|(dnrN~q9(eYqst!4mJtwU9z_m-@{1YG}qVk11yteU#(|n?Yu^kv^csbrxBmjkL7S zxfYS-J8TJAk2cbiyx>|!Hn7c@Hg2`sbrG0S3)+099x&c=U2aSIEW?MwSa)=9wJpSU zHJBT08{J(4n?2M(_pM~NBlI3>gnK91{cR-fl6 zo13it?h|NsAI<*d9zoWU{A|+0>Qnb@kCc#ClHarZ>I?T-G&_(iW%nyEoNra%B5Ra_y_g#`*i8uH@S5$!7R(P{~%V zy@{;eFH(24?<8C7pYNWey4m-To$r^HsE_>=*%f{%FGKAY$gcPAfGor8m%x;I6yf2H zD*IKMy$zhn3+Tt0R_wJ_ z%)K<$X0KLkR)keIy8=E{-Rvi36N`aZzX+57ucG<};?2Z60%g#@m+D7}&k%PLUnagz ze4F@B;yc6-frgr&Iv3bTY$Bdd{4>y2SEeoiUQ67T>O(5sAKU_bEO;aEnV{(G0aiD+ zPkRzrMC?O6nOIGnL2LlxuJ1H)btQ2laZB2((Akmp7Vyrre*zyMKAyH;S?Vw8KHy`- z7t%|hel5M+uvOa(NtrZa2C+lNP-Ap+=ZrDHO5#bxI^sOyQsOG&X5ud5K;7kN5=f4dQ#mZ;5uM_@7412imHP*oP=7W~=^G4~@}ZN%iH#4Vgnt zTWz8GR^l#Txq2{j4BTpewEahE`b_3|cTFvkml>o3bUWZP_-|H)s2R zcVs7HbpCnvV4&0^LrI-7l+-dqNi8#&l7?!V<5RuViNtY0H*LD<3cWCNf@%@aP?zP* zf^U-khPs+M8*_%DJKdZ!&#G?z9y-;{KNGE7QA?~1N@b|_xiWLf$-M}e4^u;R%^eQY zp1GGnr(dqjhXxae19cq3Zm*{O>xkQkw-N6m-cNjl_&4Iy^!Wwi%fvT`|D@?Y;(Nr8 zfZGEkZ~24$%yu?i?u^LKX}C=CpQJ7U8?^?oDcM?bMp49vCUE%$2Lo89NR3Vacr}c zM(qz;MU)d;y^%KrE9r0Nodn#MHwt(lZ!GZByc*zFd6R(>if!N0LGtd-4p*jBH$MUl z>pa_G19V;_zDe9me4qF!FkI%Py{&G3KR;R~rR?bvg)NzmlJD(0N?GbebrG?&qm%(% zx^%g^Ia|txlxjl_=;%|Un@14G0xfkZac#$P!&28$y``f!V5wW_+qUdHbdP312})X^ zpcB;67Z^@Dv@ehn*twtx`o#sgh3KO?Mvbbb{!Kkj@8>O-B~ zsnsmE)hxHwEVtDxx7E#EibVci!~w+N#PP%_#5u%9;&S3D;wIv)#QTZQ5ML)oTXNka z^it23N=?~I{F-QV6`cSvn^;8bNgPZZPn<(sO1y-)fp`<~FT`hnsCQj6+ZyWKu6e)@ zx=NcVwNlq#OXYVPZd$5WH`+uE)g?jaRCV*D3K^@MMO;a|7U))6(uNwn)ITaj+Ji*1 zyZBJuUHXOJy7vfTy4*w1BHBbfHak|UmD6Kj&{my$NZxfL_Us{L<5>3l(zG8@w|LWg z97Jm~vxoFz(OQod40haV4NTqYYT|Xon~1v*mRoh}DRyK8)k|FowXHVv+zxp*6K^Em zM!bvoAThR2*RG!IDZTb{J?{!uH~*9PIq_#=Qm?y$;oQ%M+F+}To~W0k-q2p}L1zlp z=j8nyi0!+}+3%MtX=%%qw6x_)T3So3>6Mg*^YOh>(oiFMVFgre?e!6GN3XZw=iM;H zoVE84$nnKMLtWHc+9ko(X&XCA%&dGM&1M?f?2E%AAL*}cCF5ILt!e2ctTLiI9YGwfUHJmQ7Ki-?!>mGSxIRBxfp z+o`^bct3R>CAJV>B)-=7NBF-VcDkz%iC+`gk*y-l#C+X%T#+Ll-elqSYAu@@ zIWm&GI(=;Oj|c_%d%Q@c(@Ljp`tg#US;P*&(ai&omwseE)P`CN)V_u1Ux$wWF%3IA z{biiv?eEG6k6_cOBmF6M6#ENy>hH@yJ3w_$VqfC%!~w*?#1rUt1o2ejcw!w*rx0aK z=vFc&M2t|X-D*1ZXA)&ZIC>Ny+7Itnm$Ii zFH*gawQnEQpVHO88KUmHjR7aat$Tok5Xd0SxqKn*gvb1!!k|80S`>M^Jc>dHaG5c+Y{A4c4keKK@LQ$1mj4^qt?B=Kz; zG^QQGg8t~{H-WbL8F+ZCH?-@e&KfNGdIlu3p6cfFsBOcso#!6Ux;tsviOx>dB!pntcvxi>niO3kCJaQnDhU{V~)lw7H-7 zIq@gjw@(n83BdpL_Bn;F@`>GvLr##9VKs3!aS71i$k0&t!H%JxA-;Zsl%mgo<*NR~ z{g^o}Ik8*&a&-~aSDiSl-QlIWsXg9b8nzrbf0)R;XxIhqE!8~ilJMPYNrD9@y#u#}G%X>P6Z;UWh+~1h)MDagK)?E`yR@H2wjS4? z^gI0YchsTCTvB>@Llsfy@VVr%S1{TPm)fW6TC|1HaU9(&^TcrdojSY;AxfIatZ-na z%nAkdJaN`=nJu10TuQujxQv>v7%o!D_|{PChYMa$o!$i_;Pxiy#HJ@&su5y8i>T*T zokxhPo+D(%VCaZW0qnqyxH!|TP8snaTG|OCq{TjM#AVQ#Jz@=T{)qL!h7s2SMOv&p zjFcQ1M4UjJPh0`CRhyH2S++_)c|X*7#3EvMVn5FBgUVXQeVsP1pRr2xSRMg@l9Y@&UdKuKJg>sSHz!))+r)SA~8tJAr_t@ zvULN7{pmyf{=~t=VZ<@SiNqPiGwAm07`Mx)e?IYI;woU+=ewzMKk?yHqQyZg{ zu$}!hJwW_~_{A}7en-=vD8uhW_o)(>1Y!y?9T@gG>r_dB4%F`)qhCV(a$>JjW$vkG zTgRRHB^PYJZjQA|^ z4dO?{L&UUd@hzX&hj=P+D)AiRRm5$?+lluQA0$3We1Z5H@g3sF#D5cA6Qs=eh#AB# z6UtHYk0*|Xj-k#i@ZoXr(f=6N;d}dO{g+J=~%< z0aiE5ifA=gN5l2v${ML7*8=}|9me9Suchv;>4de@`)g#b7dyXu3_f6%Q7f24#8qBc zFU_qzh`IK>+Ve6j)j*VaoTbjK-5$g^p|9Z5TJfzJ*j8OoTaNQ6tBC7~*As6c-bH+n z_#|;R&|sT@xft33LrI%pC}|V0mRKwJUhOudgREL(6}L`CWqp9Q8eS(QO84e6Dud}n zNq;>{J$>Te;YznZv#8%d3=uB|VumvDkfiWLDWCHg>jt7;`#qPTUq4Yoxs|S@{i$vq zK1o*iClTjQk`VuAEz*u2 zWN8npfS8}s)&I+OuUtt>jDi60f&pSSv544{IG8w|IET2DcnNU>P>&+~T%`|kmEN!3fd78=0r4kd z{8Z6tPb?)?5{D3Lh-VNN6E7!jCf-B*JMn$uw?t!_?0f1FP9oH{YESG+Jf1j;SVNpg zTu!`{cs+3^@o&Vv#4m^|Gi5LPmub%-r+laF0e+5qNengYv@6k?opRa+VBKjlgXlE< zJgl6QPnXuFYWmC$n0-u_cIif{rGFgV{0MYLH$O>yf%ppX4dOqD?-4&Fen$Lqy14y$ zx{*J+*>k#JGBJl(OzcSPWO`S_+~ zhOCX7f07ZVtnS99o2Vs)B@GRgHggK>|1eWl-5XN{n~3KV|4h6x^)}eKmT1nBwRbNN ztE{smM<&m*I%1Vntzy@0yhi{5)IQRcrRiQ2S=f?m*gXiCyXJHduqI zoGoq#5Ox2xd$8z8|7EBlu&HN@2xay;9mC_OZP_1WR5#CrtLo;n=vG#T!u$TZ&ssiv z9YVZhwzQdRh*tx9sq0|Bm%4>`AMr`zo5T-@-xJjwu^CTHBNh<55r-0|0&R8noO3$j zECg2k(0X9S4|~v9@xyKfcK=b2a1sD55_)0GIwO67t#t38dkWoa=pID(Cc0-?GDmur zkr{~@G_UYCpA!PFy!jdSFz zLbPAQp3WRu8@zjtq~QZVOFcU0iH??faZVXhJ}_6(zXLILlzY}(>Eo8pl@ObWt6;}a z*UmKxWPMdu`DCrtQZGQ?Qm+tYWdkigS3&iPsNOM`Q87vj_saEJhhF#4E2Vl)q<0*m zqd0soGpOWpLao??DRd1^e@eN0=QwG%>opzY+aQ#OBB)6M>;6(|`p{le!0t>Za3xUpKv688A95eFHdl=|6!NEPWTaVX4@3 zhr~1^B&MYyF&!8Z)A6BcWpeIMsg}gbKpt{m|Ft9zZpDVTVxwEJ8nRP!#bt|kYAdrd z1D#_0Tpq)oc7GY@0kchX_q6eSpbBMjK)(30GF_z#8x~=M!QLWkG3?YlYbip>@y zC3PHFbAe_Zz!Fq?Co)xvNl%#HUn8YYa?IP1sC#m}*Y%t7rlRd2@w)pmye=Ui(r#AA5B1)F{*H-M=R_yIo z?6X$v$5za48|!DF70YSG%386iR&01IbB}MkC@QCWI#_x*4(|H4OZi$RD{0w}NGB0(K7=ZoTDSi~Gs8=c(KXd!cQW>JVYCwY@lo?S(rQ0hqVAKLTSB zAo~V3Sp*!`s8|HZ&JA7_!!8UiQn*Nze=UjE2Cr3J)Bc!d?IiybC;RICm-O!BB$ z4ZuZrQpzQdiq**ulRPR`bqT!oj{uQei9VYo#to}iEA?nI)U$o8H5Svai$o<6AoY*W&&WY*vK*j_~HHb7FkK zSG5s#k}+RxCu?Dwe5NYFEn*6HK90!jXr8HdJFFV)EVZteHk+B*#cWX9_`SZA^6ut2 z>UA>h%M$e?*_!mTGy9oK)S2>;ms+EiW)1+`K&IU_sm#7&rrj-9my)el|I8d^E>{~I z_DSYgvsvBeupeNyLOn&MeK}8^z#_U*`LY(_E8bJdbm$kUNn~Pkl6jG8h%viFHIwPk zFH!GmW|U`5RF|kr`iW%X?sW4KbyI}RG5@TdBwMKlWu0ZNRxfD=n{&*|#S#8m63@tL zRBKgdR+sf^X68BOTGcDUE;To({$vuuCi5CKJHob_8`S|a9fvI{u0Q>Z6F;}8crxwh z7S)bSeA#N=q)N0+=H?DHlhsqo(tYL*RUcthYNuL6*5Z}cbf>z&VbYrJRBIe2t?5p+ z*H&vIYr0cC?J#LgcdAz$CavjCwclaVV(wI*J4{;4o$4otNlUv^ z*#j6`ue7u~Re((5_o%s3bPIj2Irgy7J4wKgOUNzTY z(wg3<&UKiyXMa&E9VTt-{ptpXN!$8Y^`OI~ZT+iy)?w1NKB(Ssn6#}AsRIs^w)J84 zO@uY7N0k0+aqmEBjj{)`o-!YenC%98T>VaWE7ZTTs?-xIk&Wj1xPw`*nNO%xva8~L z&-w@0}avs0}nRr(by^|V?`CMj5GJ)<5VLk-HVQqQT)L&Ro_A+kKDb~sEz ze_lQ0Fp+Gx+D)cQb+rNQ^#Si`jSl7fW7Jmhe-|CtA2Nw)PTLpa}q=FN)6bn+Bi&V zz+RQ+FsT81RgS}?2JBUx9VRtkuPSnw)PTLJ)L~Ks_Ns0UlNzvBb$6K5fV~nl{#x8K zkV<=1FKxyeuvhhQnACuGR6mDF4cMo~I8185yK1(>qz1gN&UBd6fPblT9VTV|fLiG= zDf1tyRSuIf|Dn3oVN&KlQacM*(q|o0D!tnUuQEB4(0f zpGTO~#xEjF^7c!$hkjmwl2wJ96y6|{*6mE|YxPNl-DiHIevPnmtZ&uC;o?z?k`(+- z)jLc=_)axDOl0|9UFI;6<=^Vek=mVv{-at#wnE*Jv()-oZE)D5@bhQ&mBaRsO*>iK ztxzB3G{S5PS&Ndm|EzX6OiJW0DsGh6)Ftv4l}e^-_%GO77qb>`LT(r17d3`VV!Our zMJ>^0thc|Zi?us7I(MUWNR2yH+^tYEa;tE`MCEAB&LqnmBTV;rhB3`yk`jh-i)Mzz z)--lIOj5#SbQ`OES(^KliQ5_`Xm$ZuoKZ4a`zfWzXFTpODLo0ssZ+#E$1lNHMb;80 zeNKXLmBXaQB^cK^OzKL4agW2Kt|S-_IZWzGg7Kuoq^=|wFE~u~VH|jU$W&@)q@Es) zd!~`_C(R`8*+!$oq~>HB+Z-k}C&$?7FsV7Y#{CYHnv-XI6k(03gYj>&mFi$F=3GYM zOdW!ho_ymfGKtZhR=%-2#;l7GH(P9q*%MY5V;z~!_X=a?oUmCH-pM&L!seJ2M&aCO z2tAF9HRG75r?F$6m`R)bi@&GwESa>)=i%1J4v#h?e0QP)6Y@B(2Hf%@M{^+8u0GsgVY*roo@2QbEc{qnWJ5I5BUc8f9EYrpxXq zV}oXDNnUBnDC2gr^=cXXJk@wA#^z|_w==_jR;kg3`z&EC-b?ekr;IjI9Cl4!C0LHb zw&(Q+>+G<5@*3f;zr&u%>tc*HMv|>p$sMM{=A;OllQP!0gG}O3pEBNfjqE~|)}cz( z7*97cbR)OJBC!1q>)ByxO0Ds^!v@2w)^IJKwIoXYi!!NyQ6_z3lu3P$GO6!TCiOkS+}}XXDEl33N;o`s zV*VmEHNrB$rbU>{yibcT=_#j2Sbf|6#_18(3^p^u)}k$#6JcAy=0%vab!SAF)OUG~ zTK-zxQs1MjQ~pwQX2h%;SVM&Mg`5i`YzWxd5hiWHIWeqJEs8Lil`k^h(fPnNkHyBP z4wJNAZ2aIbN$bUi>l{kvm9$=L_#Gx`z1YZhn56Y$qnJ!;+|4PAjlSBVvBQ|pa0$fV7>BV|Lxri>>xMws*+*G8E1u-AoI;;8&a zb$yt*r{*uUqHK06wmAR6l&IPHt=wJJid~ogXpGIBt=M1kpNKJgJijG|y?~U6h9Kjm zEfLAS%@=kqWBZi)iA>T~%DmZ8%(gpe7DpB}`+Le3<6D~PvDUkWUgL9ZlfC_Y#&-^rJo=0AJDKkLA21S{#m^OLaL1og9yB^!AgskZ zvEvgdj~GP`o7)j{Jfp&4O=SJZB=p3<4&>`7%Lo>-s$GR zr^e-)d7tXks16!8IP7h*dmJY7ql3m94wL!OLE|Tf$=L0n5x;{WaHfCI$a0wU9S4mv zhe>;O&=}}2Y0nNCCpk=384ntx9VV-c2aQ^X9qjaF;Gl6DnI2IeG-f+yhbZTn4vX)6 zE!I-8XnI3h0Vf^5*gu-;=OT)KQr(jm$BK4J#jU`ghHCp5bPEX7>f zqS*$ppqcQ3W?K+KJ9C`FZUJj=ZgSY2V43Cthdl~+S?0(WwYwIW<(YdOwx_T%wS&23 zk2ZUwa6oE-`S2^kT8vK$hoyElFL*Z1~pd?N_rb&&rMBVWZ(0iUrX;=LXpy6CmlWP`DbOd98i8A` zdRS5pxaF#6C3OY2mg=vP?gY1%s(lz^FSW;kTPrnK(qwRJrPh`72)MOYlO;V1Zmrd9 zNgKeejoL}lN8r{*?IGzHxV2RWOS%AVZPhW7{s6ajYN@2)grz`pB_$+`7VXt#k}|=q zz51f0d~nNC-;^`}-15{Nl8V5sgZhc2$H1+FdPLG|;MP$+E2$jZI;y`(`W)OksrC^r ziBsU#Nez)ykuWbPU#%;tO5#GG#*(5Ep9t!#=159TToKeo?MzheWF@{7bhFw`QeNU( zpngOPMYqIU^=7qL(%{55gSx6~B#lno9CWMtrKFjO?*|pAsox0rw@^Hu_({<1>QqTj zCw>vsQ{CX9qd|StZzR2#csl3~^}L792lZ3yeal+bCRPLuP*Xkh2kc9akhC$;3?8Je z_E5FpA?guHn-hbBhpAPLqLxy)a&p87HIfKtdG&)wsQK(B&NfO6zFXZ*#H+J=)HcUB z_REb@g6~o1c_<_JUN!!>+YRm&s9jGmB?e^$k5Xe#A(bmeQtROR)S9Q6)Yx&_eQGKZ zUpb@I{O{3??+%Ps?;~1BtE@5VG)cJ18mBIk6qp>Vj#u|`IJ(DBq&jC&3-$~-@NTFY zL^vZG99*P^6LD@Qs4+z4HjX6stF1jgqqO_ge2>p~tympG#Jh}>)Gs+4?J`bMx1Y!O zxRgrN_b)KvEU!fUR1)s0Ojdu9gyX{$)%lTq;`lICjgW+U5YyCTNw^0wLv14o_v>b< z-6cH(^pHAC67D6;Rwqiry@WX`>W7q7ZJ9h;;Aa?^x?Mvm^K?moT?p z!rXeuSlxJBu$SHjg3pq0c~TypSLad5^PF~-x`^w+#u!$4e6DUhEqIl>Ox6+VT<39n z8{FPi_enR5VVCOsgi)~0{b~)OavRHKzZxV7%VobBDhczqU#%$#%X7aPDGBqpU#%?( z^S58ED+$YNzgk}smf3!_fg~*5{c5r#EcyLvDwDt*?N>7|VmiwO=HZCi;%6qT+wavL zl5p;QPVGm;*CxE6oBowo-PIW8S?UmSDFn3u53yo6=uA>&ZvCBa@g4piaJk#V8%%3wG|#Q8JKCP?>kNhr0NybLr+QUZ{! z%_DkLWHoss7+$X?!(jYKEqL>xn^+h^6aO-74U?R#>=9&;(sRSu_YZZZnqnCe_YrJR;D zcQ_+F<&$79HB31a+|1*alaeD^Xps~S&LO274sM~Ptx3!OTpRNc}dTvTnWz6y8g;OUr$j&aMCCTS#S(E^`f7Lvk>fiD;W9kbEu_B|3+E6a24@85 z9Hb>k8b_2WsWkPDkU`o8BGfrq4AyMjt#eSw5O~`oQmI&S= zu3RZkEeX6^3$rOcWl!qeAtSX4Ne95^7;P`UtPg7X3!+>X(@~Uy@i$lh_!x`uOQL5?q zi2$m<$3v!SqpG>-g^-7|l|>NO&p(OQ~bpxsXL#ZjhTQLY8R$@TM8}wYf}7Ai`7|0n4;0M7%@( zls3mhuy3O+W}kM~G&o znUWp^TA{5Y;Od_2O^y1MQN)vd_slKYs70WX+y$TC*I|LS=%n@&9u23qH;LBGbMDpw#-AbLU(9;JybQY zT+Jn!&FOCefbu0>OJ@PN@O@}JqNV(G}OsG^Z{)M5s%sjwBeFa%K>e)B-C<1D`j#DGq!|&rp=NxFyoGp z&$NFLaho`(ExCrC@z8qpp!O0GpB*`*ZT7hB4n3s#r&Df~ux6vh5lzoPDiw{JeHMB| zD{Rh`4fKtsXENnC%MssdS8|wogWI=Sn>I+?B9D4X3HRi@gnM!xGVs2Im+o!$b?9+- zIHMTowAPt)V*bvFAGAUbbqo2??UU-^7Y|_xdI?ML7p+ZuOxZ%Qy;^yYiTB*?H6LGCnXbUBw&I;{GNvN|zTg^nZa#>p^32WuDwuy*qgs)cpH z_|WZ4=pWiqWOBm5Mqo;NHAGrU0p2Y0v`hj_V19 z>@%gsQlR#dvcP9meWWBDi>vBi5|xTPax3o1T5fGoJ*=v}`VJ&+6V>%b{m9M1JXF^+ zBrR+a5>{Q$CBi(!0~HfZ5pT3;5*DE6-pOg%33&+CM+{;*&;m|5>m%{M?~s=bSsZj9jT8c!j$a^ zh}4&OC`Z)MR}gXi)zL%lq4>b(8m*3=;-MT7r)Lte&p3TH(LzxzGcPPoU+v>FUSCJV zGl6(L>|V+r&F&L)$Q=BGvto_AgeB+^>;@~aalz>xx3|Hqm0lp-a245EpGCy2yik8s z>co{)p}s&8uA~a}$0gy4s8C-b30Gi+`V*3HMO3ISmxL>#Lj74uxXLQjpO=KItU`U2 zBwUXb>T4w7TCGrD#{_xk5>}{JjH2AaDzZ!1o%)kwm~ahrk6tDTd(%<+HX^K*9H0-_ zjY?^hrz8$Fo)k7p-z&r68t4I!+i`H4rt4!l23!Np)zgW%losnb>_%(t#d<4ASW1ia zwvw=v7VCMEu#^_-og`r?E!Mk8!ctnS-y#W1X|Y})2}^0Q-dz%w(qg@@BrK)HdVfi{ zN?xoFWP(-Qq_D+$;W&35CWSq#=Z$B=)#uCl&605S`LceyBrMOD^)ZqXqvwXL(Wkiy zN_VY3N797M(Lkq(xUAReWkh^+tko~FPiI%Oor^vz77URL$6FZCf0qR%NJDQl_tT8}89a2np< zJggU%aw%b-d|01Av`xc)_ptuBq;7CNFA(cz&N%RpB*TZ4`OAmDm`Bu-I z#xZ=Ac{=QbzCzMzpi{bQI=KlvO@Bs@mV{^I&+1Jj;c5CG^iGoSjQjuPbpl{w-a%_U(r91Zm&cBuIQgi zdM9gZz!m)qNgopV&!9XwUy{!-NoR>_OTv>6SM(G~c=F+j-cAzUeY~RICJFC8UeWsz zVGmtB{EEJOCdP1rPGJ6_zd}^5;EwK9y-X6Gm=MP5SzOk5VnQ*NJ;a13CR9V8&4ec= zG-HG$JeQyw>m=d11jD#ObV9)s6Q;2ejyquNcw)ja3La*{6BB;MNlAEO0zOhe|M*EB ze-Gh_i2!55BODG-OavIKCEQtZWqNi}czsXGu)V|^ug%98_^L`W))AFEd$ZHR zlZ-MZMa>y6l8g!>Y~|gzM1ZriRT+cKVD9?!B#B@JU7o*>HrrEhmMS-zzH&VI4^C;bnyuD0Vy4{U6k}yZz zjfjKjhUv@^eT@blTCessnh-4%OLC`$-(lP=>G|A;!uuJ0h@PC z_8A5l{fOA-U}HJE(XPWVBjj`R$!^0uW&K+2-0*um^ghr?4}FshXOuj20cecTiL{hE zzvV6pA7|V~ggJT+2-L&Ba-%`ZSHg=tl-hD*c(GB$;gqbFIpRU%DNnc$!yh!t*iB%# z$;P}dxCC*uhi_U*LM_vclSk04R7`4lD14SD2E4!Wu!r!x%_APdwZ&Wy;o4$>arHRG zKt0i7!|wzW_C$+~a3bz)78|vRFn>RUFE;wIPX*&!W(;Ez*yf)wiX~wyc*YGV#jxxjF*8gnI~&NaqzNvLJL5q}1? zV7QUmTSnPAH?3FSHg*y5nVK@=xFpO`nemGxjA4_Z|G;^`{g8Kzf(j-)4YS$kD+y0B zZ#70r!W!FV{K{nGp4>LWxlG|~+>_g8)RBaHa@&jsM3~ONn%j&557n%>!?@o=wQIg- z%;RtZ_v_v_M*PZY!Tq|AjN_8~HLb`*oih{fJ6MOsgER&-j#xOLw1fm zw;w$;8OXZAT4?=lL=&;k14f*O)~g4MWFqYA;%a_ov?0Q4EVbs>#zi8&QjZ%EzjI08 zvpdI)Xd(>vkb2yRBf`6~%hVG_GW(?K_B&&i$7fE>?~F%@FoycUCygZ@w*DumWt`E2Gz98?L_4Q=YzJHt8#pp zYM0qX66UCi*;^9k*3Yca*e6~A)y#D|>s-=mcujw^0KRaHF}wy8XnqS{D8@YClaL|i z8A;ePgqmlnup9Oa;pUxwM9$Z(Mgt9(bfy)oTFpsBn1`kC5`}r}rnp)^ShJS7xhlu! z-+E@vXfvW3Qv^^Q6Qai0;n~1PYQpO(Bqgl+bJzg~N z5T-1}LzwEO9>O|GH@A}(zHXbD-K%46-PD7KQ{Bw$N5oG`G&2_wVIDTrY-S37Dr>r` zvdlJ2bk;A+EFfA)&rM{TQ331|+eAwxgOpE z$}`(Uu@Xg!P_pzDvY;$TznWVLJ0e^3ATb$)|F(^<;62Igg1_eVaKh2HntS zj=0U7`=;D~NDD5m~Fdxkb|Rxn0ykb1xC!o9JPN)#a4omDAHKCgPO! zG-qDp2FEQRcI+o&YV|bph_IhX4DMxS)Z1Qq{;=X5q*)@(+hGjj_3~I=P=^Shxl!RB~VDno^*xL*-f0u;4%}_Hefx}^M zGt8_d345F2W|SoCQAU`}C1HB-|kwW!^0b zcL+wABPHPu!6yqIUMkdy-Ss98a@(C*Xzrdt0pV^cBQeDO^Cra)~aZD^lU zYk@h0Xrb8EzC*2r<^t)qtNqPD6-1ne$IUV#&coyGo`}w7d&u~#{ZiqjZ-5rLeHv$g z7JKOD_K$=v@zB-wRRfoL$dxxMbeV^6-T#D}s)po^_7Xku=W#P|-S4HSyrn)=H?LTG z(yhga&ubp$B{jBht*1@V6iW$nduOd@%!+16r6MhFWUUv>Db10J@MgPwpTnfGY@Gyhl5^YX{}ex9_ELC%^y;7s7Od9VZ0Lh*cuY7uA5#EwXhinlui zMw~SZh{^@d1kRc3h)TsL9l|1hF!MU0Pq<&zAv)rMc~MdY&_(mLe9}Vg<&t@j2=kB_ zamkG9f^JA@5x<%lM7+-V-TX`vmc;Mo5lQ&$!0+amTQD3yJD^wxiTK$8#nNwMHym>m zYa9{AFj7;jSsq%is@5_hoWYONG%KKh!{M4lxAG+6d_uSSO2Rp&VU3i8W1ML%DCBTB zs@c};9!UJ0smod^37<1{S)wPq;d7=gYosK6&eUbCA>!vuUDkF?Oqs;+h7$d zfvDUG>X;Hx#cCxfw&Q5<*-=tz#|BCj>sCqaiF!&ZAfE$>_}Z*ujUd8ODu}3JUFpl| z+zC%7RJVrQja06z?>IOjz*;ws2~U00vW`q(!c!l$tT7KT;f`l5>)m3eu=6C38Yf-BT>YY zOuvDaRBJa;xuWO8yJ4)b=g^JIEZr(1Dt8vwqM^^JtN79br=L z>D&vb;swraY3CsDS@aUq0-$DA{wqu?I@gSBVdcKcgl~$;wAv71-R1y&=Ak~3nbvPa zct5;PWR|6`p>WP+h&|g{BgxeTsrOoT!>2^DtujgQW}1j>tAYsMbTBY7+Y+yF>^S4h zwu&U-Y&6F@FA4Wba;?1A+2`Y3U~OS7W1`u8E35q*?1p)0WpyONJoJfdW%VH9RJXPU zup7nR#u~xlNJ|^*!8cJ0hihXkA>wdttrbM&@OGWgBidO<*e7XeXPsm>($dcQ@ojHf z+F3snvCejue;K;5&h}O~5tl@s6-&f>9(h*PcfD!Jvm%H%TnDQjyHV^Ntz;q&*U`G* z6Rx9GZL>GNPF5&UxikOf(IVe!F6n8aUPPRhd}|=PQI7JhkG6T!l5c%a#A)elT_j?i zU98aU=(F5Gon5R9BG%c(%4Ijw*~L2co>ylV>n9@Cd9!te-6(%uEqy2Hq;OrW;qQCH zb+zs%;&8WEQ`n8d-D=Gt;&QpwYP`!E?p7<8h{N4xbznCNS6~$oakv8Os3+XW$O6m$ zfYL&H#)VcU6Q!(&HJOO}i5}LRYuqw-V>o^*PYlJKoOy{tkaT*sxT zy{)4}{8pZR)>$Hsp}%#7-N2_2(BHc4Lylo^#Kg#f)<7cEGAnYJb%KabXpFQ@Nx~Bv zBdrUP@Px)l>nfAer|Zh#QI_=)<<=SAb$;Y1%b$pQhWo5A53Py3&nn)BK1;=OT{lIJ zxBT~0IOVmj#iGc{khHn$-0=IYLP@)Uimf6hD#1zCz5^T%XHS!?h|iEPzVgTtYwjU8 z<%sFl;~ts}^sgq}Qrco|U^nu)#p>ygTBh66 z3Sh6<8o&f@^#Zn8Q;ACHEaMIfdq#a|)g|JR_{ai< z@Q>dRYe=5OvFvwbJW04lzieHRglqK6)&lq%9JE%timzC;;eD`pZx>hWzgw$`(5-pcAJ%#4 zhI_t1o>Dz*Y&A>!V& zB&w<%=%E=={&p-;xxhRG*v%!O&Om#FB+PA)eMS<-9&BesP?-r#S%|$=66QA4-X{sm zEYvwHropI9!i~+POrS+tlb#dtF_2 z!<~}|yG#=9szlhw;?b>Ctn7}ow;|I;q7BIuPWiBVyXXizGll6eP+fajQ>F_*H`x^} zn6$!n(T(kbR!qS_8TOhyq*4)Acw2OiUDAmuyRcVuYkPBNrf!7;qTAZ>HzPeN1{YR~ z$hUh*Dk%(%=xmQ-qBVLKdpZ%Xz`EGWCE@C(3xYbUVgwHYDYUdJRe3OBa3s}oD zh2x?N?D%d>s|t%nq1}y$cC4d&+QU6GBf6J8l}YR@94&g=dADP@DPnKoc%aeUk+x}v z3Ufpsdy1s53rnK=*h@S#Bf77>mT0>D9ca13-YDr8qAg62&UyiN*!zg4&{);qeyRt? zFdg0v9j5fRUy~HyBTN}!zauH7N3DQ6?T;ko^e7es?JtRVWNz5>Wf{O7#NnvJVn*xS@8}-ke)}24JXtfr!HmwSOn#o&2G;*#~`MZyK)+x5J3| z3Cg?eAw+yn@oxKhdZ49T;10;W_8Ou`#r_@-M~||T`=J)3MbY=!W&M#jEu-y~1IUfq z*=XDUP9#kAbK#@ykwoQmqF}7;4DyDXVEaqL69p6OHG|#Z&WU2XjHui>*<)4ogZ3Yi ze(f<@JZRUri?ld3dLqFGtYv&9;4_uol!Bf)qQqV>lzb|8_H3Y(*jdAvM)!Ory2S1( zX)4eZd#j}7J(t2vWxJ%;fo9m{lHLKDY44P@3uu(Fboc+8Hto2f30qL10#iBRV`pfMhLD|(TgHyVAG zif+BG1V3p%O@u!8ML%V)^U&AP&)Or#vrarUx61Ba#Dr&mR@)sX5YfraSMAP{a6bR4 z-AxkC=U=t&WTN@}8hf}ToX@YZ#}HxcIby9{?4dmYYwg`0Ivc&-9&m z6JdNCYHzZ;OeQyh=M~?x`%1#|itpPaCE+Q=59~S9*eBja{LtPl3GX8AvF+*T_NaKT z&*!!G*drfeI@ITn+K26+*-TjlW}R>BGmkNKD5z8CoW1%frow`>Iu&;AvrKn_o9_5O z&om0cRdZIaXFAd6<~p^U)HjgIl}mjli)d%hMk0EDNFC>}B&^$5=Lbo6b~4T>dyBNt zmD<1o6{y9~`@;U2Gk7by;TpDQod(XmMCFd%ceJ?48Bc^O@4Q|6(WbrPK~iOQ9+eHYhBbN24wJm6T|!WmKSOK09g zVl6G4iyoQ`KK*ujeP%gN5wXuKXPt)8i&t(;_{gl#(7TClKvO!v~|iP zz1aV9o%YUINpJQSF?r5a4_PrC9KU@S-$Jpif3=v7PIV6j#&mK5JQNm_?*w}&GN!W= zCTVy7_>eA6iicuix;j}NijTR~>FJ?HF$K;*N&EVz#@z0#^iXz8Pv10EU{Gu-)(Npv01OWfmBc<9IAdz_*Jn3ht}bHJ#Wd!6{tkfw`)0~#nJoiR-I zeFH{|k4q}+< z43?BKXiwNQ=Uz!|28|Zeo$-=7f!lPaL{eAC`wXY(DCJ%00a|7_b0rNPG+E4amPr~j zC`UZxd?u-s=(MDVAlyTaa*VYs0JqsrbxF%1zS&Ngq}PGwII&DLRz2**ALnp8Aijs4 z6iNGm<~rF#SQ2@4A9V(JXn5!XXDX98Jg9-P(3v6WETm(w`$PLl2){9xTT zokB@722TSTENQ{u2FeC!w4~)kC6d+--V?ULd4x%9g|uvRmU-xr(2dS956!9jmXms# zQcdlw%$Z8GP&_f0 zMIMTd{m@zAq4?On&ij(KgU)@<1xW{p?DHJo$-A1w?sG;+`sJ=>vHP8il8#2^#vX8f zC&IZ+``FJM;{t}mwA>1mOjItgoqg`i`w`u?X?2J6iv8UAl8E=ZzH*L9!o9AqoRdVT zWmxQ2&cL7CItPV*;}jE>iq1pe`AsMGBB!&E=o2E|-TKz~SrYDNed`4Oj6UJ36|mQJ z)Jc?tyIV({3`w}3b=27_3HP&(IzxWpaJchz!dW2+cU4X}ewWw{cU4X}MUrq=<%E+_ z!EU&#a>CIsGvThv31{H1Ot`B8K7V7vU6m8g$}3E`^L4`cOcL&jedipKgu7|qImad8 z{@F?Af+XBEI_3N>33rW7JI?Q{6L*cicY-D1uF)B%jwIYQI_o4d(YeZVPIF1P-+Inz zPsDq%=bSD?SVs@Uo^uAVPdd4B-WkKB;I8|R&QwXb>wd}E_Xnm0SJ%^GuQ;`@BJs@e zs?&{#-(P;!=_6?)Jh5`s86*jxX1VIzEeW4yx$2CSgzx9O>P(b`@8`SfOp}C9I#>HtE}p-I7zt5s^MxR37?Ru;p!*}&nE}CI!nUy$$_q}lJE(cAlC>FjTXVK zkxbOn2D@wxW9Rw{c1CwFWWU$65fBvahwD)tTiG`6j4Y7KOQ73R>- zW81mTOS%Ho!8I?K!(qMWyY@-Kdhg;23t>0BZ_&lIfC$czXCII4>WU3Vw}qm~u(PqZ zxu#0W9(FOdo9h@6mJ2)qOjFZ-k?8vD2=Ny2pA;hM+cpq~h-*WXoA3uAapJUOghy@9TQQ52to zWpH>yIuJ)7>--=Jud&c zNSMwR_3m*^s^_M*_3m@cBjOWT<6K$wDL#Qc^f*_ZBs`Hd&NYx8cjgmW_q%pV!ZrH+ zu3?F8on7kP?lhK9gZLqEl52C4 zTjyjk$#pQ9Ql{W3j1t$EOmuaWxHhD?eZE((#5E!n37#Dv{&Br2u8V0Lu4CHg^`^PD zH*?d8dNW)*Gm#z@TZjKxZpRkGk@SumpRnkGfV9@zp=yRV~Ns zbG}OmMVuHegeuN~RK%e`G=Bk76Yqi4#cq&>n3D>RPKaN?k0q{>PkRLVtO8OI&B0X_ znIQii4ZiV~G{>+ktCQx+W?w&S_Kmj&rNq|{+xTi@*C6uA^4Ta%`I{l$*x8S7C||R$ z{(m?2nv}0P6g!#~!O~a1uQ{IlJez?j>>rIa(kuc)C6GO)MD;N%g2S>an%y?&QQK#!#H!B+4~P zmFzY6t~>f^`xf5-oS%g;259j^uVHRx>v>UFM4cJ-Bi=UQO@ zvmh?FPG2)$j}Mb8r-ZM~g_J5^GwZ1t>(%3HK12S!QiU&_V!l{D>dC1pDr;WglQv(S zzB&EttA>64{nGIE1JI%AKKB8o~q#Us|SXzYa)Md`x59ej4L&)H7(*GeuQ!wFGP}2wQ}MbVMXbMO41h zE8F~MJ;Q&dhp$=gKe1J)qBo_JOW=bB3EXmaQz-7u z(5wg?@vkSZe|2cmzqiKSHt1iKuGr_h&DXDGx2ta`-;{8@dt<>bAzwe89O|E>);A6R zB(->ryRUb?c6bKjYp&c6_=duAQ>YEA0^6`gGdG>uFw85cZ^Ua~6Z=5w;xmwj_#C7n zuqRQ)ja>1%xSme#Nph(#MYFpXy3sZ3wb#T~!=R3YV^tJgwm#~<2=R4uIN)2KZ1=R zP>(5Mve5tE@#Af8Fus#!UmI>|0Xdi>)UVMPs!N}7mC}iR6u~24<^IjL#c~UIoMwX; zDOBZnE8AdsLa#{v*=*d6F{0U5s=@(#O;iV|i%^fBYwe@{=j;E~KNpayUTBX^tLNn*4ObrHx|1vGVR@r^@txQCntiV_HhZt5Yu7=QeX>n& zDviM)H8GOXGmdN~lFd|*x|j{p5c5HrVkt;VJPXnmFM)K#I*=~$HpnVsD@aBBa}C~6 zLle9HOFI8~s{X$QuHVXS=3}TMReT1LTahll0<%Fa!4%k*ENVqIwIYXFk&D_7%m@BK zSe^~y6`_dA=3|r|cCFl^{z<(58^hvmj6(YriujJo{Cg^&A3!SNBH6Hcbtu|Ykp1sB zkQ#hL5N5d`6;aK{{4k6iytf9U2HA&_ADnq!&*nxmH{Y3RQ`)JECJk)9{yInz?22JE z+S$`ZO^Utpe$=0Bbh(<~(GB+!RhpeDG&^N8jEPhl-u*^J`0fHlQCz+@*c(F{sC{zS z+T^D$NL9px)I=gkT{H%1h^8Q62MHvMdLZ4d3p=9gQy>+A--m=zzS0icpKWx}5@v>m zXb1jHnY&J;xhrYD9i%RLf;0qvWzrObK`P>Ik~i8}dNd8w))b^BMv>1cAYnYMWUjnI z;Id`yef` z6r>{NQ?7lb@0RX z`aa|i_Cr8w@CI9S{hDmPB^zF;eMdHDd~Ddy4`Ab730@*U?EiN%v(Du-KV%JmtQpi>{i#-xvAze!gbzb9`<7j+s|*yn-%*oeEuC?T!7X)(5X^3#29dK-$6|qyvw* zmP&W)+%^fcs^v0aGmj3w_Gsh2ix)~MxzPwx3tUawAF~ACkHcNH>)qw_-ABEV z*;`ZY{oBemoD%Q;s(a7YcjoEc*K)i1hN`@a`{&(ho$k^aB8qay`!khOQrU)M!I8Ie zz3U<#v>76abf$r{L>5S1?<=$$$va3I#m;qlZ5rBuf7q3R64pc>n03(^q#Uc2jZBm1#KvHYvG#8lxuK9y!! zns_z{>jLwokc~<#MCV)T|~3f^^5iu3q^c&;1}hs+a~j;#TLL zU-_1R@0IoEzRR~&bDRI`mJfZ`4PwN+{HM9V>sJxOsQr7(L=oQm0@s?^58mrk1+VP* zJ_VZ#Xifd!TJd?;m5Sgw|0qbOD#lZtPNF)U4bl*ggEYl+AT99NCp(i| zJC%2yeOG6`X5V=Kj@h?e`IU&MG{~e&meTDjJ@NLgL^1e?o za=tzVGu+_%_ci0_z~@h{?Q^&UeD9unuQlIm z>RL13DZ_Hp#FMacanA%ezi6Y1|L*Vy+g<=V$N4aD=cj`pQo^BRzwudD*syIT3DgGEvoQc7xVp>KhM&25e=sx;9dib zZX6@pxYzz%ci2KS@e`y(7u?R-o@d>^kUhE@($&evoT5FC8t%MsZF;W`o<&z4iFh`C zJ=Z^{|3))vgZARwVJy<@_OOtF)Ve!r{^m`t&CT0ym84jn}6l*fq-&*6nj2U7;L`&Dg3H;XRFZZM)@qVd?AVf7+Z!DenRj&QXBWMGuhf z{c7J+k!-%vT>Z~yBcYC{-1?IK5v2cq(*J8yterC;P4NiD_1Au?!LGQwr{ej*pZf=0 zJOndjcmLL$u6J*1an}f!?TzgDIy%@D%i$}Ky7(5PA-)4?if2Y*8oXnryYA*gF1R07 zs2^4Z?oVpsIqajUG_W5$ZE~$2U)RbSd|m(Du$5!SwE3Q$yVjgfwdTFM;lA_8C)u`B*xlrZ zC7u~j#9zzGHO}SC^FUuS_T=uh507^|0(wWWYv*-nU)kob_m?XBuWaMiz-yoPA=jGt zi1O~Wfphg>qY7VFuMM|Dx5W9t^=1e>PheN?c@Ov8gJ&t;JaRg(mEQFH`zieExebr1 zUsK+Xg5=qV`|9TFSA#aM(Me97PIwx0!qcSP6-%D&{NLj9`My|3rzyX0gkgTqgIN*3 zf>eb$3eD^XM;}f2gAI(B(Ebe(3T9J8fV4y``AjCsR}=3~^4%7$Q=Y^7-iP(Bf8jI~ zv}&F1`1{_0dSyrYhTDEnIt5R!)_# z4WDnk6H;iQq6o~USVgJ&oz@*(@3@n%(M~>`&4yiigLd*w+R2CgYOt|sC*Pr+e3#tG z_on&Uk)8W<-sS%DNUhTDwbnXG)$TYS^`H*TNHqeBYJ~$iGd)i2;A-W z6Ze3*s(2OrR1-mPwN@8lApJ!mq)?$9233p$KMLu0kHz_Ln-C=o5uN-zBgt90NHl@LxYfM8G z(&KlHo*Sis_55dP_;*W#uNu6wtkJASmwl14;}QD&z{MM!~EJbmbHp()C5Kfin-}(|0e0*QdU{;+y1W zE6F`14}(+%pQAWMHb0SM^B-icEY15c6!!m$_&esR&-!a11{3z)`sfs7ayo#jv ztk?hT*;3ypL!)Wzc$G9@8LHxSFvD&zje|Tc;E8>AKY(UUl#ze0E8FN|tH)lEe!OdK z?hV|U@rr;s3|$u-Dv@ls9&^F0iwux(*NJ51-8r5$vE;e;U(bQrl}E_|q|XAx=dq0-GG5VU>mGk23|3*9izGJcX&aZm~%{JaX9PXS# zFX7%NztL{PjqJVaNB91NuVy|O<(rqkW9C}G^`It}L64z}r|F4+zb>V}quE<}*V`5J z)xbXYQ%&Lh7x&IyWgFkp_SN7uYjh7(5ucMbK0oPe=KL-lhOrcb)Wz4}(-22NnsV>g ztId6_aSA!C?+W@miiMw9J_ly^UPt9MxNpkk>YMK2dUrB)cuOg2`15|I+x|wgQ(mo9 zK1Jqj8;al)n2UyEuAc?zwyB^rPk}M?dZ#hj9#<$Do!-<%BJFzJ$ll#P4VXQeA_)Hc zVICF+e?jo)4>LJ`m`yk^53CJ;{vs6q!r(7l^n*Wt5edJM@EZ-k(ePUverv;TlGq@U z!JG`{RQOGW-z;%ncUW2yqX%j{>_>dESVsS=+H`c@72JxV%P#yyJhroTdh*xKWc{Z3IfjA$5-=$(Z{0&i`5F^#6 zM6vP|gnJ6YJu7CaFM-`lkhj(F_X_-NgRt8m%yw}^*$(k;7k=7yu-guHJH!Ze2bgw% zt{o6(IdC~}IdD1fA<<9!Rx}ge!S5MxI|FWK!0jx=c^2Y43vvDcY5Dmt>_M_W$)P0Sqw)}T0+~xm&LW#fLHgBy8st*3 z3S66z(7ayVqBI_xQ{NF8V~>gkopLw#*PB}e~ldnHu!eE1_Ix|w?$bxc2@l@%ClpGBG)P}kIe<0&yIZrY#NW9PjYC$ z76rdhvqgD*?5Fj=QMQgfNcQLIe=K&5{i*&b<(sjmgFY6g$5xQN9`~{MW^9$X3(Bu! zYs8&W)N$2<7gK5%3)i^FxXVgnP^{|EJ1Z15YFuQDqb84wkHZ)f;uJM=T=O{e**30* zin)e?337@uN1z^*u5n}H>VWIixQ$}cICzhkm^*GZm{$*&4|2h{(c*$KH*6i~OpLx1 ztf)^wEEp;!zNPy5xMuN-1?Fh6z#J_Wo5ro$xW=C|$-Mgxcvoeir1&HEu=2X5?qF=s$ibwC!1A ze++I0wLc>Y%h{C>E=G*+C5A#uplsEb$JR_3r4Ak&moP!aHnT-p1FkJW!=TWXkfZsb zn34=ApT=WfN=^$36 zzW_cb6y4vbgIJ$l9&k#TTy!+3gV>z@7MLF@3J&fd_NBiA=6@BHf}gL`w}N?T(M*VU zMbTWaxtP8NG`w825X@K8*M&me6|HErQEV@IBoscD0=4$BI(b4u(gjiP{`D%B8(!V` zwL(R`louaT2JIv)xeV-=LJP#V@RfpjQ0QfG^nUohA(%s&I2yjEus?-GxnA8)=Iv0+ zEt_nITFz^70m@{(`i=72{Rit0C09lL4fJ2;5>|&jFfOH~IuYaq((tjE`v6?aU>+WN zN-TX~PuMAHlNZFQ2dvZ!;*AG#AcbWQM8?2JCLqR^fYrP>zp54fEq1xPs!d6p(k;RTO+U@bQVEPuhL|dLf}AOKft*7&^T=ia*(?&f zMK$rI=%!Q?M?_zc$HZMAPl}Ns&xi>i&x^?*FN)b9E5rhjSHu$_h4MT|U3nFxt!x14 zr)&Y~uj~RDq@cDi%tA*$x{iKq{fPi5L;V?$VfrqRU3EPW_Ts=s6UX#5U_Pm%<}(!P zJo&$K#Fmq9V$11jV#_HocS9=+v(|-F6%p1ZkhQIHkg?Vt zknz^R5Lhc&SW|vBrr+Pjln2>Zs$n*!Fq7=U$NU&KQsh&V(>cWH<}LY|+Nq6;@63bG`tZ5snfL`xvfo!=e1ExN3s_ zDq}(BSGm6?+)b{6_5-U_g**D8%zO7%DX(dYeO2BEd9ccEkY85W z1M*0fPe8*l2nF>{{?CAaTb!@5AM7ty`5gRIR5=XtN|kTHM);vl-4FfPe$P93@T&^(*7iFIHnD!+gH602wz>pA)SL{iwn!yy8Kf=K4@)xF?;N8MkU7Yco0JTs- zsl7t!6jcMDwdhr`{n%Bp{rFYI_Tyi5HxA?yh3D>eeUV{bx?**!&pMN>{8R(C-GsOREa2?@)6y!*MEa5T!C&64q z_Qn2Z!Cc~x`ltG1Tr>T#PnhG6bvn-*u8~pLAEF(ReDJ9$evCZuC$6V~D829>L0&V-q{-FOAOjQkR!^JiF zMcHC?jfX+5sqrt6>uNj>azl+}Aj@hz4RUjh=Rt0-@e;_LHP(RKU1L4Sy*1tfxv$2% zAP?5q4)V(y?}I#2<0FvAYJ3XvWQ~I$&(!z|3?QUvY<`{Y2Zm(;+EV9p4<3^FtDb*#a_au~S=23D(GO$-SfTN`RH5YsRcT%iU7 zu}+I9Y;hpgX-Oc~=~S|x8HjZ{ClFIUk3ua7#B?qS#M)dIh_$&q5NmTqAlBx}K&;Kx zfmoYs0^Fnrkgm**M)y1L^6XdcGyDpRr^!QLRAy|4VLoj!%L-2}Q z6N33&7lQrRh7inGSqSEFa|qVR_7JR*ogsMb><+;zdT)pewCxL-2tE&nR0s2yAweLI zgkW!QjC`I984Kn!A>nl)U!iZ*)x^rs(XpUE6w^=^ifPyohWg9Go`C*gbJ*zEs$xiZ zk9t+bi0}a*M}`jvIVOAz$fEFxAdADNgDeT33vz1sB9Jq~pRNb(Aslti3rC#`!ZBZq z!ZBaV!ZBaV!!chg!ZBYf!!cj0!(WHCvnCwJ!FAy;f}RcGuY)WL$5C=~_@)rpniIWX zd}{>1DdM&q{H-GD(r=(>K)&^@CrS z;KF_h{=oAL@C#F20b5W)gWr7k)!`Qo5(oo+{o&Vy-vs!zl)(C7_3PGeSiedAjQVZs zcdCC|{qFUP>o2MQX8p4ITk7wu|55#~>i z;7S8=lYLY0O))p!e$)J$Hr{mXrtpTf8#ZZ}(=fkbuZDvf4sZBi!|4s@HeA&3iH0vW z{Gj3ChCejC)DXI;gy@9Cgr*7E32hSc6S^hzP8gd|lCUgcZNl3L+Y>%aIFRs1LPp|- z#4i$$Bpy%vC2@44CmOxf=;H`KHMaO)8q`DXx^7DNRxeQU<3COBtC`nzAlsOUka4uTs8C zf$2kPU}{2Ya%y^Nc53I;38@QGMbq@A!m9Z$QQW~Te4$D}t&&q%*5eNg(n=?|r^OkbP6G5y{2?dj*!FQ+RRK^e6(+GXTt z+>vpA#)6C&Gs-iLXZ(@jY8Ke6X0zyK@y!aGEot^nv%}4*H@~TQtLBrNFKzx}^R3N4 zZGN=*Pt5~c#I|VIBCSQM79CsM(_&VO`7Ktoc(=s|Exv4Vx<$3j#LRY?cVte@d?fSb z%#E4bGe6EenE7Mo<;*`a^{lE{(OFHiT4r_3x+SZ7R^O~4S>v*bvu0*3%6ca2-K_Vr zKF|6-OUbU99hTiFJ14tq_C48;WG~5Hnf-e9#_X-xd$WJeu96d)qe8pYV9l&UYlPD( zu+td{*Z56flK^+x6QM0P62Y*C84A0T;UZJi6xrY-2X;6+iQ1yGXee$6AHBdwf6-Xn zC7OyMB3%p@8L&s$0`?s<#a!6!Tmqq@h+@7H;ay93tSsp zLE8>d0IRW{;(gHg0ql-`1X}ls0pb&Jr`RtBibGT8Lp zrz{oql_y0U?1tW?EEkE&b0SGu3G1*`B29ToG*?!Ow#v(*qwenqz38sIB?^_Tq8IF#_EvU?JCyfDe`S}rQ`s#BDIdc+a=#b`yQ6n2 zhsC|hw_=R)ofxN_5)UY+VMY19n4+8!Q(<>>y7GgVrTiiuQ!2#c%5P#BbXRFXcR(MJ zMKYIUYm)6qb|86s&_M9hpX6Wr_x1TlFzmVsI|OAQ$=W0vkxVDql4M7cx038jau~@` zBwzB8|E^A7J-+&@hhnONNzM(6g>rZyED_}6k!a8I-;K*xzi+&IqF}#4d=-@rGBtVt z$p5b9Ikhq6zB>Q9JbmlpZhdk9ylFXR2FUT%9tQbv43@wXF{snm=Z$LW@A%nY7j+(v zjf6LWoQ_5FC6b35p!qyW-&WyUdX@G2*4CI56OE;7LlAy@^O-@NNy$h3CXXL@`8mplhg&|uSo?Uwd9^4UCI4Gaw!Eg#&}~$ zHX+%XWC6)RB*&4QNpcCvXGwBxvVUJaY|lP@!?J!3>#Lt_d^P<2lyF?PG{IbO+5WfE zvxjtkkrD~dM%|i<`dOYL^A99{NgWTaBbuV?B$Cre&PYQW&ev@jo8Y}VzUBuqFqCiG z@OAYyv(I5IP}?|?50aeeW7ev-f~gdT`X^=h&oyu@yp@U9<1UiEdF0y3$U@geS(u~e zNc#Hmy>i+A;%xM}JR7gamq@-rHrq)0_G7+RpHpRgxDXv$ZUO15pLJHQFYZO_wZ^zA z`{z*H(*8*abNOVo$5?JA*}y2QB?R|5P4duwhTnRyd78`|Z{_q{BG=zY-fhRh1bb^o z>>EGoh#Efch$VcAdyEa#Os~oK)%%(?+$P2oC0!Z=Vl=H zbj|^JpmST0Uv=&T@>u6vKz`r3JIEh9qlOo{pj=0id$ze@s53Qsm`BbHL(RT6Y~Fb@ zhW&)(jch81^1VhTbX^RZ-zY#moJSrjx!kx^xr8~_TmrsVakm~AwjarhJ=eoJN$rI; z!6Ylk)u1oBrbbVy;~tNuXrF?4Q2#?9$C2dG>hS^S^N9i21H3nQQC$J2k%mx4CrJJqbCtU|_JOdV^37f4cH%qE`d%HrEQ#c>{&M?dc zm;BtYhk|%6t-@&pc-JY+sx6Q@%%qX4fP}XegS6m`D!f}k)BssS1cD5J8MX?hP>?|| z%Z6uMMJ%pvC74>0WuE1Y2kQp#HSHYA8vN_DlRp6E&Gev8VSuit)w{eN~AmPp} z$XuB1tKg$E$X4QJkgehVxhmR-+dy^~-9Q$?uL|?_9w6_AUlnHmeLyaN6Amh@0Q!Sm z3iEh4D=7wnd;(Srs(2D+^s0CY=JTpp4&hX|4|^}jXW$G3ybA#4@~T)N#)5oKz}I5n z-t+`W|68!SP~lv`M35iD$p;ly2&EuDg>OKpVxO1>a=(}X@&MfBgZK2oNeERO6c2+u z1S=9%{2%t-JxJ2*suR1F+10hJ@pu@tuSy@?Csjf$M zW>!CDBvM(KSyidZS7&B*RY?f7G-yF+c?bgI4a_Pq%K}zl5JD@2Okj+KmWL3mOb{3o zpyg$jU@;~zL12V`zjN;S?)PP8)%3I^d;g$2>VEgT-+i8Y?s?sFZ~kxig$DeBg8NQ{ z|2uv)BL$WHyAe)7D}YBbbOOxI)b}F%O6UccovH6b_z$PPAK?Sg5HLGaKZx)_XbHIM z7J5R;EkH|1xkcy*DYpa-A?5PW4^q$^K8Enx)W;ECpZWyC8&jV|_~O)0BYbJSqz&occM0eP|CUH<7Ag00 zQ@?@me}RUPa-W*|ErdTm_1g$P4Sgf!{>{|q5&pu|?;-rfsXsvYOVB@XCJGuz%6$g< zN6P&Qw2zegEc6ffoK5{X;QVV-e}VYFo%&0Je>3%0Ncn#x{KC{SgOqzDyf<|M>Hn#O zUy-^8>Gw%^f9hVOe;sn;P~O+A8eE%g|}Lh5mZS5mJ>SWLlj!j)1_AS|bzM7W;9M!dV4dK%$1 zc&1<;Q|A%Bn7WAYrPQ+s-;%n7@U5xm5Pogy1%#E2*fKzJ+l62iUITM_Q3DhR(mRYmyj zR1M+nR0H9AQdHy() zrQV6~ds6R0_}``8jqnqx_aOXa>ScsKlX@S*|0(s2fa=et-jDb%r#^u2GpTPu_$#Sz zL-_lt4e-z>6^hXe0 zPk$8Qjr7M5cG4e5I81*6;cohq2ydl-8sT30XAthEe-`0E`sWb7BmF6a?@WIh;n$^q z0pYvSzl8AR^k)#hKmA#R-<Ayty>GWSA{5R>pM)(Wqzd`tm>AywzOXuXb_L-d9xEaI(_`xh|HH9s2>{ zSElz67N-vomZsl{usr=PgzMAqMtF7lJqWK&zl`wu^!pIrnEpnDFHXN7VQu;Y2%{k;g^JN+SqFHe6T!f%-V zeuUpN{R0T!KmCIUzj^wH5q|6RhY^0q^p7C?ucv<$;lG;x2*Mwn{wN^!LkLsuho|9t z;r@Gsz?12ZBmCXzPaypK^d}MiyXl`s_~+9^k zUqHBc{Fe|e9sdl%`ti>qY#jeJgqz1dhw$r<|2o2VAO8)sbz8#s9{)|Gyhp-+dHlB! z|AFJbjqoRr{|>@W9REDRpE~|~2tRrJ4-o#3$Nvc7?;ihSgr7hDC#dJKnLkDRQG__v zGxKMNf0cw^J@e;Cc|Af%I5U5N@Z`*2BIOi9%=!tLrubz1F%#&Yv_R{P-&tJdrwhLPqIv0i) ze(~aGFaF81=Pq5k^iM7=URt?Sy7Vue`@ZKs@xmXz@MCY8a_>6cd+QYD?CjT0Vb1#oMPaTfd6%y~n?OXUe^NywI9*-*EiD*_(3jJN`om|LO6sm<1O(eiq?39pC;3 zIG2C?ClP-0@n1#wf#bi6@Sh$33b+k@%W;r1_pQeZ2*2(4Ho|W|zK`&O$A1XncN{-< z8=@C^|5PyG!zyAY&f8zc<_I$p8u73eC;}@{c^9Ahjd;$AAU%=kZ7qGAM1?=g30n*fe zcphDPex+OAZ8hHLvZXiAUOrQAwOna;W24%s`;@oO?wr}H-fFZOL;F%X=!}0nGdSoB zw;O{d9Jbn9^VMNBmZ#aSZZ+IBt)|?sb_Sc>e%s|cx2}~n%+#yBVWaOBy8XRtWfm{= zyS+iI4&RA0)lRi_z-F}PrCy^Ja=C1`-O~o-LvAj+R$i_ipk^H)htuc`QTG9!wQikX z5Z1GEtsOR7>eq4|)M#nrH*qsF3iq#_kAA(P-pOtANxiqcoBf-RvN?YZavr8Z6m)tR(4y% zCZ^Sv)X2NdAr?}jKWJbv%{4pK{s9p=FAxn_-|P~+*ZNH^vsf~h=9%v9(8FId6bdVb zC)qA5XCYZf0|8oLXE@*Rt0$Vt0NpbHF=u&69+oqJNft}D)2(gyyX}Vb)G)SC1*I7_ z-$5)JK}C;M3(XdL11fZ_JRZjc#XsS7xw$#KxPRVdLjDk4XmzVYXB7#}NHk-ZYlwBN zI;?HSz_&YWwp^jwueKZL)N*q$AMaz&(#A(Sq@a zQv$K+Y)S2yPT*Oi+-%1Q8gW{PVY#sn92wO5O(8pi>nqWmM@*pH9TXSm;*0FuGfMXY zx&Xbat!8XdMFfaM3jN^kQ6uCVGpp0>cksx}r^w?*3R_UpQGK^3#n8>wK_)g^F!5>Wu_YO}E?GZtHA zx>QCJxVkRwKO0+J>|0o1u>~ljVy!ZwoN{3u15mcyojVvd)|3ztu+i9=CC;A-A>z0Y za-EdS7Dntq4H^?afGp?Ry_Rt1Mm^W>a|W{6>o;DML566tA%v74OU#iPx9W=Te{P#b7V+Yj{_bTNO+-7R(oEC2#jc#mS16`U!| zRwQ#!qzm#Ldcw+9Vi@L9z-A1;Q5!}~pQP3W z$~L#VxhXJ&m?v6@fj`#pLLt707YdviNTp+IOyo-7t#Tkr@1#-?04N7aJ5UK?h(hw{ z=5mLA;&**{bnWq!<7iDDO&jzqAeJBajbWg={6nQwL%!JEAUOBqpPBZiLMF-p8;{7Y`U_ zvr`*J?8viV#ee`aqtTO^+EZx?3nRhx^5tzTrFwC<(^0h$Yn0jxxDzprP@acGSsGTM zX#t$e2Yum#TspEG0-7rXhCD1((?KA(e80kLLvI&5tagF}7V1(TRF9iOGp?Y3f3E$8y3 zcrQWzu-N7sr5ZGcevF|SIr6&Go4eqJq+W?RSQFtCdaMe=obkBy-S9Fp08M#T`LxKr zRf)$|?YU?CI*mz4f^ZbAg>FdfFRVw^im)5OK7xLbx*Qm zj5?a_ad~>7proxmsCE0T<_6TG!LZSGYa4IJx&mQrL6;Z&3z%}X+S*mYM>C%R>a>s> zC^K+N)xq{q3s6-Z&vAwwZbD>-7Ux!>8cB7b3M+!COUT{&;*loP--5W&iAmC#Ua#fx zHuO|m5AXT4SllEQET{=6>l?aOuB;XFxz%!Ic^R6Zy)4CFmY3pRmfTijSb?gB?nx8{ znSvsGAhW#bgF+f4f@MgSEluzCuYqh(5@I$mD0^)VR>6v0x8Fp?vOZl)ih(FB-)cEP zJG)n01h2RwCr}12AMN8 zDkB|q(X4*xl8??EuSvn8hNfY>%;BnZK=`R+SW-}?Y5KOeL^e?*IHB@FR@RM=O`kD zcKqNgtP=wst}kDYTGjU(@{VfklSCzzRh4!(22#Xc`~1`9>jEfk0Vs;kBG!&W*grvdrfe^dn1E zqBj;Ad6R0<(MlG^Xe`Y}O~&0J)CR7@36oS1Px(-9Y`Pi}p=7xs^4OCZk;e%gcl|CX zN8{32;stV4VL;^AEs9j;XIZ3L)Z>s(Wd|7bsa8Mo^Gobi<^aijS}&UeW?%}043Pik zfQ3filX*ZH27){S2~-G>m>GW^c5N{{D~FR;%rm97fveVryVaJ)yWr$)a2(gKQ%)Fc zcUyIik(7x#+#K|t9VQeCvMLLO8xGQz$aX|nQCp?@4%~ePJI&roX|CYvw9yRed)Zze zBA_=3FoCwMM8dUUA(>-tXC$Ery)jW_aWr9WG~x1SLa|&7rU~|AaWr9WG~x1S!hE|^ zPxNEHT~8*M@lID_eJ>Jz2#IJ*BmQDK71GdUE9axJkE44e$RBmZN=4Que5AB3={uuy zfP~~6AR##iNJ!2B5|VR(gybBsNCM|j3y-KfOU-KNX`LrBuyM-?>3+mz^;qsiv59M|G;gw zAU(KdtF_z44hPCKpc!;>(4m{41CU1g2bE%@16i}Ka=%lkbmzOf8@Ad$n2CRW~SATJGW-eF^uQfg-Cc7g?3%kGd43BA;J z5;mE@#bV}$E9U0iVlkInb#u$>IX46wt5Nag;3U_O?6le5A_XFoL-B>?exqK1A|o_i z^bO$?oFIND*_8NNphY376ckwtp7E0fK#yBoUOZNhsPF zWDy_$XQJ5wnmJG-*m^-(S_g=2QF~%|fN&3g+c>sY+rSib7;tNVrbJ>NYJl3YvAb2R z*F}#bfIPWtH9A`W(?)k6#*IF5b@#E2Br8S}+Q4qA_X=?#S`rWD9C_94V2Q>IW-R1i zy+u%UmauW^w(G8kMqRBf;XuMV!{#GrpxyxZFb`YK4>VcL0IS)yg5kjrdqkx|D=z5T z+5)8Md6!#VU&$3SWkh8Uak1Yx5TlA4+<-l}A&Gg|Ew&&XuXlDj-MtPwMu9Y;eg(}6 z>O=Ge@$jM87Wx7c2SIeA&q}TV#5r+$DMEmXW=Jv8o$^ zB&@>xChQDkraNpMU@hXgAq6@R=pFv_4XE0O-w!fy0;_$5v6^%I>g&aVv|3@Yh;kts z+K#o`>um%7+TH$vC&NhCsM3oGgQ+afEw5!SyA4o_21r?Fw@t4S$4zI9z9qEJ4PmXT zweWzb0F9y3a)WM9G>JxUTb@1GYr)Kg&54fyi3+N#ZICBs=NP-P3)9R{!kUBw3HuW6 zOL!n5G^+k?rv~>Y;uUxnkZ2Q>f81`y>8g_5&>zbuOEB0xn zs5N+_SU$)#zAwc^A46X&fIMZN#FC9AMr*o~u&A;mAVqorIfM9$*xDvTYJ^HxkwIK~ z2$@o2%WXG7Ux6!56m`wl*|e`6>`Fy(j5La4Tu~h3j^Y?wEMB%am`iQrEJ*mq*@JO4 zH9zoraI?e3xVEVlfq2(@T5qE-i*x9QRM@FU+c5*zOT4LsLb~7yG{B>r5+ao6=1xWV z_=Yyo$eZeF+l|@|M*vS4<$;1@*t6r4dvo%{{v4m&x6^0Vr|UcS^_b56JU)R}`W`*u z_9O0O{K_3b$&DRFeU_aNiyD?=6Z_CQYd*=Q){({{G4k->^<9Cv^KzySEOTXba))eHP>DG3jBIuSIwu45NMM%&k5n(@v8b_Z| ztlz*+OrzSVK>BL94X`wg)wZjBjltg1A8Hh0t422IT%}R$F>Gn9E()2(=kn!JrI0ID z=5Ca8E_y31trf9_X&>{ev6tnw)kVkbTF_^W-X(AJCYia)y>}V+@MNLK-}ybevLXUp zq~DSnj3vfu(#OYq)D>-Cs!-0Fl1WlewiJCX_;{>-E3uIR-K&Utri9WI<6nz!2AQe$uWr5K&=F4IwV}Anxk}R=Te)SV)4g#!X zQc|`A5z?#<(lv-P;`GK!{JtxrkskX|rCbosD2eA}53JKT1$3<*dS{!4r zD3f8)!jM@*(R&2Jbm3DViZ@`HP;-V)n}Y~Hl7xbx7AX^6nLWJGGAufmuWY3OuRYOl zeS+^kErLwh24M%Vhs61w_nD^hX`>u=ml{ydL`v_`{7lzjNKp+T5o0Ugf7C@jvYKu6 zMitlu6vmb!>eZDuVR`u^SxOoY*nbW?!Ca;xC0Y-ocppP@-yuVA^fQLZm`TMokHG+% zdNI|MRyAr~D}y2+8bQo*19Obm3Pp;~be6k&tBoz&#RpPWBn^H?$+RVC&0`svIFb{4 zH{(@m;&{#pl_KkeTDi$2G?yJDCd*560QIzy8E}Ihjx(c!868MQYK{#;6Jp&O>-<>9 zefMIOjCVNdY}C<#9I&95aHZA5?o4>y#w*99Vwj9dPD%Kj7Wdhf3usM`YIwN~WtS!; zN=~{%FYvU6<%$w^dn%n|I&hNf-0HSqI*oM!N_rWrT{g~EGWk{6{)#6?J7n?1XrpW_ zG1@Ove2H))S_B?S)b3R?F1^{rMnNyAE)ms#cH!wkT8pkH3p%4FI)abT=1ePhrQK z^35ch5D9zl1`W=8*inEX5&JK2=veCR!uqkd((F*Ma(g8>0d?TQQ61EpO}9g17v`wm z#dgpT8#D2gQmc!(lPqu>k&`?6_69MRWj2Aw^e2%VZva!?1*=IWYSmg%eNRLPj7Ooq z$$bzw;=^o8h^ihN*7+=CvD!ir9CWHV{=q}3M}db=wRP=N z*yT4tr65C@Idi^W-J{V>o%<<%ff#`zA)+%2c27II7t9!);JPFZhRvGedV1SAw+I1? z$H(S)s;4TOO_*02aLZk(zTDO2CeAUe6D1}tJ@2$tQPYukEc0)bjpmk~G*Cx04g;2o zW*de}@hgOJ9ELuhB5~c;ViZuq86+RWc_klv=7PjZs0&bF;(Ex*sL#qM@?t84CyJy@ zo&)b9x7QwsUmg1cY=Z%O2-jq~48em;4kx${Rz|Jc0Kzf0(UTJjQEjL@s*rImpGuWp zaFUNny%}MOtA^O|V>4Dncs& zvRGcKEN5Q4;bQU1;u@T#eZp!kQ>nbHkS#~$K*C0CR=1hvYN zhDZ4%$1)#;KYsmGoP3EptXhFcN}AIK^@z|N7pX7n+qk9n=HBn+dJ zxr$*BVKkOs&ALqxb#}6JfK3uGp4!B?m^!d)0c`Y~o_qzfh_Qwk)M~UN{e&g9(;RFo zWhc0J-iL@2hh7A^2x>`)aV>KELS{X7wZ*I^wkqkn8Zxc-N?azrIgt)v%&oIk8Be~W z9)FE3?i9NHK74%+7{FMC13-Wn}d1mtWXFTEifT7>Ja}& z^Z4V7mjDVNAf?{957RW5~%{I<9$MgCZpO=t6a@BnYBHr6~@Lhwrah(CS zV!4Bg-~eR%8vd4C4V$zzY|;)p+KH-v>aGx4MaN~8tFPW5eo`17irNQeij6EWs>Fy0 z90FOk%Q2sW?ofB_Uu^W@S|F!Zu|)d=2-LM52wnsl7GI#+p78W|R!M4s-09PdonR?* zn~G&(QL-1lO6^vkB_ca^*Q|uGC}!XG#jJ8Gfxf~y7Iqn^H<$?^9}bp-wSg5|<;vEe z2Mpm&33mYBr;q?|7RxIfkY6xZY{cf)vPw#78+q4~ZKd5o0~U9QR`rZfL(qs!&T3)j zU{zt#GFGd&`a$!R1iUUEa1$n$fGAUzb+gI8Nk%E+bB6?JX9r__N93>rqm!dEllkdj zzTjSrgEyeK*dA7WQ*ax0j^U(qQcCoJj?|J*-(|HxVnOG-O)rvk;A%3&tn#?-3P#FL zdD-S+Ze*5oyzT0aioXFQp1QER4cmQztwJ40D77eu`3Y^fy17pkE!UxjY+KCn3B_KbojQ~#h`R;qGs$3ilB?mp+Ij>{30+b z7y6e6)e>ZsVJG$cBEElx0{4XiehRCWRce5JZ29OR0+6qmA@Wk_Q4 zFJhbds<`3sNiP^qI;HRWMiYpk4mTZ0qvj8b3Av@7G`E<&J=iE=~hK-P39#f1NVBk0YZis;E%pwZwDk=@@v9Xv~3CF z)zNBHL0{l<&Fx}(R^_vFGq{qyURj*0l$L1PvA31dWsR?{Rj#aOR?GPpGukO;H>l@| zc?g7>(Wm9E7r4c3sd$ao%-59+>dqBQGAw!K7AvJ2D|2hhCC5EV?0Vy`?09!V0U|ab zwj7JBaD~b$Rbku_B>3a{gy8Oq-1?r&uV#vH4rF{8U0;PW;&uGpz~76UUf5}~;%}*O z@PK|36c~nXO@=*+$btoh!w0dK8&sBJU&6}`w>Rij!HFb_Dr_*75B$p({oe2V{G z!}!PRiKY5{APCGxpd_oql0^sTvu-^|_Q#hWZ^Nhf@2G}5N--NE%x^eBE2g@7V>3E9 z3!(<4h*|BEm>ih^GPwmy1TKb1RAi~43X+-Vh_0xfJE}5f^=z`Y#T|ET300M zU5TXQK&nBbu_KIH0+FyCxjVkc?7XC_Dj2|*63jr4H9^ks4mmJ&5R`PDsraOg!8JV* zN$Cfr92{DpNK2wuu|@J3xKIr{1wyJ=0)HGXFKWdcIL>{@G|CG-SUIQB7fKb;7kObl zFr>L_m##nSu3fx--euQIW%wls7ibT*G}40CPrvU_)$-aRDD}dcyOt@g=CK2vD;C#^ z?nAqaXNfb8tn98SLQ_-W*q8b z^P~@oW$znRtch)Ej5xWs#qmbYz79@naFYe`h6w?6L3ZvoOEq1B<*r|zV99%24`6c| z;qi7!p)ZU?NLlDG6MOMrh|tj&eeqxDK%=l?X)n9`YFD%-de3MUmZW~*vw7I)!^36y zMx_Y13nA)-HTYi?u_~B?595ih`9=uf!O_b@ z5;n0M~0FO}@Y(lc`ZH z?Rb?;_I3Gm{{~LL@eXvos7Ax30|{At#@Rc1o<;A8o{sjii{lEIFV|x*)Ud;N3zO$? z=o!Hl!b-2}BlOUels0y=20ebGdZMu!T>m8r_dNLsIH9CiW~ei$8@5XZXaE7e*P=>l z6<-M=S_vXrNv)!l)GDk5xCDh z_{kFNCBqib6!XfkCLw&^B}qcu5{D1C5day{HMh^;fB}Zwc6|mIciZi*RNsz`fL-uW zJo?^w=UfzNZ3 z8t@!6VT@R-j~G>9T7LPYQbBw0uuCaP6kB#zM5g(*N*VeynEZ>`%JRBr;id7oXaq)m{1Jyr;Bu~PGu`NX$*`38tbP^DtWiqQ*6Yu}_hIBWm|SMP$8!hAq~ zDnCg$hVEl9n^}x1C#S&A1g^?bA9XlQLpF{9cy<)PmJk8h5(0ojiUHVC0)Rt`0oYO^ z09%3pIJ^KH_96fdF95K;D1c*cu~G`)@Qnb*8_sgxI}tlP50=9rx&sR|q%@EN^;Y%b z42@+PHM?EU?;BD(meo(&=t8~|D^N)s=Kve^--_6Ugz2J~yRx1y=2jr=mnw5>*Bx$r zl*N=OuNQNY0m}0k@*378J#_L5`K(w< zq=G_bIadZ5(*9YIl}w=kvZ$#`nbp0Zr_5#OGC5IE1FxZvhw8 z(*pJzjS%x&tt_-_Im!t=iU+n#W%gRNi1rCHyrd*7^^%e>#H&I8Q4OhTiMM)uHdyaT zcX_s+CQLD!N{cvQg=7ps-ol}KtX|x?z)|CBMch!vb-Rr6umzWp)Yumf4Dm_97a5?W z$TuFr;`~_ANzvJHBk|SDm>^hPBcq{Kh zIf~|&qUc-!a*CRPH=8`oB}dt@0}X{zP!oJpY!k9JW6+J>4ZM<&fUl%*Q50acz&aQI zF9Je7(3yEx$WG!?F!?5)RAHig{>d{{ULrpfqd=junpw%o_LHT8^g4?dih10(1Wra_ zdiuJb}2!T0;Ui;aX7FhIj|z!;3n;IKBQW$m%6$ zaWC16ScwzWu!yXc;6k$i8@@&~WjR+WS)#=2Fzs&aZk8cF4RIP45wZ2QC91L(yj$R* z!cR5$oJa&lv*fKM0tRScvUr3+=oXaSVIl~LEv+??$iUqwUKwCqaaWa2ZUokFA`!Ma zj9j;B3W~27H>P-% z7?%94#{LrgJ{)xK9&O#5!=ARo;% zcquuyMnEyRii95HbgCT;>I#oj&H@eQ@F#T;h3u&g^r0X*gdW;q z+L-r05~BkEtYG})O-Kb5Y;hc;e*fntC4z1H`!hB-N6!PGj-=y15@Kfr@D`Phogo1i z!@VM-hoYhk3L)f30tnwde4?BIBuE;Ah_s<6B6yk0*h!=)QNUqu;S|I<^l}tJvEhuu z0?mO=`x8OHIAVxRf8C~&!a*39@X7G1_B%<`;}JfgEjPDg9oo&6^i%aX7`p}5=S~#W zdykc+@I?MK-5HSwJU?nXubCiKz6w2Go>Xr52IfSV~muQP<0fT(Jx1bLTFen3hgA_!)F(1LFu z-JZa?>7xjyEGZ14q*iwi0u6rmLd2{dZZ&Asd2f(w!GWXG1*&@7hY}?Rz2dL*)aI@7 zZopu-(cuYpeH*^_YgD(XP;PiU^BqMkp6)biNEk#3z##Z z7{GXi7j&D~qvBTU?yw=bWeMx>eENEq~W4OeahbS3IA@$r!?8hM~B>@N#Tf!r|H+5YFg@i(3pb zfT>2b5!?X|OFyU;Gej@U6ut0EevoN--)6)Kpe=PgWzIwbuXRO_270xWG#S-owIm7@ z;9CxhGNx00<)$^e%3}reXTJt{Qv@gcmA6)IyeHhgM z@TeEpM5#k$jyvXMiL1Jyak<$ZWSQ3$B~qXS=R0}yM;r`L6XQ;LsWm!Y<`cPp*1Nbd zzqxq;n*$exZ>Smb53O=fh@P9+MDQ}W!xWiqjOe30s10d)4n{|in0#gx^tfDxG0eZg zda%`?5lGoWHhD8ioz6P6(ZJzoKEI+~O^Ij*Y7A%`E=-{XO>Rs9-GV)*RLYgeWWYgS>ymC& zR$%cfNMdfK0F6d-L9y{)7Vt3Qr?da1Yy{LL_8Q`A4VBuNuB3-i3o2M!R(TiT58N;PQ6&n;u(3^ zC6nn_0W(*2t#E@pNUlMNy`z63ca*@UH%u4i3(+1X2ihCLCEZLEg#^o4ZoYzP%ex{F z40>yneS=j;2#mPTxHgOFFXuLTKeHEov zXsw~4hBg|SXK0y0o}B?oPB?Bga@q968~VVmp}*M<+pa#g5yj|l z!(D~p7TT}T1Cnp(Yr}1_F*=;ntDkbAB}FO2~lE#$hYCVfpQjPf@qT<4tY%(~8b zE+w3?XpnH#Yk?*McKqF#QbL4pK7J0YIhMTD#S6^~A?pW6cf}R?u2*mhiC#brugxNJao&oH>C48B#Iskn&6Y*o}gMVqTmK0an{+!p&S-I z;EBby99XR5WWY9{!*+345iA5-Jvc5^-2^rZTZo}Fe4S;=(LwnwABP_WBQ*qOgNxwA ziXOLXiXm5gRk)nPfjKT=039Np$aFYCL}MZdAWS>32-9l92)H5+BIp+}w?LQiWpI(@ z#@oG_#dTnAadnJz6GWtrKG2}3(7m9Ki~4v*AJ4*SkM#rN3xkF*GPDJn4WGqUQbq2wgaV*|0O4ICK&}nF%f_A`F&?$h>M=-h}we zGqQEJ3D+>_VSDfe2F~+pFV}9l4P0yoT^Eybi~Ya|>Ey--Wgyppz=O@R8obaUGGKAb z8lZOu>``>IU<@eM*3|;cq{~!Wd;u`s>IxuU0JAuw8XsVNGk%Cv=pkuUwGU7zsgtzi z`BT`j1v}_mrRQ34#PA)p3h&|s*6~PFm0N;sSO|D$A{0fDY6P`q$&1yp?>2$|#PG%~ zRd-eF(KxxO?r7i)vQPv-7S<@aMwn&9+$JAn*8-FaCVed2j<-LNLJ>SPEdqJOs3%ZH zk9;>7^A&;LBmHJobF*FYwRB8gzfk6Lkf5dBF>8i zx!bSNFwa%Nvy*zQe@4z&Vg%hi@z9GE;F$F@E`!JK`N+NU+)0nB;z9ynfvB%ZF*^dO zG8&T{(lSU1Pjl{yvL)mYE-xTyPSrax_oMDj$~HhU6f8nEM2f#Sm2RPEd~H*wVg(m8 z+R5hCYNu;)bcdSVESv#NgQ&_LPbU&oo9%hWjW+lg0{PJ`$4mF!?nafO2ADn{S7CVs zjsh73-@ z9BvE*((W|}4ZU@p$P1+D@COa~Gbwf4R2ltZhKR-h3+@#3KS;qFi5H>H0r&myh*40^ zWBF3u(g=ueV}EUvzlnl%!_^JJ0}O^axz2#PG)?3O^SI1Z#qUfho6qy7(a=kH$6#@@ z6^JmV{9*`S^6;v^N)c4C|P=FnJP=c2^z5O7gFe-LFfoHZMx3$vV zfCKL6*#LKt$@oRjrPhx@a)=nky8K!xQBv_u*fzUMw{>B?{N{4|LSE;wAxJ`iW9U3Z z7BhsRQxs$}^*Vn_W^EIL0L=<#Vp%1BY*o{xH?WI}w+iNB8NXc#s0&8XYAstC3}6#Fak)mjNqf1YoWDtxX;{&h{B6hX)2J9ruNO* z?FH+dCO3xN$nWh%KRrv*ujm+^=M((H^vVU@2+@dHgX25=np< zz3>oz9+s6`kZqE`CNbZjBtCfKJI|e;zc_n#?wQl)vzdj{7cv*lpPoCLxo|o+J9}aF z;`y`LXD`ma%M*=8-z%|=dO+Pu1^Vf*S(6o%=N=l}`LWzzIQND(xUc?d-32(i|IFF5 zXP>Q|KYfmSYc%aFHCoue9@wiG)|SAfFicjXKb52Fa=ez=2{oUekaofOTOCHLxPXp~ zr;S&8;q-X57fxT$odH?$M*v{Fx{GM`@B&bcNBeZ~^enDO)nENtqjrNWh^^Rhbft(B zKCEJigH<gvHIP?e zkLc$+X{D1KV1 z3fn*fF=%B-!BH8+tN7c%+ZLWpq&@21jQ_Llto(P2MU=qayu)@23{6eMn5TtQ*46T* z>WJ?n&U%OP+(g(%?EnM5x#>Okf^#!Zp%-}G^Az&6P$I++7{6E3Yx*U6lW!=~M31G+ z{ZFAK+lSl7@DxgR@sly|EJww_{xN8L- z1&DEOI7cgZs|)lXgTXEa+XYhj6{^3qR709e51o5@4d26@@yEp=+TiP>XtRoXcO~9L z+N18Io4WnoClQ&{U8uX#5S*L@=tLYM5xWZ0A3y^G7rLO+!RlZLLP*;rLrq~L><*EK zr~+La*O9T!Wc?fvj5kr&r{09y4x|)8MwHt{39W-@HW?%Y9D6Zv?(PO=T|H_zv+_?&AAht zIo38c-6l;q@f6}6#vWvB0E*H?e{p&o^rgV`*D)Oy4zygwdgmhGiXJLOy!RB|NE2J` z3Dm|uz#fShJWkQ?TZrAlljC$JuDcuVMbzd_Jc{QHcO5Z~k?8JDz(gvPf+a{H_mF1o zx`*&fgaSSGVheRda79Z!v;ews0@hDie(uBu#=(W?PV~_VTXiS4qS!_h>pT1s2py2Kk}sCiaxvq?$O2n9k&jc=ml6T z_Xg&;2Nq4?O^83FxS+()T9dXOI;;lwkd{}>#N{IBr|-uJ<-8cfiMLwX`b)qp!iTl* zyVJli&b_XgJRoc}D5uhVTp^YNZuY2)^NnAFKph;%)N7fq=3c@~!VlEF75MEQVGXw0 z$cdDv4+Zxz*5}xoauafAlK`xJ!@CW%&_&%l$YFEmR@m!g-MZqjv5Z#3>Q}Zy#uV1L zuFfW~hD&cV-pVyZhM_eQL&#~h4A)Ai zpLEH3$sS-Q$JYW*K0}ypI`^&|>MEjl8-VtB&p3lDXLPB7d1QWEl#aGI7nGjh6DuQP zD`E5%d?DSeAg9&>|60^UXyW%b5KcWdi8YhLMG#wDm-=_57lfMON3tjL(sC6TuN3%E zcM5$XoY|u%+|>Oz4v9Ak0RvrA(~qJD?g2{;q5-h@B(ZoKeI=E`c7)?<*~1&ILc+UZ zJy!HI)7{fA0T&G_uLMQpzx{~}vXl7glv2DkR(3k-X6R6|X=5DmQc0$Os~ymF4xH1* zx+zvuP&92E>JWx-c9PYj`D0sMd-PCb#wyl(+( zwIm0ty&z@22HLcQr_jFizH`?&r<}#4z^FOSy4Rquv*|McqLevBe~E!HJH*6ue=Ed<~Mo44hH;T$_Z0M`1yjeJ*a!bzDw$ zGb>u}voE>lQNOOfIN^9Bxap(dteoSoLr6?uib@#nRwV#TIm5qXIIN>kWvl18hV zUzN-oGIEjPbT^g_^YrlKk{?IPNyttj!O7wB=XX*xE*HjBce~q~*#o7H95|P>DZryl zOUIfVpY?;IAk(gTh-mLPLJB?BQ%=m}F%w$s1(7?h1dl^UbFwBAj*QeOYYoepuwgC= z3tYmiS#j z74=f=V);|(-9-@N^N7d$^@K2A8%uFw*(`cDO0LUC5KOkFB;*OSX1YHG9TQQ)__#m` zEB5aa`uFUS{mY}($>Y15J}Nm2ebkji=I0(Mp&V}_xr_#`l!w$`>rcDG> z4Iu#cluRvw$U$;>moYQ~;}qoIFgdDkCclw5u=ljuaj>-ishHV7*)wkHJnfIB8)^U4 zipnXaEEIZZJ#v?HS#9y+{fuZK!5u}g4{iX`#|Hb)%a*mp<*L?$!2u%EO^&lp-i? z6COlucOtS{!;&o)ZCILd_ZD(_Yj*72tk(Q4d11k3TrA*^9A-^856KlR9cpdhclR(C zcd)mcwk?nqD}mZYP|u<5oO90{*$%(ErwLgSamw5VUDGa1K#48eikGuuUA7fmTc{ zponJ{8UpThM7WBSReY_WcWbZ_xzkzb5(T8JBWDij4B@j5E6+MosavcjaxWo24lW_r zGSYD;r&~a-RcUi1_c^u;hjyvEf?C-hTzViS*zQ>UPfqITiuAA`tt^JVoM&E+B@az! zU0UIonaW<_UP~EE1|yF~`-1j2gZwzvBmKv1q3G8#+PH!DBS-%*N4kulm73hx>YJA+j z@-})LB13Iq9(}717NiZFXu^N?+^4_p$dTShY+%oP?)%r#*E~iPr92!`IIacsf6}*Q zl;TW8-|o8(IK^tt#MvWhG*^srq_Cob-a%SMorA2^TSYIC7m=Q+X#2`_*vfU!kv3Bp zApN!V71|l8ip|R-2{B2?<$}uCnvQG7P?S| z&{p^nx+)Yg@K+pTgdSo1%SQ|GUT7j@A}zhZUItZ=IZG zj~&)ZQed1ko+Q6_@6MoR$|mD^*;guWDujg{V3VhU`ZCED5k{zdfHZdlby4L}ah^st z_XHtD4oJ#v^jUdoTor;IK21A=^ERVvz&%<=h=T?EUlER+uo*oP=7q{ZM>Y5jM zmG4oSU=3`GoNPm&Kns*&beJ#QD`tx-{SaEZUrac2N18ccHhdFzBksJ$(c0XNvR7y0 zwVM2>+?o9&UpA>NiCNlHnn0Du!?j;vrh*GeCB+1`L}R`Z2v;L|QLm(+#|a=3&mkAwsPV^%Cc0CgnuO+R zTvP)qg@*$2k=*nlmTGSvgW2=dwK3Y8L+gDNeV<$hmn~6H6$Orhx*i2F)qK(S#;fc5 zC>e;uCkN=z96Smikkd<9v`ct%sV8j|L^LBR;|mT)NHA}4%NghIC|gXZ?aqzG)R2i$ zFdbeQ6;9a}`-sbA1(S3`gNvEiM8d^It2n1>gpx-5*an~sYXc0v>bMfeH?^D*dL$3J z@RJ(>lX`y|{U`|baaQyAs|iGvSnCL;Zhs_Bo$h3hA#iFAG;Tr*TA;4HBblX>xr3$l|GFQoP#oTI{Qs%&X< zLIy>WZ{>BVbB~wh@UjH3;BInsoBAAmFzaF?y8;1aeq=B@G^H1D9n0PR zzW+EnNL(`Vs7%AuQun~50lCXuv~i32B>KPsbC!Zi&APH9z;XB5OjiKV|ENG>9UB43 zv~*MeGd7qL6jmv`xf8Viti$Hv?%4z?&}F3 zQ;%+4Z=x}k)lR|l;VJylm>nl?$`d#XL{sh-D52siog}+hKe%;o!i1tPSH6*1RWv3o zAyvFg#hTOt`(i|s`w}p|-~Pzm0!1n1q``5hRCv@Lmn1Hu{DUA{1t3W%48p!7u{52Qhf$0)w^jO3CKa>O43KXB3+BC5P=o5t+T?6 zX|bN#;V3(JA6>d^DT$DAnNlei?UXukqcm?4jHlXn-WF+MU{!b3TJz3ZQ#p=|?XFrH zjFN0_IjRhhgkazqUv-yl>Fn)e-(?bn-IsATN9o#^cQ(gb`?AgESWA0%I-8thN(O3& z8K2P_<~S({aLz{Ci)Qrp`<7wC&xypsqPigUD8Q)c>2Pm1Dr<_w+*}!LY?REAOsGGD z?E zJpDppM71lmd#bL?(&KvX_}U!b?m0yKoYDH+IrfUG#vPYDm-FMe;}Y*WdR%K`5KZ<* zJ!|M*oP=i>nPP@fpb3KG zn^C;gwpU)E?MY^YSJSqZcQd*wjvnVE%JeGQIueGHsMf2fdv9`iDpheqP#m+lrAN%` zU~TfLRtf4qs-=u?5~*#&#JN80!hjp8?a?cI5=Z!AR=C#Z&arLMtoW#o#A=jKDtE^v zu+_fmF5?fs&xg=uRggaHC|Ht4jzXVWD2!E)(;e%mem>^hRtYnwLKSC(w2UJ?y8iJ| z8Rdw}F7b6f^_)@PyD57(y?o_%Z7tnRyN2s`S5~>-U6gHnDd_DhLgvU;?v&ELVk`Ld z)k%iqyMT&N&a%U=bsfRu@0J3*;y8rvjjxROILz)!%P$Q9+=Jx~)VP%RDk#ZKqdj-N zB43H##tkSFDD4Y@YP=Fu#x98}9JaR}{O(RkjfAbO# z8y?A=v=bZXRL0HY-5eizu#W&z?UH$!ht!=dllxjrp$^l0_1I!D0;V1jQEMfA#Iq=y z>bk#AzO{#+{}_f9w;{EW;S-{Ty39u?X5vhe~()=s8Nl z!JW>bE}C_@d==Wn-Yn)royI7qP)8+X-Luqkx%}gn_0hasi@X~lxSzXAxI_{)sxN}J zM^d9ZaJ0`5FVy5qBQ{4zjKg8f)1;7u7xS_o#7KHL1b4Tv(}w#N3KOz~4u+wuAYc3p zEIVKWjyJ;zJp9b7E0@Kyoj!g+M{6hr)VYS7Rnt4mO)tB<`x_mKID zj(y38N#bZm&z7=HPI@?Km7HwOf_j;{*M$Q`rvzG?f(%40Y_#6L;EA!CF>tE!UGbA~ z!HZuF6>O6Ph_}1wNxA3c?Ttu%N&dpAx>r+HPuIQGO}+g6L^-aA`1v|Ive?Nw+gd%&betEhI=~mBHCGY)fiElW?_lRz~ z=z8N7Ae?)hGdlISCz2(UOLB&jqd`p%vL<3^vJUqckIazu(c6@CA=y8ae3&IEH|lCN z=~XuTpI(*v468U2#SNA3xE6?&Iz~VPM%%yWB9YvOehGXg205;7KD3=L6vRKO4QKNSr_^)4=F zE^MF=+;}kgVpuDiRp%x>Puhh%Y-cfq6CFpkN{Rra5Nrj1mTG4_CP6nqVQ;3n2 zvL=19UljATtRn6Zi+&tF80GCxB!~H>n-bDtrIAK|lT#CJ<+#fUf)8|jf)4BXBINb? z2w;kr1Ydk@1Z*BdgS_oD#i3q&Yc zkn5w)2d_xKSW6v(y^axLVrFvsC_S{LGA@s~D|V^SBx-0H%xQOL?Fd!MFQV7lQxn07 z@<;8Pldc(`C1$E->foYv7VraodTt)6bj~6y$=@dRRtZj$6X3Y&<0fHDwv-F?O+Ssp zHjg^c^y#m{MH3DeQ_zM;xKVyo-!0tMix_bv$X8|Qd8E(2eh1(5tt8xsN&_yCMz7f~ zUbYB+a&mGm`t+06(RXY#AFk9qyuL?5eK}wfp=tW4)bnKc&U$JX14&T^?I_>VQo0u6 zd8w20N+)X36LUX~pGd(9G0PIw(|vbU>L;g_8gVpNujJB0NG}0GCJW$tqnWn?-hlXh zo3Mg%{44_)4PHI`Ig@dX-6U-46yvrOQU<*mh*A^B=}uGDsiBsnUBhS)DO2F2?#Y+L z-jLM)O+jbgE!a5pn4)_S*R;$0k!`6fn84;9{5CAqV2R(0ACHy8qWV@q2_A}-w>QPd zHF-2)w6_R%Fsw7=87*nuN=FZ$| z3ZSFk)C($dBnpnJ1b9ksdT59JbJckd{qGE4=JZJVFU-)C+PD3~{4ZPPyAZNYgV1tj1jmWP0D>;axK$*0{JuT|mzs zQ)*SI8GhrfFR+Uo&{G?ilOQgY^Qo}If zodTQ^2Khc|paPVj=ioyb#YT>4yx*aZuYru9ZA>1qg3crs>i(p(lu?v!bB*xw5VMJ~ z2SI<*K8!jEN_IIGwFNK8s3On zf^C@n9)*C7TBVt!C5CVg*KsyMkICOyLYFlu5>ZjD$2u>(Hi8s}EfHSiw04;UXR?Fk zNIt5w9-se{@|99U=s)v?8riPa!5ZS6)o_S)P!qV5T9}WcISe-foF`cZIbXc}dv^pQ zDFdnGLn3w3Jt=S0hX{GnXVo6qGS{Tex?u?6M*c^*s1N1_O6hhv)7YxY9E7^A3bjYX zYf#TGZO5)tq7W_JT4>Scg{SC6z5%6DAP!; z)RM?=ZTwe>fqWx=h9@Z_Chc&nNB37*GsjDPh47)oaS|)cRykolj@!2J$1gU1J`Mlk zD_UDlG6+Z@xYm)-aOa+vky5f&&CD1CX#{)LLanTu6pC9+?i->T544J-3M#RTZx-n^ zD*zO<6hhQ9B6L5f79X0O4Us_iP00#4B4b7rz^z{9;S>;%irDcHPU@o`2}=?=mgkh%lF=zKQg`@) z1M9^kMeojG77&6npoG&ClZ%+kB#beT#(1jrd@?rJQX_FR(i|?Uprmlv{G0-e zRpr`39+R@T+&P9xn^U~0jmvix<0664nN#fL7u|mW)a||k+Z6#)e2nkd2 zn#*zVTZsT+XJ$I9D>-)tf09J@m+cdZ6op7)Nk*(B=Vny4A`>U^V82vN52H-BWDGR4 z!@4K}7^V~FbrXv*_AG1No*oEowK@%qdLCVk( z%B-kFs>rsfO_~FvwH^AVeGc66XzoM9717bo0kcO+$!v5q-mu5km$ixQ+oY?B!A8PO zrt7FtX)<9Dwl0WDNe-_H8QY;?sJ+n9P>k(iakND|)t)IL(a4d!yWZMPLXk+Pd@4YX zkf8xSxMS)9(>65>^j#G{irzXiI(Ko^&qylE7;36oxvDp?R(c}oJYGVCe6=YwU#GypU?erz-p4EAV&#y{ zYQrB}QHv-QD{Y8;H4YN1f08H|%ClBQDS}1ij7d`*$h%&-Jgy9p*zsqp*dr6csS&du z+Cs7?mZnY^aY0<$PYS?9Quc5F>3wIm zC4+X2SJMY574P}cFLtTffc}1>IXQur9ZrHqr{2id=t7-1?e`_o(!KYCIbeD){ph+F zRiu-JDEq5=Y!r~>_h`&vXX1EoKOS{T*kK$C4^wrMRLG1EDhkHwP`H99PJQm2;*hy1 z=qRLV%@p;NjZLB$N#-0x=)j_g+~Ymfy40^~oCfHAfeO(|o%Cw@(H^j`CKiP`P@bnc z5Or1B&doY(SE|}RWBmGhYBz_`=V0-*-Uhav?3Ib(!Qax5QMEhjlA)FOWip^l^7CFB z@dISfJ`ar33+R(PO~pBzTZ#bGU%4ATpdj$x-!gXu>o7_EMhsvryb+a2hV2lQc1+1X z?O&8M)ljWdNHQ9iJXB^<4aNqp%$8s1BtW)el2wo-CwE)ffy$&>3z-2~QJhI7r$Y0^ znRFcW<48esMleCdzbHE~#uM}xrc4@Pd~8YOpRgu;h+gJy%LTx>u=<(DdTlV~V$+Q_ z5(gqosLAHWk~@TDSFZ5`wY@ zbI~A@8pZ|tUT8T!Cx_1#mdU-X_!5DHeAE&#J)K4?ER5UVcof^#iZ~f+(4RDQ6X8Q^RJF{CN%InaWK3oLU`U}nz}CbA z=U!F1E9fuIDBk{oNkjb74$S})@2c%d*h<%pU@~h8x5`>94~%mU67fh^*lo&6q~@FG z{)b;p_pK6z04Mw|)%&NN0qDD}5p zEv4-QDpgE1d~-=WNNv zuwc0GIBQ&3CxMfS1C<1J(!C_8*hx%|f8lbC4@(I%F)p-6q5=$faB@oy7h`aAENNJD z*w!3LLmaPcB5XTs7Ij!F#-bHw><`JAosI^^fUEU@ zd4dg|3pj3-Bq_40i9neNO|z^D(`W7(bCM6<-+Rv)t0%kod*3-_aY2|7d-DTE`YWJ%$lMmP1I*OoyL$xoB!z;Uw397cFa3gaGXlN8!G7t^v-X-Rz5B&JfF zl1bJ)Di@M~k9yp(Oi;kcTXffKv@NZhtiaT<@%lO5$vV!|J(e2)U4I^JlKityHTc-t(r5qjFLjrtcjcN~m!E`j-Oip(7NDl**b!7eKCB55cMCHkwN~kt#@ymomOc z;Om5w7ZM0IO#}r+IYpb8R)u?jIBER3Zn_w_`<@4yC}<8i?DycRT6H$DsyPe6E79h} z@GQvNqjC@BQ9b;|fzr8!QW_m-bfA<6>DiH#^2o^99>K-gWQox>DfRZC8BvJlaYQu- z(D7O=Uy3!8dZ>eDE>#3sHw^$34QM|MM2$I2>|hH?gLiUlarjKhZv+Tv-vf)&=%vHX*e)l?uL;OGw6Mi&IB1*9y4*+C-y{v0+xNs~6M&OP z#Z-G##3J7E&;s4`x!q(0MD;mNqngGj#`BP`8fZvpQ~(biTe>U2>t0p?e6C{JGxCY) zVtu3lrWbPhbR*eRgi-x7Iknb1BX98?0A0U&^xGDX)k@Qm{w`49b<`G<2$@6@ZqiVl zr@@JIhfnw3pDxJ+CcQ4Ss$|x~0Avbjz+tmcnFTU#E{fwdkw=kJQG_nvWPU6k2$q6I zoqkmhA{wZ*;i$WYbdT~1YO)hB1n6iDcDPJUOcy~7&dX#b-Io>YL;>mz(;ku-9Q$NPDebei?-%zw1I!Eu7K_=a5Ds zAaspafN?311_!-duLOGv!qVM+HK#5)qyN4qS9})P zAd`Bdszk(PraKIZm?Uo`BD!x$h>RO6M@1X!fg-&kTj*;bf~=Q6z^?xU^GfxVF&yL))|juK~gU;h7X?_FTzy3RbmTh))MZgv;hMbQ#% zw$)lmLvkci)XNsf(oj^3mN+IwiVw>gnUN@xCOIM{nGZ{{WlVLq4AYnyWG30gWRP)o z7pnohiL!A5BQ_Eb?9B>T2Qv=5ffwV=1M5wUz?%rW2BIB|k=7&Bpe8N?A>Dpsk^sumwv{gvkUSY7~-&b zR*4&3BM`r==RN0E*AsN%(dhR&%2@;>pwZR!Tz#>-4;Jk>sL-9X32WRT+R`=UAzslc z;bPU3g4Qh-_viCohd=zfvS>NVEOxDQSE^{Iw{8NI+LQR2HlmnYW*ggBN2rpYMm_N> zlv#N=6#nY&NVWOVMCqg2+2@P~acRYd3sszx7@LaP+MqTZfJ_C>%Bd+`OZ2rHQl7m) zoAHFX1NyT&5?mH*clc!m1-2E$JOvlyB|S_GEZO#NEnVyB>LO)H1Q-2ZCARFb3UQW5kQjFTUc)Q^Ks9G4XbMaV& zcL;qjPq7bnc|m^xY52={PR3aF6Z^xUauApV`HQo{L=FT4tJDuxpiedeR}P4CW{|df zGXVrzWe93|I{ zR;{Y5H8zubm;6nClK(8RRA~w z!?2jl%W*=oSL1Hg@B{tgI_<`kDpRh!F^(yg@4KJmcW0ATu+s$2i@Du7Nf2m&xyube zh+tg%WbB=XKaBqfxRl3Fbht`asH4hwRT9Ih5kA9i28nC2`zCdwzC0Z-BM?(roeCDh z1Ds{`WXS0EI$fyg{=(cdI! zfKNbXc7r+dq)^=C$WabL_R=WYooa;@%qsYDmW;o8a?~$9?*oZtlfr*RvWvOj&*s3c zRxfWN74R-<2=24 zJPglzstPhf=lLDesI88y+~}_D2+!mfHI_rVasz?P=w?;vWtq0cic&^uD9o((oN{2Q z3@Xgz*(7)f4*5-x?ZGApY1BQcd!a)-7zNYO$M93L{6hG!%;g^0>OpX{3uzPM3C&`r z1t(_Wj5%`7@0oBVC7LavCI(-f?+2<-a?gshNQl3lenY6&^&0|XBI(%cLIBbJnQQP7 zuceieFZt!$my1REJ%^y6&9NuKRK1R}PcfMDP$-9_zbT1-ilD;%{T@vSs1?A*#C>Iy zde?(;iWGjqA&8jM_9u8T^M3gms~-BAq2bq=#A&uJR#6;7UYr~7sE~W$#1xUJ{MY5f$vLcCts}xg34^6S7!J!4$4>$IP&68aF%gi#bR}f9_u6M&J$T9 z#L#TkrEuF)T8AFw_DFw*gXsEAX=v;eo_%n18?{Dy)-K>6eBCSkbbqO2q$2TVV(F?i zrAJt-FOfEntH3jGL2xuYGe}eB{(BTIQzBFiG#Dh`j#WY<2cO1M#CB{<2=|Ar2&z@3 z-Q4UERr4KvWZs|GPC$WvnP(=;?u1J}vyym-xn;~|vrejNqlkf!P4vRC6evKyu&INg z&zd4Em+3Ls(~b4LXtW%O65Hhjx-|-6N_AwCxT~CFu+Iqg$`KjxBE#&=MjLtJl_`XD z!Sy*J0~;Z=(MN)jNV?k*EPKxM88c*VvmUQv(~&MRQjKea8_PMWh~$ww>T<7_?vv@A z@r7i$J^S^&Q8>F6*&C^9?t9gigFtJ7h`k5-F<=#}MTj@3A80Zbj#rI&v354~>qVJ& zID~|~UKaRXJ%or7pIv0>*HyJ6$E(jDEoO`s=C0PtS91==Q}im#EEs3=b3fT%pjNxz+*BpNo z&WQ3=E?$}=dObL3UDNxFnMYJ~zIt^!f_-xU+H=SAIkn_p`TU4F_8v6F{NEg;DeS$G zYee&dJNrfO27i_Xg9EnUM{`lm1yc?TUTH)Ey`R@AV3W#IqHXQKqg9g;a+JZ- zjc2gZ=tO7Ycc=C5S< zVVvf1cA0R3pWYWFw8N~L;pVfOF#6&T9 z!2)El%jd4cI-0%k#eZ8cWTMlbUu5ffn9aQ>(12CEKT){)_x!^8o?Xuv<}(~tG`Oj0 zt=GksVIHc7D;xFYnu3j;NJ2Eqj;pz&JUm$Id}(FPUYY62)m}B*{pPAt9x0^_3&AG{d|HqFkhlSutwWFWJ(iB1m&r`SHb1fkKYOy@uk z<1o5qLJhn0#T~m-$8d5;TAWdt>c$(~@oI3ektA*qHO--7v7gA zR0dK$Bcu_HgkKij2DLc~aMmMMGKv?0 z9F>UmvB3%iEki40EFzy}v@m5>I;zPJ<)=Lvwfrg zgSpPCd=Q@&cMPt5kn%4MzZa3t*{7p6F4ViSn!CXfbnx%NWmp5@9 zt1BLhE&FsWJ8?NXH3&Pk26HBq>D>IddYIh_JAg-+)UNB?Umj0DfKLvhg@ zK8jHiUd*6$SAR23xZ}`!kz5EXHJ0rlsv{@CYWEN7B0h@!FGFO;jW$JuPwehddMkyo zm$XuFBZ)QiZq!ZceJ}EUrKru~o7qB@{k$^qm-p@D)vack-3y7tXINv=xKm8+?_=HX zw2DaUI$#&4c6E>4BZMnoE)1fu1^+IG3H7!Crrn4fQrI$;mGxn=2?!{s(t+4{)vj1?UXY3?d zvl*FZi{fndNZeER5^o2w@rXNwM<4>8?A_o*lBdtXi=TfjJ#WitFXpx4mZBKh%**B< zW04$?2csb7XjhLq61yD`Bka~UtVn!LsungNU__Luvp9+fM#NQ)_QFZ-uO;W9GBbz5 z$<8rh2~1mbe(xzYXSc_pBaRl;vk5Ff03b4JPL75l4~XF^VAIv*pA6LUB&-F22BlPr z&mv>U!=OzZvquZ^t{#f4@hn?OFKqf+`>mIQYcEiFBCJ36+^un2i?J;kk23aSxy&~P}15?acoR3z!H#;D0V!P z-fqjAYY&S)hg9BE@9x#&q&d=}f*8%yUC1{92ZhzV6> zsv}S`Y#AD>`V3aQ{DdLro$Z5k$un~Qj_3}MyIrX5kf>UZN>0-{`e?BI_AHiSVWBtD zgYnDrII-V!i4J^TXVDQFj_q{}LMW6_Y3CDaa8_chlV`%&>Q|N6uCR^x2CQ-OyohpJ zRx{O|5J-OYuZVPH3u&0)$XtcUA3{*>(hB5r3dhlgRlS`c+bL>*@Yx!N{aVRtN=Rv# z`|-iNya$)X3D2^io{0*m4dNSI>9TD;qu*AGB$ud>XMQ!6KPU`VZP=(f6 zj%;+nw;@^@_nQ(_OSk7)ni~@p$R%UQOc%cZDqmZ=4*OP6ku&=c_;I0#2U8v_?GjeW z5!;snw_8e^6WLGdH?8J_YRL;yi+5^WNA7a^K{&cD{7Avsa!M zYKghLmX^6GD`g|}$Ai-6`T529Y;*rF(q~?+&IhXUf|KAO0}P13mFB^i2Wd*6o|e`h z7=S9}P|N`uR18hBoYrs%x?6RHoFNPSa9Bs-Ht_Zs21iJUAJcEUmq=8kBr)ZvZ6v}` zN7>gUq(%i(B6OCqW@nI^aSd!1e8=V)xZv@qeUR3Q*ofLsF4hlhy*knEkGJ`CD;wo@ z+@-&J#EIy)@-XZ+)wNCkKB{|^xIe8bsjcn5UVQOt+3BRHcCD-!&n~iI=f8#x%}^{f z#?P_}X2Os|DioB>4af{O!7ITIE^lT6`9az$gSxF?md}aQ9vX|CxS6Q!HBFmDp689k zp?l;{>=wa7VF3LBc>{+AT*u4N^;wA+p(u2dSFxF8fd}Pf zMc>&#fjb^V4L?ABTVqW1$TRBhsS?RFT}gYcICl}hFzCE@*!!BiobnXA#bWH;LUjKH z+8+9%_BI(4PARMFcR!;LaW=tEz9M^uW?Jg3PSawy4z8UPv})@x{-lFG)UH{aei7j3 zEuJiPsPGvv>aU1h!@$w&UV~O_enz-PDP?u;*L~w%*{AOMRjcB+{G)Yf!dLYarVZSH zb?*wpX0Mg2+VsI<I_ttN9Js9CfH{X`)w~T8KM*qfuPtI?*vLLv zo@Za{e04S36nt4(XCUaZC$s{Txr;MMiM;l=DAXK%RQJk`=pYsE zBy81Wy1N-Uv_cEz{9KCl_YLm9wTCA@7>Thdi6iA~UTmi9R*QrCAnq++CfUj}8b~WR zQ>?!P#MWzNh^sBf4x31Q*3g2u^S6zHjI3*Cf}Fe@L{4ld!;`(t27+OMoR2DlE;d-b zQ<4&|;nVN4J5whJneihD1~X4HupVLPW}P#AkJSIFtxMH{yUR| z-$R1lQV?zmmkSlKe3#0~(ZjXb13=MQdC8rAsEjT{9kU)#Ma#ERo7|a5>Cm>AtN21t z&CwXf13I49GSf_l*SpuYs0#ElOH%pH6QN!gO|5IA49>f2VnK5l?W~TfS94puW{0>n z;K;wDg?uH30f73Mt8DKzt;M;vmcpZRn^zN{#FMp4f8(G~h~|tW0@|ybp2+6TS~$9D zltOpgg{q+S@=sP9<-i)l?4&>_2vCdhb?yp((;LDc>M9Ti!=5WscF(J|@;v50NY^{? z-`@vJwOOLK^(@2onkYBe;Ai!P+Noj{HnJCXCHaVehjzSBg@K22Nw!zg(dx7DSu(3^=e^aIsg9yf8+X9?pyzKp1V z{eF3LkLeAiiB2Q`ilj$M0!jh5;R-IKH=!rW${iE#lF?Uqhx!?5-K1VSm(GX)@m~!6 z+jYTVi3L8^eZ5*GyZnYAn~7|8-ai8^LjCxZ6v~oXgmXd=k`Ieww>l#`z_}aM2zF%CA zK=>~4G6^Jj|0M$ijv=@pXr; zWG*B#YiBE$!wp)hR5(05^Sb`+fE5K?b>sTgSVkwcGczLMBondMAhVPy zk0AAuV-VC39mmovh2qYtYB?MaaE9m!u>JCopd80!ftz1&?j z%)SflvphyW!G7)&Jz!};)y)K-cAHF1Oh0|Vc>%+Y7*UmQST8K@Aub4egxFP~S5@p> zUH^i+aGh`}2;;4ns-qnA@=$8E7cP2Q#Zs9Es~E}9M!RK+;Vy2`#nivdSu5dKxXfJY z;dJWpd}OdgZq)R_81nXCv>urrD1rtmPyPT7sFBUY@{|b|5#0kF%jT^rgG(6=^gM%j zaMeY@6>rV(fB-!!V@y6VCAduC78EnNw7I5w$&N?ZA8zjh-v}{$|32wPE-xG3B zXK+PMf0ak%xC&lRsy|g-8LuX)zGo)FgI>#2ev3a#9_Phla~^?}sob$}ZWaFCIX~kKYUeyttj#cP#B$dTEh&#jD-w@yol@ z{!e#kxQJH--XlxG_Z=EK((3}}^ge@Xz5@_!_yYv1F;sxJ?ZqSq+7P8d4v2-8%{R?1 zuvcVKd7kbdm?lDw(aXZ0x79QA;)GxpQRqQ%{B9T}YKm-Cs0{=SqnTTXCwrlg?>fJC zX=IcuC_fr0fLQ?2a_#geXQ=^BI&^06zC{6trk{|Pgu0XKhd~{2- zPb35&Tb4e<-QUN4RxPt8eX%`wNB zC?T~|!@N(VJBuaPGFk6{{_WFW_vy6<1&Kv7l5oY1*%x-gh&;Za-@aS0=PQ)zpK?I2 zOyNjnXar~@)0CN7Y7GwDvwh{%mo0fT2>;F234RQ{3AOR~7$+?{zl9;)uS%KONTHnH zLJWFYoZ&29jevWCljpeAWJkf070Pa5xhcC}OjruCFgJB?_ZgTw=C868hgx7Om!Y$j z`K#%zDgE8ojDgNq%46ale?x9EJ*orxNIKEaY5m)x=6GwAyJ2|)J%?2So`mOkBUpbq z^D8op>^m;3-EN7bg{7a2P9Nw#=}rShU#E}ZJM?$j({Guyd$<1XQTp-frr~~EJ*Tf} zD(=&J1#Nka+#vq62Z&5($t0qUNwHuX{~Ad~3-VWd_G#U}s5{wvf)TuK(m3)2Y2z}M zvg{JGArI7YE+u9?*U9S^z!4fKO6(Uz@v7)zY`^G+>=)0J=ND0b9#canEVZ@&(k1iO zt3~JU4>E7Im{x_h=6Rsal3?fl1%Z#eY*Nl?pO}wei$qB*)&rG;j9`pJ!k*cpgNkrD zuVO2AAqtgnN(%rej~Bn8 ze|UiWdsec!PjXv~JsxMPairjWPdYE=^me1_Xacug2o-}Vw@5j>2u)d?F;*73oC!sQ zjJQD}3(63Z7zfFT4uTt-FK--$(SRe%!Wkk7XJUS4G;wuSv(2l#>cBEDh6Kr17W%5X zwhEA;?EKlkPpFy|D%fpX2I@$!h+jvWkP{q+qX$1`4r3{u(56N4$O^n~$C^ORPS8QE zle(w)gr2bzMv!Y)?|!*lcWYMRtwr&hhxB}2lM)Hx@HA|IqV+A3NpvuBP*pkAL|DLz zOdw&5n>hhS{rE$X2&=u2y&8171J01OaG9&JV0F?fW0t>oA`D+S&@K)>DCtDjF_q|) z8DZg9H5zcM605y;?pfl_y}}*;7+289Kt%64kET0xl_0tf}9_(sK!rE55-xZ~HmkcBhQt0{^b$5moW z`p2M^pU|uEw45jBG0-x6)Le?6o`ZXxbl<}gfx{%*dTAr zmNXJgO&bUkMj)NxQ= z<@4$hbue@fjV*AgL=V=ygMhGhM}{BH2!W>G<$tM;#j<5pLEnk!9a{yyYFd_^sUb`a ze73(u+Ns^~M2ZjTiT$B|6%~}LRr-aDVxZM8EtLfC(8T!$c<{9@r2@)gb=BUoC=-QC zWP}Q#m14|JDrfe*bauPzuCA=6wD|+s)&2?_;Z%0WX2ZuA8KW=!Sy?B%CRn3XFPs_H zM+84S?0iTuc|fhWB|)U^Uy$U8d@&fhy8e^t>V@u3&p6PIii;`t6#Q5l6e*%Fzwm>8 z@?v#j^-PPZhr+U#wSD&)<)~ua zep$wW2HR7fe;^sScj?k(I;!~`oI^dM)^iJys2T)u1TyaLlfo(1*%{R=weEGo)~#f9 zV%i-g8P_R?WNQ89gVH2u7km-hF{~QAkf6J@dLPFTEIAj_EhE}JmY`wESLE)>)JxE4 znf$Kq(?OvqlYErbV}jrzMW@}qYspS*3BnSULOTih`5CR_f~VYjoLKaupHh#sK!^KP zj|1Gz>Z)*8;R2+xd;J->;FPrCv+6%t7wAG3fZ68U%jf8pU5j^+=v*qF@9$nUOwffT zKlxv=s&sxA=U1U^Q~9bc>DM(SZB;2Bx+p)_|=dU2)|UbMhU( zaP2kk76;8}h2aAlk3;t|F2@3{?dczF?@_x*>5C~()Z2FL7W`HukT{_CUy?Cx1+jd; z(L(uMyyDC$`x@DP6w^-1I9rL14xjrWwOLH(v689}neIL`Sj!O&lm{8nIN6&D-BonH zTn#dUD1|vY%LbdTds0q%&=JO8}M5 zCwJ;T=GecU(iyV~2=0r{-;D%r&dHRu4zdHHIQGsRajqp2RY+L5YRi*no`D7sa!|m6 za>r(#kdX5{<(e*bXKm!?wV(2pnqUaf15&t1@tT8Pz)h|90*XZY$Rk1sZX3KGv*aU% zl+y`_YdY*LxG{l#==i~N`#<)HlQb@rWUKN z_YMAMcB{tnAh3<%s`!-x-saLTr*ReW(8N zoALZqJd3y?%2q2@9LuZ7d(>uF2kZng<-8}#AGOpU;W(qjbDmE%s0DBkEC36NuwzfD zJTYmp^FhJbKl31-u_4BP$xMUON--h} zF?7FzvZv&yX?^tMGh#ZD0cj3@=U**Hm|h~_i_(R>*REQs_T@UN&sND{WSgk5+X`=+dP>d&ydCWns~$||@3q9G zX{!P7TGk{~HCP?wUDm_u2j#HjOK%|`T;eyaSo1sO3@}WB4L*5L0`A|`$l9uW?{0Nu zTnQ9UgHB^{PfcB(qr32YzmTG+>9-NK6osS z2|TCKHmCT6qnGmZ9bV-?stYAs-PU@LOy$&jasMM?#jM(oE37kBkn5dNmJG_k-~o5? zR!3msVqL&~mt+oGi|1IZjjO=k?a}6O?VZA~#1^G6=!N$&phT?R!z^e={LUZjOF1FO z%Q&o_B!Y}&ca*y{X1PS6Iqt!|#AIVU@xX#n^9}&G0^!tRMUZs}a8_5V!bS6ZBrsdh`<^rhf zNkhB^6clFf-69^!B$gH**GR)sS%#QxUFDxEk+10dK{Yz_%ntZrpSn)xs3HcKJn-Tu z4C@(P!-CR*vW7A*gcQPguz$>zap#v_i>kP2Iy6ncMc3W6S@SOvJ#*KHQYTP<4$B?Yevwih=g1$*x4jtCtK@AEs zu{M0O@}T%4EK@K#+VYg(Ntjtp$mch@rQc(~l+IQ?ueQ7Ajqdm6?E3{I=pBMoFP;h8 zQ>?zI9_2J$v8r^Q`W}K+uCchaKlKFHILbnF;h5?bXj$CBJ$MtZ%$Wv;7EtmQ9b*q| zS6n=()Hl{=RyCvCm)i3a>3J=cM9_O3H#jG>=(B1aFXCA}5q>j1I-ZGr=VCJ(>G2y# zV~b^?Vd^Ca!O=@wBrEH%QV7-aFNF0B%)@P14JZv?mm@y{7Xly@9e6TF4=S}QC9`9) zU5*gTYN^<_TYXuselVEfa~6bF@j387P>6PMvCzeIE(_Me_&QTF;C)8D-Jv;OD>Hv? zF4S6ivK<=Q$_&6@2^a|xxu|pe@xh(XwH8NLf`&YP7@Jxw4XMMK3}vwyYmo&g+TJ2a zy^#$)-zJpSPV<(CkL#Cx54J5Z8&>-X1K3#txl8pMY@mY5M4yqn1@VfLE9v#M;IoG* z?*DZ`fb+5?u`KYy++kGqr+b;94P9;f@=p!IefP<|yVVZDfwPMOi4?xwW#%1WB;gCU zo#!4{9Yj1hQ{1BT>45~_X>GK}bKXp)*xpM|E2-X^P#&hQ$BH%KAmI{qpt}Va7=d?v#QH$a^!7)K`15mv@edz6AT%Tw|Xq%Gi!A z7p@AzzL!x%k1^$;#yyg8#omges%kR#_8Mw?tejd$imMr1b1B>WT!98tjJTBduBuH1 zT%_c=S6l5~FaAEst0Y;0+DldQC(o0806(E?9r%;lw71MR++KD?St;oOE;}N%eQW0lJk#rG1Gitk%3b5)9!+Om9`0~OdXv-8x3-184(geG``kaWHnk_q)M5H+ z7}{`Tp%@ys*LH%PTf7q+gSDv3p?=?{q-yqUq+7IZ|BvYv%he650NJsGEIe2< z9HxZ2Yim&)fA|`5>b?2ED3iUasp7FO0T+#G5-oxKqD?lO-3Rk?ydCgL3~{b^lQEJIX_X;fT{B z;b*wc)D($6Y9^p_dro3iSpYtE4&Kt|p$wit>W|7tqV=%Aj4VWL=pD2`Z;!nq+0p)8 ziGf@WIFM~DeZRJ|$7m*ID=CvH<$?2U!uW)@WHL)o3Wd+{d&6SnF{DKD6XNoUsNEkfC3OT{6xl@}| z3x1#m)KjhIfBm|dx{Uiwt^K;fEJEGi7@ITO`wNe^9O)!`q1k@D|Ef7h5h4LI^S?L^^ zG&&9AS{xJFD8o#x#wF5!zKxVScv+fR_hk4pOEMr(yUiDr_nnKHJ>w2 z*^;y0aBAJ(do)_XlxNQ!A(U~F9G*T&XfSmoZJ)6xSq*~EVh4^Pi%SWgJsy<<6w+1P zrDUYeNIzq732ilGyqcieq2_Vm9l4X0!aHKhXrBF)2M(x{5Q**cLJ^e5iEmMAY`H-$au5lB*JB++#Yp@xNVC% z#lSDADG!>`V!XLQEc6<}=1~^MdGvtkHADh=zHC4ZG3i}oIh=81SlT&IN3O<7nYDv> z5W~c(Z9kwEh%RGK9)0$Md!bS zZU}pss?i;T?TkaN-gyZWT^C)m8VOu@m zP#&au!aKx#-=Qtvp=g6fAQo?O82PnMl|&G9iy+P7{J2 zTvS$N5%KC!Z(@ExMcGf64DW?BS(bOXV4ayb`=BgVOc3&KR8&|)t{_fqetzYaW>bAw zMs$poq1`o4fO7rS`!Mq>T}VjlkYmQtxVn~irj{dPMLr3ur+M{X`u(bxX_aO?!t+e* zV@3~JIW#an8A>a>(Qqs%Q!a1ChOy^Pbyg0YXBfn~$KyVrabuENRIbs=h7i0Rxm+Un zm-5&_&lLkXUhv7_FK@- z_U}veLa%@euL9XXHgUR)5mXP)BX=%sXDYu&tRG|G)hlhN+t62sMtu#{2((B~KtZeS zkCiUUFEHT2C?Jdh+VUhMONaYS4dof-7X`arKqy0e7EozXe7R&NMnm0o6nL8`#X z0ctgQfDZ?i$P!`btu^Yy3R4ax6lM?;$2BVhk4%Y>tLt`Ke#$zUOkPA5IFw9lvbaa8^ zKW{e!#I(Zw-@56S>_g)?s+23N3}@Dt0r@gNE(}803AwgW8KV!qs+3jvPzg z%qB%QJc}kW^OKPRD^+eO>&4B|Bx!wz)nh-ytu$>AM98|NtlTr(xt(zGVIrpjP|QL2 zKnd#9EWOg&BLssOgWN~g1M(@s3|1CFWi@T~=-nJdt79)40BVlpuz)V1Az3XkKKh+;dPs1#;!eDP0ozl7KE+at~#1^nbER^7GZErQ$ zD(@B;y?r#{Fy_b|>^dai4mw$=>>w8(dqzEh(SZ3?jjBL5%LhyJIhD zym~WlMDSS5!KC-0#?BOLVPU)^T}p8rN!LZS(w)b2?}>qT9#LI~^BwK&j)VlJRLjL{ z#T+vxv#O4H(0)OAb>0bf%8Mwm=g_6NojvEtmA)Y8UKX8VaouA&W2;V_?_$+)(V_cl z@S-UG+nIm7_P=^?_Se4t=Kpcq?c4t0fui}%&wu)p>+k#VSL&n9VHGHbYaHqvhB(X= z?WU&sv^zhmkJg7r+w<>^&(zyZ{nq~>@!^`8My<(bS{L{m<$klNd#&M3JgtL@j#w~|?QIxUU8;O^W{7sFc|&S{#gn#V*ByoM%1 zi-XZ*SvAtzd|u>AC+CV!X%Ot!i{{BVc|J~Fh?8@oDzDL2neNH+aq>c(92cc=&ehgg zedz>a!R|u>gB8}ddi8|qo#Y!7F_YTkkHhxNe-ivLj1#`Q?3252|Use>iHprk2Kd7qwW3e zhtt2gAGXu~(W1sRQLp{*hz@f<9D!&Lw`WE<@-K=)zm{59lS-_C9&>N#zgS_YzPlLF zz}0k1_0@DORdtEu!XY?A95p>HMpOxbJ$D1V^_J>-E){%3XQ##r!JR79S^GKt>%7&Z zFPw}tsa!|u2EgNbxtXa?RxtZ+(VgNGTC+JOZ1}99E=;%QhbLynx>t04)EgB4&c8Uc=z!wT zpd(qQZ|lZL$Z_!lS{mQm;UKmx^4CsE;DN)ycBODR%0c1*J;xUBM~nYQHLN@jQxe8Zd_u$IU>x|9n$-&`hl31ag98&J?+A8c|B*ksz52+Nv zG!_|`>ZGCu7D?|{9eXtH_A%8)A{&=@Miz+(iSs`fMrd_c>hrPGE_!%I(&qe|VSP<^ zkfyTsPaiU{4c*niWLF9%kIJpLECs|Li6JwPt6e=JGAZ7 zTn472%&L#<&~{(?M}G&kaZo)puG~i6FT$L9B^k;5jOXMb7r4T>1NRY<85?l7)7S)a zz82V934EkNei0G@uU$RQXFhwcyaavV9`i5Wy$9SKJeG&;N!vlI3*t^>jCW-Lhf_E3 ze#9RUj-op(jV|W|8r(%%smd`SH%=u{`?XMs-W=|>8t^%9QKq<4wbrdHNv16lOV)5X z6a_D=YRM&`R`e2oJrb-DW1(G~NVPwYrplO&tO=fobvM}bIChL}INXnji`rHu4|%N( zdLtwiOji>Q;5Wkd!Ll2a$apMMVr9@cDzjB%Jw$aP4fqZ`o#@J9^w{(__Pjj=8Yo*z z*W9lij8kkVJWLDF29EOrALqZ&xe+tbPixgGhE}Q#4v98cS2!Q~Lu+^_c)_(se^yRO zo_}vWM@nsrjwc%jjMzC~Kr;sREe8Q1(Fwdr`|O3~eozm-D|F9EBs8h}3$D?PV1u%~ zK1;v9(kt65;V9&8d0mt>V!viV4|$5VrPos=h|NfWs57GmBL^N7w#lUrKhCHxsjSKz@q#aT1+wIyU_xDIP) zJ7cKrHHE(ccZF7UX|M>i9z>1Ss!tK}p~v#RbYLd%fA!e;MNu8eH>UdSWmrqEQ9(NB z0~+W`gOnaGagBkApc6htBw=HE;46uWeh=u?NiN%QjmvB8we|Ll@Nr_N;k&eG!|#E* z60dJ9?km4Gih&wtL!K`lmcl`0p4YE?r8r3-en!`}$`qZ_Owpls=dUX!S0*LJ=HxL( z)73xZs-kSAgcO*bQKm)NlQYecLaCaTV%_NxWrU_WUneiLuuWw;|CvW^=zxvnxLimGb*ppJJssp(GL@)i4&N9aCIO{zzDN;u=Ub~3 zk9WS?o_j}GAVtrk`Wx5Zg#IRnA6B`BvoOu^nUPw1cM6)vblOU1DIf8i@)3&KpBtYU za*W@s4{L*EMd8W{=njX8l(U#<L({b$j{nyuTLwp^$t0e&b#f~rv-tCe|sxo?RIiM?b`)vVwu}h60Qb| zjOhm7&1R5z?glloO4ZE2-ky6~68Z(>=#mjJxwvf{Np5{IQdccWbVOqGTh>;-hief$_W*9zDJ6hjt!;sMwGouzsA$A z@#zs2C8I@AzV)SxiG44Ct377Fx4?n0rF2+2jH(hfMeb)mInMbXrQ_oq=N{y^a3Nh0 zxJq6ARM{hu8#=dURw?b+Y5f=O2mZ`aq8G9Xw&rdy0wr$(U*cP9aj0`Znrjm+XgoCCYUpd4Z=tG1mWz(up#CZ= zD$H~$MLub)t&S+Ca@ z&jh{reg4gfl;#>~O}A7@eRPzx*J%3GM<<)>zxn0!|KN{K?`Zzn-+B3=@ju)AaPzJDQ*2^MjWkZhrqy^_jR+`NZ~u)PmH`egGr0cS--#*}|dr!XbeOKSa4U} zLw{rXTSd3tlzvhITbNYtQer%=JYh?WuTMhb!VQx%BElP@NPA*2EqO_g&i{o(Ls&-{ z{{i664f*YPDMS-?c*bbx;U8_IhZkV> zKl#q^->W_S$HSi+eSXLA;kEzguMYo%_4{`Wf9i8z-I16Vj)C#{Q)lZN>aztfZZa=y z`lVu3d+uBMEo8RmzS~~d-=2G*|NrnWqX&g7cYgjaTJd%u{Fu+ngbXzW?&_IFd*O_l zVhBNJ^+$@7`Aw>OBC&<^_QE^sl%k&d-lDD26)76YFPuIxE~N93J{P2&AB~PCMi^Vv z@@p{x*8egDV&S9dxI1=8TE0E$sCo>7O8FIq>l35#; z)Lmk9C9$-=M^%$IvnmCzI+)gC}xH& zcPA6r5*+YobE0Up=Y_CIKJZ!h6c(SqL4raRYdE~q6fQn1H7fSfqsdf@!!;t5SV(F0 ziRSQMUcP);?Onc{pu8xBD0S7;AOlK8BP(;KRMo?i_PIx!a}kPkbnNC)`eh|4o~PX0o|KoGjSPTNyz8Ek*dP0g8A^N>idh} z`MGUfC#Z92D)mCuB?);~@ZUHyCZ!KI;w7Du6zHdvhYW(<8o<2;U|!W>0W5dm%7q`V zpJ~?GYo;~ewnmp!r>=pOu2Gvmg`Lw#od6PIlXH-~()n+s^KYf&x6|>v>G*a!elH#W zG#!7Cjz6UJuXE6E4f?4{(fzH|^ZBnQjDI5?zm<;PPRH-2$H3bo@a&{xA_| zUT%X}_~oN#j}3q3#PMUpGv`m78$QP2 zIEM@8k6ly+=PnLU(eoK9y@|6Mzxj_I7{2^}{jXbtkrz&xk;SClA+~q%kMD#v{+14% zA5MXzZ91`3s$Ua}L*UZyBJo61x>6!E(QMxt_hhdTE7{EH5%E33KtLg4%g8|U_LF** z7sHhNrPH}fqHY?yoc|eieq3x`A8(Gs8{?D7MUpP*;Ao9{(<*@Mg!?t$fiF-C&*>_K zXhp-IH_-79a1hCF8do9lyNK2NOgWC+nqEcBb~7ynezFV0*OHz4?R5M$$N9eyXmj5MzWtm%z<})}hk7b>>K3`Z$t76QAGozx zBU8O_O2(u;_uK8cZ>v(t$E5z`zo@g)7gDHFwx^mOEGGF@IT^0fKVjvDV%)dB9k+Dx zrb3kME3X^l#V`a`6OdZS{xn_qG#bFCr)mm(RvToHDl#-lW8kEHI%P*=6?jL2s{)!%Qv(?I;{X&Fm-=yP?zPXwO_+m z9Zs&SJbqCF#~-aUjyJMul@1eFg2~C``H$;3E_gYPk0}IgADI>lr?W3|CiB_e)l%f3 zueQWmt+d*^Xl#18y-PhDN_MG*x7(T?!mdnf?gZL^Z(Y^*#33$f z>l}WY!+#<}wU@5QR|V+yL+QBFR<_wboq8sOK;Lma(V0t((D_m#eCJCkB#Vo4VvMQI zS16$`Oc^*9ulym_qBECB`4!P`L;JAmnoD^QO<&kESWZ1Rl>829GSc!4X73kL8FC}* zLn_v@Lb%KMYsvjxcx!a@=Fac5xA^OB0V_5B+y-?keao4X=cx-3TF4U?cfZvQG`^M? z{A(cSxrB*@ugRMemeM2iMHlc2|SFl-4!FRaTRK6t1bh7tzKS9l=98;aHC3nd86*#qT zYkuVWAA8~-O^rR=ynM^Q`SXiE{`q&B|J@5?KmS`N|LJ#{)4zWFWB+vDzx+<~XJ7gs z5B>eG{pF74|NPRkjsI!=-`>&G_22pZ7yp|b-7??*PV<(V-#zomFaG(C=0E%V4S(;k zfA+H-&CQ?w5C4aMwdt)Lspm=X6}PCf`;%G|dL}tl>V;q+nGm1bvP={LTbHH57PL-W zKXiRU-d|#_sm}L@HQS^)-Wb*#uk^3c5Icjt#!zwv8_ez+<*2D$WI#5IH0nAw8VaxU z8B%mG+>mxvriDm(U3(pEW`Pz<#;=+juM zm{twXi`LW1G_=>X$d~A&(yD(%2bFFNwRBq`j7)D3=sL{QhML1gtvYVh&n#r7^SoFmG5shq^g^89MoKWWTKG&(fPTi;QbszsE5i_Nm`@lm z|I%@$abuh-Yn1N80C7#>z;bC)aWX6KShENn{sd2mQ~c;LaA#s>oe zBc@?@7{{?rSHYkdOp=sy0>_$dejxF)1{@p5tPGH@=8W+_(L+e>zjuToBSb@7~_HfvKeLroU#ZJ^w9ArwfOpo@Vox8#gVM z5=*Ptw+9u|wGH<*ZdUi(^M9IJrJWFw^lh+RGvG+c=nuI90esDWSE@ryH6r#IvA`)E zP;ZS2Q)ALTBS}!3t;Tr%F}-0-SbQ>7E1WBuS}Sd&k1~W$wx1*l;K9Z)(Vzs-pJs#W z`bbd5$5FD<4@tcYElRPKMtg6n0;M3*4(YSD(df>Te7GGmC093A;o8&Vl#Ua*lhU!o zi|qwub7)B8Gja@v(Sp)j`Z;$$LOT&ZCX%guNS2_zy(M01jObe@VyWqt#5{5KhNi%s z+o1o5t5nEMjdrWOaH%yShdUTjpbU+Siz~Yy>8+MHw!f5$wS>T1^1l&m4cb@-olZ(A z(l7NaohTnJVhK6hr$;t4Zg97w8JRcb3=SpTf@9E{n%g-ww+jRxX+JFmCi^{!!_sU{ z&FyAuz;LDJk*( z_uBWjH@7wRu^HE9Geawz8N1pnvHd}N{sB2Fows!0pUQoT5h}H%ys;ILAWzoYJv0Hu zF}f_!ZfgFu#$>nsB^$?X?c(s{n#Q=mlr;);9B6Dzwh8;Rf`2P0BI79hC|J;OA{`kW zQKXxWtJ1L{Mivg?4uwea!Y27u)F=Ga@Yk5XR`DhBEOb%c`T6RG_?3^Otu0M(Lt~A9 zrz0YzlN)_83HeB~D}O>;g%`W^sQ3`#+!C*@YK$#bCcU_!6^$j;+0=04!oN<*zRu65 z=FUoN$C_he{1Ltc=kS=mB(u%2F_}Mq^voOb&ZgcNzOgYiKoEvTZfuMXB*P7j$vO{o z1*g@T7NE#lQ%70ccO`(dF}-1`vAWw@eBIKxu_ZCQq5B)fH+Q#w*s3FzA8xE`&ws-z zD=}1JE*0UX41Y>xi&V9>#Z6Mhd@Q{ZRDF_nz>V|^ZP5Ls8xJU7a5f zq)ol0CE=V71+_mDCm1_>L6V%%qcZgRK(;#8#BhS$1!XIXpL682Ad{}?l z_X~NA4`6{SrR2PURj%RU@NZ3*x4IQhH}7fOCOy_x;(pLa18bt;C8(rj6#7r0+Z#RL zgh^9bm!Q=81|B=fsB~N33hQLdaSTSqCOgGs&DNA02R^D%(DIQhnp7mtneI)>NQBjq zITD+<_lk*9mS4{6T5$?btS#2oq>DGGt%>Fb8f!MBUQ?f2*Hu;3u!FZ@gQ(TTo_#08 zX9)gYcypEf-B3C=HP%uEANSGcprBhn*<^(hq}>jjFFOD z_+2^JvMBl|<1U9KDR`+rxk!Ch7WG$WOaOoIv{YgHVoRzr^eTjG&;N8nT3ESDdNBb< z2m%$AKuAmKN6@8x|2S&NsIaR(w=KEk-LH-mn&93hFK=$!2fM084=lW?|EVG$UbuqS zUGmZgJiS=aiu~@6G;WOt1eE@(&q^91TxgRnXwqMyFI@VbfSwpc4|R{})W1bFRh9nx zk*%OL3i_etif?Sdw#(EWU_%h}Exz*`Lz;fFzF~$k#9VE*bXnzZXvoVJt9AS0i?=mC zB!adkq#9I2p0vK)?-EsROR^>}3Q}=B$}j3sJ)0yWmr)U`*;H`?SALn=>Q+&%hP#P- z61XWg7JmtJNhLnDAi9*krxcK?w~8WxHm0w@ikWU+yQkD2W5G?-CNh&>~)AEE>qnNwzmI}z?r%G|Q(}}b&Dn>eU@|PMPG|{@QcA{~r%7>95rACNgM0)w? zzTJ=?&4sptmc<7Q$ttN06cWX6Pq_n`l#*3}ORE3yO+=%{DlR8I#;gJ1`-3_4kYKTr z%oAnPV;H!K}Pmpuahk__wjUP_EaRY~&I4BUA^38ZYpif>O zL)rxLDaWEZP>I`>CJ=(K*%%2Cb^LldPO!(t_t5;8lb_b&6+0zNCrQ+|`iz>R!=+D=6y< zzYv0E;G?TuSEnhqMy#k!)i6Z6_>pI=Tm3h7H0~;2@9)x5&R?pTC{e@_j0T$~yCBL1 z?|$UKOiYA5M*o~roq;%|IJ#igiCJ6Yt5Y^hL^zbP3KQ{LDKF7|x_Pr1PrWXEf+O1V zKO<|n_>mCI5b;lI*bN>VZ;lGQWCc@mHRS`v`(pQ78#hVKqHM({^4wdJNon0cGM0Pf zgTf)Y*d+*xNw;uiNIC+N99DuoRXC}PqwozQx(3{LQ(qwzstJ)(=6fR>svF1WKR9 z;8uMHHJA*0t;&LgE6lgD!)baW8mIHFwYVl~T8np*qA6){XVLHt7T#PJv7*k^g4}TY z=T%8{CZBzSGtTMuV&PyYlCcwOa7xObHA>+hZqNNhVIUKiW}32M`bwJ8Kt38@T>H(&`@Njxk7+Z`1rdtB7F3~Z$h5dKHdkRX#<<{UjPEX#}SZ660 z+yns*27-Z-o^LkS;Ro?Q3Z(`UCRCEpf%6C>Mzp%TD8@B;cJ|<}-Sdf}_;9UQ|7-hq zJ$BEwZQEv!pE&l?@zXD!oH=v)`Ey6l=`}VguTQha99(@=x>g*jf8eQeM_+#8r5AU- zdhEo@7f!$Q;-ORLUV7zxt@EF4t##fw_}cjkC(dp?c;eW_bEhx7wsrrBb7xPVKd)lv z7cb^(J1<-~cl!B@7f#$cbN2kPm(Ho_J7+$5;vD54x@YS){@pn6ca0>Cc=#b`&V-%MZ>npUR(q!TC)5s($~tRL*yvoqg%tqi4?SJ$m}ZB0+lM#Ma|y zv=yX2mv2`+OvbqWxsCy0pjI%YU2}3eRdgij(@Kc`>y&$3f1m0!igz?)FFxsw5t7sdY1Jg?C{} Date: Fri, 21 May 2010 02:31:14 +0100 Subject: [PATCH 180/260] Change the way the object perms are propagated to attempt to salvage some older content --- .../Framework/Scenes/Scene.Inventory.cs | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 60f730de10..96f22a4255 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -382,29 +382,31 @@ namespace OpenSim.Region.Framework.Scenes itemCopy.InvType = item.InvType; itemCopy.Folder = recipientFolderId; - if (Permissions.PropagatePermissions()) + if (Permissions.PropagatePermissions() && recipient != senderId) { + // First, make sore base is limited to the next perms + itemCopy.BasePermissions = item.BasePermissions & item.NextPermissions; + // By default, current equals base + itemCopy.CurrentPermissions = itemCopy.BasePermissions; + + // If this is an object, replace current perms + // with folded perms if (item.InvType == (int)InventoryType.Object) { - itemCopy.BasePermissions &= ~(uint)(PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer); - itemCopy.BasePermissions |= (item.CurrentPermissions & 7) << 13; - } - else - { - itemCopy.BasePermissions = item.BasePermissions & item.NextPermissions; + itemCopy.CurrentPermissions &= ~(uint)(PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer); + itemCopy.CurrentPermissions |= (item.CurrentPermissions & 7) << 13; } - itemCopy.CurrentPermissions = itemCopy.BasePermissions; - if ((item.CurrentPermissions & 8) != 0) // Propagate slam bit - { - itemCopy.BasePermissions &= item.NextPermissions; - itemCopy.CurrentPermissions = itemCopy.BasePermissions; - itemCopy.CurrentPermissions |= 8; - } + // Ensure there is no escalation + itemCopy.CurrentPermissions &= item.NextPermissions; + + // Need slam bit on xfer + itemCopy.CurrentPermissions |= 8; itemCopy.NextPermissions = item.NextPermissions; - itemCopy.EveryOnePermissions = item.EveryOnePermissions & item.NextPermissions; - itemCopy.GroupPermissions = item.GroupPermissions & item.NextPermissions; + + itemCopy.EveryOnePermissions = 0; + itemCopy.GroupPermissions = 0; } else { @@ -895,12 +897,12 @@ namespace OpenSim.Region.Framework.Scenes if ((part.OwnerID != destAgent) && Permissions.PropagatePermissions()) { + agentItem.BasePermissions = taskItem.BasePermissions & taskItem.NextPermissions; if (taskItem.InvType == (int)InventoryType.Object) - agentItem.BasePermissions = taskItem.BasePermissions & ((taskItem.CurrentPermissions & 7) << 13); - else - agentItem.BasePermissions = taskItem.BasePermissions; - agentItem.BasePermissions &= taskItem.NextPermissions; - agentItem.CurrentPermissions = agentItem.BasePermissions | 8; + agentItem.CurrentPermissions = agentItem.BasePermissions & ((taskItem.CurrentPermissions & 7) << 13); + agentItem.CurrentPermissions = agentItem.BasePermissions ; + + agentItem.CurrentPermissions |= 8; agentItem.NextPermissions = taskItem.NextPermissions; agentItem.EveryOnePermissions = taskItem.EveryonePermissions & taskItem.NextPermissions; agentItem.GroupPermissions = taskItem.GroupPermissions & taskItem.NextPermissions; From 78a0fd52813d80a61af0d2ae4522fb757eb19d2b Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 12 May 2010 21:42:23 +0100 Subject: [PATCH 181/260] Only send dialogs and notices to root agents, not child agents --- .../CoreModules/Avatar/Dialog/DialogModule.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs b/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs index da38edefc9..b5c3176d8e 100644 --- a/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs @@ -81,14 +81,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog { ScenePresence sp = m_scene.GetScenePresence(agentID); - if (sp != null) + if (sp != null && !sp.IsChildAgent) sp.ControllingClient.SendAgentAlertMessage(message, modal); } public void SendAlertToUser(string firstName, string lastName, string message, bool modal) { ScenePresence presence = m_scene.GetScenePresence(firstName, lastName); - if(presence != null) + if (presence != null && !presence.IsChildAgent) presence.ControllingClient.SendAgentAlertMessage(message, modal); } @@ -119,7 +119,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog } ScenePresence sp = m_scene.GetScenePresence(avatarID); - if (sp != null) + if (sp != null && !sp.IsChildAgent) sp.ControllingClient.SendDialog(objectName, objectID, ownerFirstName, ownerLastName, message, textureID, ch, buttonlabels); } @@ -128,13 +128,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog { ScenePresence sp = m_scene.GetScenePresence(avatarID); - if (sp != null) + if (sp != null && !sp.IsChildAgent) sp.ControllingClient.SendLoadURL(objectName, objectID, ownerID, groupOwned, message, url); } public void SendTextBoxToUser(UUID avatarid, string message, int chatChannel, string name, UUID objectid, UUID ownerid) { - UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, ownerid); + UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, ownerid); string ownerFirstName, ownerLastName; if (account != null) { @@ -147,12 +147,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog ownerLastName = "user)"; } - ScenePresence sp = m_scene.GetScenePresence(avatarid); - if (sp != null) { + if (sp != null && !sp.IsChildAgent) sp.ControllingClient.SendTextBoxRequest(message, chatChannel, name, ownerFirstName, ownerLastName, objectid); - } } public void SendNotificationToUsersInRegion( @@ -207,4 +205,4 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog return result; } } -} +} \ No newline at end of file From 74ef1ed36f234d93aa3d58b1602344bcb3e00d6e Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 19:36:39 +0100 Subject: [PATCH 182/260] add prim item and test asset save in save oar unit test --- .../Serialization/TarArchiveWriter.cs | 7 +++- .../World/Archiver/AssetsRequest.cs | 2 +- .../World/Archiver/Tests/ArchiverTests.cs | 32 +++++++++++++++++-- .../Scenes/Tests/UuidGathererTests.cs | 3 +- .../Region/Framework/Scenes/UuidGatherer.cs | 8 +++-- OpenSim/Tests/Common/Setup/AssetHelpers.cs | 14 ++++++-- 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/OpenSim/Framework/Serialization/TarArchiveWriter.cs b/OpenSim/Framework/Serialization/TarArchiveWriter.cs index 0bd639ff3a..fca909f2d8 100644 --- a/OpenSim/Framework/Serialization/TarArchiveWriter.cs +++ b/OpenSim/Framework/Serialization/TarArchiveWriter.cs @@ -28,7 +28,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using System.Text; +using log4net; namespace OpenSim.Framework.Serialization { @@ -37,7 +39,7 @@ namespace OpenSim.Framework.Serialization ///

public class TarArchiveWriter { - //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); protected static UTF8Encoding m_utf8Encoding = new UTF8Encoding(); @@ -148,6 +150,9 @@ namespace OpenSim.Framework.Serialization /// protected void WriteEntry(string filePath, byte[] data, char fileType) { +// m_log.DebugFormat( +// "[TAR ARCHIVE WRITER]: Data for {0} is {1} bytes", filePath, (null == data ? "null" : data.Length.ToString())); + byte[] header = new byte[512]; // file path field (100) diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs index 4215f97343..a1451ce705 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs @@ -239,7 +239,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver if (asset != null) { -// m_log.DebugFormat("[ARCHIVER]: Recording asset {0} as found", id); +// m_log.DebugFormat("[ARCHIVER]: Writing asset {0}", id); m_foundAssetUuids.Add(asset.FullID); m_assetsArchiver.WriteAsset(asset); } diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs index 624dc22044..f3e5458dce 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs @@ -33,8 +33,8 @@ using log4net.Config; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; using OpenMetaverse; +using OpenMetaverse.Assets; using OpenSim.Framework; - using OpenSim.Framework.Serialization; using OpenSim.Framework.Serialization.External; using OpenSim.Region.CoreModules.World.Serialiser; @@ -44,6 +44,9 @@ using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Tests.Common; using OpenSim.Tests.Common.Mock; using OpenSim.Tests.Common.Setup; +using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants; +using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader; +using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter; namespace OpenSim.Region.CoreModules.World.Archiver.Tests { @@ -55,6 +58,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests protected TestScene m_scene; protected ArchiverModule m_archiverModule; + + protected TaskInventoryItem m_soundItem; [SetUp] public void SetUp() @@ -127,7 +132,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests m_scene.AddNewSceneObject(new SceneObjectGroup(part1), false); SceneObjectPart part2 = CreateSceneObjectPart2(); - m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false); + + AssetNotecard nc = new AssetNotecard("Hello World!"); + UUID ncAssetUuid = new UUID("00000000-0000-0000-1000-000000000000"); + UUID ncItemUuid = new UUID("00000000-0000-0000-1100-000000000000"); + AssetBase ncAsset + = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero); + m_scene.AssetService.Store(ncAsset); + SceneObjectGroup sog2 = new SceneObjectGroup(part2); + TaskInventoryItem ncItem + = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid }; + part2.Inventory.AddInventoryItem(ncItem, true); + + m_scene.AddNewSceneObject(sog2, false); MemoryStream archiveWriteStream = new MemoryStream(); m_scene.EventManager.OnOarFileSaved += SaveCompleted; @@ -151,8 +168,11 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests TarArchiveReader tar = new TarArchiveReader(archiveReadStream); bool gotControlFile = false; + bool gotNcAssetFile = false; bool gotObject1File = false; bool gotObject2File = false; + + string expectedNcAssetFileName = string.Format("{0}_{1}", ncAssetUuid, "notecard.txt"); string expectedObject1FileName = string.Format( "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", part1.Name, @@ -173,6 +193,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests { gotControlFile = true; } + else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) + { + string fileName = filePath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); + + Assert.That(fileName, Is.EqualTo(expectedNcAssetFileName)); + gotNcAssetFile = true; + } else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) { string fileName = filePath.Remove(0, ArchiveConstants.OBJECTS_PATH.Length); @@ -191,6 +218,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests } Assert.That(gotControlFile, Is.True, "No control file in archive"); + Assert.That(gotNcAssetFile, Is.True, "No notecard asset file in archive"); Assert.That(gotObject1File, Is.True, "No object1 file in archive"); Assert.That(gotObject2File, Is.True, "No object2 file in archive"); diff --git a/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs b/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs index 8b80ebe3dc..5e6124b4f9 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs @@ -58,7 +58,8 @@ namespace OpenSim.Region.Framework.Scenes.Tests TestHelper.InMethod(); UUID corruptAssetUuid = UUID.Parse("00000000-0000-0000-0000-000000000666"); - AssetBase corruptAsset = AssetHelpers.CreateAsset(corruptAssetUuid, "CORRUPT ASSET", UUID.Zero); + AssetBase corruptAsset + = AssetHelpers.CreateAsset(corruptAssetUuid, AssetType.Notecard, "CORRUPT ASSET", UUID.Zero); m_assetService.Store(corruptAsset); IDictionary foundAssetUuids = new Dictionary(); diff --git a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs index 0ec3cc3c73..e3965ce978 100644 --- a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs +++ b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs @@ -123,8 +123,8 @@ namespace OpenSim.Region.Framework.Scenes foreach (SceneObjectPart part in sceneObject.GetParts()) { - //m_log.DebugFormat( - // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); +// m_log.DebugFormat( +// "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); try { @@ -155,7 +155,9 @@ namespace OpenSim.Region.Framework.Scenes // Now analyze this prim's inventory items to preserve all the uuids that they reference foreach (TaskInventoryItem tii in taskDictionary.Values) { - //m_log.DebugFormat("[ARCHIVER]: Analysing item asset type {0}", tii.Type); +// m_log.DebugFormat( +// "[ARCHIVER]: Analysing item {0} asset type {1} in {2} {3}", +// tii.Name, tii.Type, part.Name, part.UUID); if (!assetUuids.ContainsKey(tii.AssetID)) GatherAssetUuids(tii.AssetID, (AssetType)tii.Type, assetUuids); diff --git a/OpenSim/Tests/Common/Setup/AssetHelpers.cs b/OpenSim/Tests/Common/Setup/AssetHelpers.cs index 1fc3cb5455..1beafcf764 100644 --- a/OpenSim/Tests/Common/Setup/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Setup/AssetHelpers.cs @@ -38,12 +38,20 @@ namespace OpenSim.Tests.Common /// /// Create an asset from the given data /// - public static AssetBase CreateAsset(UUID assetUuid, string data, UUID creatorID) + public static AssetBase CreateAsset(UUID assetUuid, AssetType assetType, byte[] data, UUID creatorID) { - AssetBase asset = new AssetBase(assetUuid, assetUuid.ToString(), (sbyte)AssetType.Object, creatorID.ToString()); - asset.Data = Encoding.ASCII.GetBytes(data); + AssetBase asset = new AssetBase(assetUuid, assetUuid.ToString(), (sbyte)assetType, creatorID.ToString()); + asset.Data = data; return asset; } + + /// + /// Create an asset from the given data + /// + public static AssetBase CreateAsset(UUID assetUuid, AssetType assetType, string data, UUID creatorID) + { + return CreateAsset(assetUuid, assetType, Encoding.ASCII.GetBytes(data), creatorID); + } /// /// Create an asset from the given scene object From 682efe94635943fbfadc19204d77602fd30b3861 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 19:41:00 +0100 Subject: [PATCH 183/260] minor: refactor CreateAsset to eliminate dupe code --- OpenSim/Tests/Common/Setup/AssetHelpers.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/OpenSim/Tests/Common/Setup/AssetHelpers.cs b/OpenSim/Tests/Common/Setup/AssetHelpers.cs index 1beafcf764..6dc993f069 100644 --- a/OpenSim/Tests/Common/Setup/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Setup/AssetHelpers.cs @@ -61,9 +61,11 @@ namespace OpenSim.Tests.Common /// public static AssetBase CreateAsset(UUID assetUuid, SceneObjectGroup sog) { - AssetBase asset = new AssetBase(assetUuid, assetUuid.ToString(), (sbyte)AssetType.Object, sog.OwnerID.ToString()); - asset.Data = Encoding.ASCII.GetBytes(SceneObjectSerializer.ToXml2Format(sog)); - return asset; + return CreateAsset( + assetUuid, + AssetType.Object, + Encoding.ASCII.GetBytes(SceneObjectSerializer.ToXml2Format(sog)), + sog.OwnerID); } } } From 721c1085da22229f4d40529dd2738a2cf62a91b7 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 20:43:11 +0100 Subject: [PATCH 184/260] make oar object filename/pathname creation a helper method reused in both tests and oar code reduction in checking is outweighed by greater test clarity --- .../Serialization/ArchiveConstants.cs | 33 ++++++++++ .../World/Archiver/ArchiveHelpers.cs | 64 +++++++++++++++++++ .../World/Archiver/ArchiveReadRequest.cs | 1 - .../Archiver/ArchiveWriteRequestExecution.cs | 11 +--- .../World/Archiver/Tests/ArchiverTests.cs | 37 +++-------- 5 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs index 1cd80db24d..475a9de0cb 100644 --- a/OpenSim/Framework/Serialization/ArchiveConstants.cs +++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; using System.Collections.Generic; using OpenMetaverse; @@ -85,6 +86,11 @@ namespace OpenSim.Framework.Serialization /// public const string INVENTORY_NODE_NAME_COMPONENT_SEPARATOR = "__"; + /// + /// Template used for creating filenames in OpenSim Archives. + /// + public const string OAR_OBJECT_FILENAME_TEMPLATE = "{0}_{1:000}-{2:000}-{3:000}__{4}.xml"; + /// /// Extensions used for asset types in the archive /// @@ -139,5 +145,32 @@ namespace OpenSim.Framework.Serialization EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; } + + /// + /// Create the filename used to store an object in an OpenSim Archive. + /// + /// + /// + /// + /// + public static string CreateOarObjectFilename(string objectName, UUID uuid, Vector3 pos) + { + return string.Format( + OAR_OBJECT_FILENAME_TEMPLATE, objectName, + Math.Round(pos.X), Math.Round(pos.Y), Math.Round(pos.Z), + uuid); + } + + /// + /// Create the path used to store an object in an OpenSim Archives. + /// + /// + /// + /// + /// + public static string CreateOarObjectPath(string objectName, UUID uuid, Vector3 pos) + { + return OBJECTS_PATH + CreateOarObjectFilename(objectName, uuid, pos); + } } } diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs new file mode 100644 index 0000000000..880bd7cdd4 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs @@ -0,0 +1,64 @@ +/* + * 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 OpenSimulator 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 OpenSim.Framework.Serialization; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.World.Archiver +{ + /// + /// Helper methods for archive manipulation + /// + /// This is a separate class from ArchiveConstants because we need to bring in very OpenSim specific classes. + public static class ArchiveHelpers + { + /// + /// Create the filename used for objects in OpenSim Archives. + /// + /// + /// + /// + /// + public static string CreateObjectFilename(SceneObjectGroup sog) + { + return ArchiveConstants.CreateOarObjectFilename(sog.Name, sog.UUID, sog.AbsolutePosition); + } + + /// + /// Create the path used to store an object in an OpenSim Archive. + /// + /// + /// + /// + /// + public static string CreateObjectPath(SceneObjectGroup sog) + { + return ArchiveConstants.CreateOarObjectPath(sog.Name, sog.UUID, sog.AbsolutePosition); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs index c52f029c39..8d62fe1668 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs @@ -38,7 +38,6 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Serialization; using OpenSim.Framework.Serialization.External; - using OpenSim.Region.CoreModules.World.Terrain; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs index 9fc6ec47c2..586d98e322 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs @@ -145,17 +145,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver { //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType()); - Vector3 position = sceneObject.AbsolutePosition; - string serializedObject = m_serialiser.SerializeGroupToXml2(sceneObject); - string filename - = string.Format( - "{0}{1}_{2:000}-{3:000}-{4:000}__{5}.xml", - ArchiveConstants.OBJECTS_PATH, sceneObject.Name, - Math.Round(position.X), Math.Round(position.Y), Math.Round(position.Z), - sceneObject.UUID); - - m_archiveWriter.WriteFile(filename, serializedObject); + m_archiveWriter.WriteFile(ArchiveHelpers.CreateObjectPath(sceneObject), serializedObject); } m_log.InfoFormat("[ARCHIVER]: Added scene objects to archive."); diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs index f3e5458dce..4d04af16e7 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs @@ -26,6 +26,7 @@ */ using System; +using System.Collections.Generic; using System.IO; using System.Reflection; using System.Threading; @@ -129,7 +130,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests //log4net.Config.XmlConfigurator.Configure(); SceneObjectPart part1 = CreateSceneObjectPart1(); - m_scene.AddNewSceneObject(new SceneObjectGroup(part1), false); + SceneObjectGroup sog1 = new SceneObjectGroup(part1); + m_scene.AddNewSceneObject(sog1, false); SceneObjectPart part2 = CreateSceneObjectPart2(); @@ -169,20 +171,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests bool gotControlFile = false; bool gotNcAssetFile = false; - bool gotObject1File = false; - bool gotObject2File = false; string expectedNcAssetFileName = string.Format("{0}_{1}", ncAssetUuid, "notecard.txt"); - string expectedObject1FileName = string.Format( - "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", - part1.Name, - Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z), - part1.UUID); - string expectedObject2FileName = string.Format( - "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", - part2.Name, - Math.Round(part2.GroupPosition.X), Math.Round(part2.GroupPosition.Y), Math.Round(part2.GroupPosition.Z), - part2.UUID); + + List foundPaths = new List(); + List expectedPaths = new List(); + expectedPaths.Add(ArchiveHelpers.CreateObjectPath(sog1)); + expectedPaths.Add(ArchiveHelpers.CreateObjectPath(sog2)); string filePath; TarArchiveReader.TarEntryType tarEntryType; @@ -202,25 +197,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests } else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) { - string fileName = filePath.Remove(0, ArchiveConstants.OBJECTS_PATH.Length); - - if (fileName.StartsWith(part1.Name)) - { - Assert.That(fileName, Is.EqualTo(expectedObject1FileName)); - gotObject1File = true; - } - else if (fileName.StartsWith(part2.Name)) - { - Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); - gotObject2File = true; - } + foundPaths.Add(filePath); } } Assert.That(gotControlFile, Is.True, "No control file in archive"); Assert.That(gotNcAssetFile, Is.True, "No notecard asset file in archive"); - Assert.That(gotObject1File, Is.True, "No object1 file in archive"); - Assert.That(gotObject2File, Is.True, "No object2 file in archive"); + Assert.That(foundPaths, Is.EquivalentTo(expectedPaths)); // TODO: Test presence of more files and contents of files. } From 2b5b2f4e60737f0a2197ff32cad4314a78671525 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 21 May 2010 21:02:26 +0100 Subject: [PATCH 185/260] Add a new priority scheme that works like FrontBack, but completely deprioritizes static prims, creating a hierarchy as follows: 0 == own avatar < other avatars < pysical prims < static prims For a child agent, simply acts like FrontBack --- .../Region/Framework/Scenes/Prioritizer.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index e6a464206e..1eb0c286e8 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -4,6 +4,7 @@ using log4net; using Nini.Config; using OpenSim.Framework; using OpenMetaverse; +using OpenSim.Region.Physics.Manager; /* * Steps to add a new prioritization policy: @@ -25,6 +26,7 @@ namespace OpenSim.Region.Framework.Scenes Distance = 1, SimpleAngularDistance = 2, FrontBack = 3, + BestAvatarResponsiveness = 4, } public class Prioritizer @@ -50,6 +52,8 @@ namespace OpenSim.Region.Framework.Scenes return GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance case UpdatePrioritizationSchemes.FrontBack: return GetPriorityByFrontBack(client, entity); + case UpdatePrioritizationSchemes.BestAvatarResponsiveness: + return GetPriorityByBestAvatarResponsiveness(client, entity); default: throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); } @@ -130,5 +134,58 @@ namespace OpenSim.Region.Framework.Scenes return double.NaN; } + + private double GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity) + { + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence != null) + { + // If this is an update for our own avatar give it the highest priority + if (presence == entity) + return 0.0; + + // Use group position for child prims + Vector3 entityPos = entity.AbsolutePosition; + if (entity is SceneObjectPart) + entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; + else + entityPos = entity.AbsolutePosition; + + if (!presence.IsChildAgent) + { + if (entity is ScenePresence) + return 1.0; + + // Root agent. Use distance from camera and a priority decrease for objects behind us + Vector3 camPosition = presence.CameraPosition; + Vector3 camAtAxis = presence.CameraAtAxis; + + // Distance + double priority = Vector3.DistanceSquared(camPosition, entityPos); + + // Plane equation + float d = -Vector3.Dot(camPosition, camAtAxis); + float p = Vector3.Dot(camAtAxis, entityPos) + d; + if (p < 0.0f) priority *= 2.0; + + if (entity is SceneObjectPart) + { + PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor; + if (physActor == null || !physActor.IsPhysical) + priority+=100; + } + return priority; + } + else + { + // Child agent. Use the normal distance method + Vector3 presencePos = presence.AbsolutePosition; + + return Vector3.DistanceSquared(presencePos, entityPos); + } + } + + return double.NaN; + } } } From f83acf533b708d7963c5dd1238709898a969c96c Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 21:22:53 +0100 Subject: [PATCH 186/260] Fix a problem where SceneGraph.AddSceneObject() would return false on successfully adding an object rather than true, in defiance of its method documentation This meant that the returns were inconsistent - false would be returned both for various scene object failure conditions (e.g. root part was null) and if the object was successfully added. --- .../World/Archiver/ArchiveReadRequest.cs | 1 - OpenSim/Region/Framework/Scenes/SceneGraph.cs | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs index 8d62fe1668..f97ae5f791 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs @@ -243,7 +243,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver // to the same scene (when this is possible). sceneObject.ResetIDs(); - foreach (SceneObjectPart part in sceneObject.Children.Values) { if (!ResolveUserUuid(part.CreatorID)) diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index ef13c9834a..bb570e74dc 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -278,7 +278,7 @@ namespace OpenSim.Region.Framework.Scenes if (sceneObject == null || sceneObject.RootPart == null || sceneObject.RootPart.UUID == UUID.Zero) return false; - bool alreadyExisted = false; + bool newlyAdded = false; if (m_parentScene.m_clampPrimSize) { @@ -305,7 +305,12 @@ namespace OpenSim.Region.Framework.Scenes lock (sceneObject) { if (!Entities.ContainsKey(sceneObject.UUID)) - { + { +// m_log.DebugFormat( +// "[SCENE GRAPH]: Adding object {0} {1} to region {2}", +// sceneObject.Name, sceneObject.UUID, m_parentScene.RegionInfo.RegionName); + + newlyAdded = true; Entities.Add(sceneObject); m_numPrim += sceneObject.Children.Count; @@ -326,13 +331,15 @@ namespace OpenSim.Region.Framework.Scenes } } } - else - { - alreadyExisted = true; - } +// else +// { +// m_log.WarnFormat( +// "[SCENE GRAPH]: Scene object {0} {1} was already in region {2} on add request", +// sceneObject.Name, sceneObject.UUID, m_parentScene.RegionInfo.RegionName); +// } } - return alreadyExisted; + return newlyAdded; } /// From 8d968249ec02808db18b0d96d94383a7e2b05b03 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 21:38:47 +0100 Subject: [PATCH 187/260] extend TestAddSceneObject() to check Scene.AddNewSceneObject() return boolean improve test to retrieve object by known uuid rather than dynamically assigned local id --- .../Scenes/Tests/SceneObjectBasicTests.cs | 16 +++++++++++++--- OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs | 2 -- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs index 78f2ae3aa1..4fe4a6a6ad 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs @@ -55,12 +55,22 @@ namespace OpenSim.Region.Framework.Scenes.Tests TestHelper.InMethod(); Scene scene = SceneSetupHelpers.SetupScene(); - SceneObjectPart part = SceneSetupHelpers.AddSceneObject(scene); - SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); + + string objName = "obj1"; + UUID objUuid = new UUID("00000000-0000-0000-0000-000000000001"); + + SceneObjectPart part + = new SceneObjectPart(UUID.Zero, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero) + { Name = objName, UUID = objUuid }; + + Assert.That(scene.AddNewSceneObject(new SceneObjectGroup(part), false), Is.True); + + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(objUuid); //m_log.Debug("retrievedPart : {0}", retrievedPart); // If the parts have the same UUID then we will consider them as one and the same - Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID)); + Assert.That(retrievedPart.Name, Is.EqualTo(objName)); + Assert.That(retrievedPart.UUID, Is.EqualTo(objUuid)); } /// diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index ef8ea50e64..91cf323fcb 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -32,7 +32,6 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications; - using OpenSim.Framework.Console; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; @@ -115,7 +114,6 @@ namespace OpenSim.Tests.Common.Setup return SetupScene(name, id, x, y,""); } - /// /// Set up a scene. If it's more then one scene, use the same CommunicationsManager to link regions /// or a different, to get a brand new scene with new shared region modules. From 76135eec5bfcd31357f8e698538747eb72fb8233 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 21:46:41 +0100 Subject: [PATCH 188/260] add test to make sure that adding an object where one already exists in the scene with that uuid fails --- .../Scenes/Tests/SceneObjectBasicTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs index 4fe4a6a6ad..39f8acf982 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs @@ -72,6 +72,40 @@ namespace OpenSim.Region.Framework.Scenes.Tests Assert.That(retrievedPart.Name, Is.EqualTo(objName)); Assert.That(retrievedPart.UUID, Is.EqualTo(objUuid)); } + + [Test] + /// + /// It shouldn't be possible to add a scene object if one with that uuid already exists in the scene. + /// + public void TestAddExistingSceneObjectUuid() + { + TestHelper.InMethod(); + + Scene scene = SceneSetupHelpers.SetupScene(); + + string obj1Name = "Alfred"; + string obj2Name = "Betty"; + UUID objUuid = new UUID("00000000-0000-0000-0000-000000000001"); + + SceneObjectPart part1 + = new SceneObjectPart(UUID.Zero, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero) + { Name = obj1Name, UUID = objUuid }; + + Assert.That(scene.AddNewSceneObject(new SceneObjectGroup(part1), false), Is.True); + + SceneObjectPart part2 + = new SceneObjectPart(UUID.Zero, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero) + { Name = obj2Name, UUID = objUuid }; + + Assert.That(scene.AddNewSceneObject(new SceneObjectGroup(part2), false), Is.False); + + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(objUuid); + + //m_log.Debug("retrievedPart : {0}", retrievedPart); + // If the parts have the same UUID then we will consider them as one and the same + Assert.That(retrievedPart.Name, Is.EqualTo(obj1Name)); + Assert.That(retrievedPart.UUID, Is.EqualTo(objUuid)); + } /// /// Test deleting an object from a scene. From 4b518e0288386d37c2e13a9b3de6edcd5ed04412 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 21:48:47 +0100 Subject: [PATCH 189/260] minor: remove LongRunning test designator from TestAddSceneObject() since it isn't, really --- OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs index 39f8acf982..4baa22c0fb 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs @@ -49,7 +49,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests /// /// Test adding an object to a scene. /// - [Test, LongRunning] + [Test] public void TestAddSceneObject() { TestHelper.InMethod(); From 93ef65c69055157e0b7d51e544abe5a1035f40f0 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 21 May 2010 13:55:36 -0700 Subject: [PATCH 190/260] * Moving all of the prioritization/reprioritization code into a new file Prioritizer.cs * Simplified the interest management code to make it easier to add new policies. Prioritization and reprioritization share code paths now * Improved the distance and front back policies to always give your avatar the highest priority --- .../Client/MXP/ClientStack/MXPClientView.cs | 2 +- .../ClientStack/SirikataClientView.cs | 2 +- .../VWoHTTP/ClientStack/VWHClientView.cs | 2 +- OpenSim/Framework/IClientAPI.cs | 21 ++- OpenSim/Framework/ISceneEntity.cs | 1 + .../ClientStack/LindenUDP/LLClientView.cs | 46 +++---- .../Examples/SimpleModule/MyNpcCharacter.cs | 2 +- OpenSim/Region/Framework/Scenes/EntityBase.cs | 3 +- .../Region/Framework/Scenes/Prioritizer.cs | 122 ++++++++++++++++++ OpenSim/Region/Framework/Scenes/Scene.cs | 96 +++++++------- .../Framework/Scenes/SceneObjectGroup.cs | 101 +-------------- .../Region/Framework/Scenes/ScenePresence.cs | 118 +---------------- .../Server/IRCClientView.cs | 2 +- .../OptionalModules/World/NPC/NPCAvatar.cs | 2 +- OpenSim/Tests/Common/Mock/TestClient.cs | 2 +- 15 files changed, 207 insertions(+), 315 deletions(-) create mode 100644 OpenSim/Region/Framework/Scenes/Prioritizer.cs diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index a62d8974a9..3e926587c2 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1065,7 +1065,7 @@ namespace OpenSim.Client.MXP.ClientStack throw new System.NotImplementedException(); } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { } diff --git a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs index d1f098892b..c293480e76 100644 --- a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs +++ b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs @@ -609,7 +609,7 @@ namespace OpenSim.Client.Sirikata.ClientStack throw new System.NotImplementedException(); } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index c0da3263c2..51026abf80 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -615,7 +615,7 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 00681cf80d..f51bf71f5d 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -458,8 +458,6 @@ namespace OpenSim.Framework public delegate void PlacesQuery(UUID QueryID, UUID TransactionID, string QueryText, uint QueryFlags, byte Category, string SimName, IClientAPI client); public delegate void AgentFOV(IClientAPI client, float verticalAngle); - - public delegate double UpdatePriorityHandler(UpdatePriorityData data); public delegate void MuteListEntryUpdate(IClientAPI client, UUID MuteID, string Name, int Flags,UUID AgentID); @@ -571,17 +569,16 @@ namespace OpenSim.Framework public float dwell; } - public struct UpdatePriorityData { - private double m_priority; - private uint m_localID; + public class EntityUpdate + { + public ISceneEntity Entity; + public PrimUpdateFlags Flags; - public UpdatePriorityData(double priority, uint localID) { - this.m_priority = priority; - this.m_localID = localID; + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags) + { + Entity = entity; + Flags = flags; } - - public double priority { get { return this.m_priority; } } - public uint localID { get { return this.m_localID; } } } /// @@ -1024,7 +1021,7 @@ namespace OpenSim.Framework void SendAvatarDataImmediate(ISceneEntity avatar); void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags); - void ReprioritizeUpdates(UpdatePriorityHandler handler); + void ReprioritizeUpdates(); void FlushPrimUpdates(); void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, diff --git a/OpenSim/Framework/ISceneEntity.cs b/OpenSim/Framework/ISceneEntity.cs index fa3c514667..5ac364f994 100644 --- a/OpenSim/Framework/ISceneEntity.cs +++ b/OpenSim/Framework/ISceneEntity.cs @@ -33,5 +33,6 @@ namespace OpenSim.Framework { UUID UUID { get; } uint LocalId { get; } + Vector3 AbsolutePosition { get; } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index c8a542b360..9eb35fa67b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -50,18 +50,6 @@ using Nini.Config; namespace OpenSim.Region.ClientStack.LindenUDP { - public class EntityUpdate - { - public ISceneEntity Entity; - public PrimUpdateFlags Flags; - - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags) - { - Entity = entity; - Flags = flags; - } - } - public delegate bool PacketMethod(IClientAPI simClient, Packet packet); /// @@ -325,6 +313,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private Prioritizer m_prioritizer; /// /// List used in construction of data blocks for an object update packet. This is to stop us having to @@ -462,6 +451,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_propertiesPacketTimer = new Timer(100); m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; + m_prioritizer = new Prioritizer(m_scene); + RegisterLocalPacketHandlers(); } @@ -3457,14 +3448,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void SendPrimUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) { - double priority; - - if (entity is SceneObjectPart) - priority = ((SceneObjectPart)entity).ParentGroup.GetUpdatePriority(this); - else if (entity is ScenePresence) - priority = ((ScenePresence)entity).GetUpdatePriority(this); - else - priority = 0.0d; + double priority = m_prioritizer.GetUpdatePriority(this, entity); lock (m_entityUpdates.SyncRoot) m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags), entity.LocalId); @@ -3613,19 +3597,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Packet Sending } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { //m_log.Debug("[CLIENT]: Reprioritizing prim updates for " + m_firstName + " " + m_lastName); - PriorityQueue.UpdatePriorityHandler update_priority_handler = - delegate(ref double priority, uint local_id) - { - priority = handler(new UpdatePriorityData(priority, local_id)); - return priority != double.NaN; - }; - lock (m_entityUpdates.SyncRoot) - m_entityUpdates.Reprioritize(update_priority_handler); + m_entityUpdates.Reprioritize(UpdatePriorityHandler); + } + + private bool UpdatePriorityHandler(ref double priority, uint localID) + { + EntityBase entity; + if (m_scene.Entities.TryGetValue(localID, out entity)) + { + priority = m_prioritizer.GetUpdatePriority(this, entity); + } + + return priority != double.NaN; } public void FlushPrimUpdates() diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index aac47d19a3..967438fcf5 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -548,7 +548,7 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { } diff --git a/OpenSim/Region/Framework/Scenes/EntityBase.cs b/OpenSim/Region/Framework/Scenes/EntityBase.cs index 1c76c546e5..4e25c468c0 100644 --- a/OpenSim/Region/Framework/Scenes/EntityBase.cs +++ b/OpenSim/Region/Framework/Scenes/EntityBase.cs @@ -28,11 +28,12 @@ using System; using System.Runtime.Serialization; using System.Security.Permissions; +using OpenSim.Framework; using OpenMetaverse; namespace OpenSim.Region.Framework.Scenes { - public abstract class EntityBase + public abstract class EntityBase : ISceneEntity { /// /// The scene to which this entity belongs diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs new file mode 100644 index 0000000000..af250143dd --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + public enum UpdatePrioritizationSchemes + { + Time = 0, + Distance = 1, + SimpleAngularDistance = 2, + FrontBack = 3, + } + + public class Prioritizer + { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private Scene m_scene; + + public Prioritizer(Scene scene) + { + m_scene = scene; + } + + public double GetUpdatePriority(IClientAPI client, ISceneEntity entity) + { + switch (m_scene.UpdatePrioritizationScheme) + { + case UpdatePrioritizationSchemes.Time: + return GetPriorityByTime(); + case UpdatePrioritizationSchemes.Distance: + return GetPriorityByDistance(client, entity); + case UpdatePrioritizationSchemes.SimpleAngularDistance: + return GetPriorityByDistance(client, entity); + case UpdatePrioritizationSchemes.FrontBack: + return GetPriorityByFrontBack(client, entity); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); + } + } + + private double GetPriorityByTime() + { + return DateTime.UtcNow.ToOADate(); + } + + private double GetPriorityByDistance(IClientAPI client, ISceneEntity entity) + { + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence != null) + { + // If this is an update for our own avatar give it the highest priority + if (presence == entity) + return 0.0; + + // Use the camera position for local agents and avatar position for remote agents + Vector3 presencePos = (presence.IsChildAgent) ? + presence.AbsolutePosition : + presence.CameraPosition; + + // Use group position for child prims + Vector3 entityPos; + if (entity is SceneObjectPart) + entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; + else + entityPos = entity.AbsolutePosition; + + return Vector3.DistanceSquared(presencePos, entityPos); + } + + return double.NaN; + } + + private double GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity) + { + ScenePresence presence = m_scene.GetScenePresence(client.AgentId); + if (presence != null) + { + // If this is an update for our own avatar give it the highest priority + if (presence == entity) + return 0.0; + + // Use group position for child prims + Vector3 entityPos = entity.AbsolutePosition; + if (entity is SceneObjectPart) + entityPos = m_scene.GetGroupByPrim(entity.LocalId).AbsolutePosition; + else + entityPos = entity.AbsolutePosition; + + if (!presence.IsChildAgent) + { + // Root agent. Use distance from camera and a priority decrease for objects behind us + Vector3 camPosition = presence.CameraPosition; + Vector3 camAtAxis = presence.CameraAtAxis; + + // Distance + double priority = Vector3.DistanceSquared(camPosition, entityPos); + + // Plane equation + float d = -Vector3.Dot(camPosition, camAtAxis); + float p = Vector3.Dot(camAtAxis, entityPos) + d; + if (p < 0.0f) priority *= 2.0; + + return priority; + } + else + { + // Child agent. Use the normal distance method + Vector3 presencePos = presence.AbsolutePosition; + + return Vector3.DistanceSquared(presencePos, entityPos); + } + } + + return double.NaN; + } + } +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index de8ecc2713..f35dffca32 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -58,13 +58,6 @@ namespace OpenSim.Region.Framework.Scenes public partial class Scene : SceneBase { - public enum UpdatePrioritizationSchemes { - Time = 0, - Distance = 1, - SimpleAngularDistance = 2, - FrontBack = 3, - } - public delegate void SynchronizeSceneHandler(Scene scene); public SynchronizeSceneHandler SynchronizeScene = null; @@ -388,12 +381,6 @@ namespace OpenSim.Region.Framework.Scenes private int m_lastUpdate; private bool m_firstHeartbeat = true; - private UpdatePrioritizationSchemes m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; - private bool m_reprioritization_enabled = true; - private double m_reprioritization_interval = 5000.0; - private double m_root_reprioritization_distance = 10.0; - private double m_child_reprioritization_distance = 20.0; - private object m_deleting_scene_object = new object(); // the minimum time that must elapse before a changed object will be considered for persisted @@ -401,15 +388,21 @@ namespace OpenSim.Region.Framework.Scenes // the maximum time that must elapse before a changed object will be considered for persisted public long m_persistAfter = DEFAULT_MAX_TIME_FOR_PERSISTENCE * 10000000L; + private UpdatePrioritizationSchemes m_priorityScheme = UpdatePrioritizationSchemes.Time; + private bool m_reprioritizationEnabled = true; + private double m_reprioritizationInterval = 5000.0; + private double m_rootReprioritizationDistance = 10.0; + private double m_childReprioritizationDistance = 20.0; + #endregion #region Properties - public UpdatePrioritizationSchemes UpdatePrioritizationScheme { get { return this.m_update_prioritization_scheme; } } - public bool IsReprioritizationEnabled { get { return m_reprioritization_enabled; } } - public double ReprioritizationInterval { get { return m_reprioritization_interval; } } - public double RootReprioritizationDistance { get { return m_root_reprioritization_distance; } } - public double ChildReprioritizationDistance { get { return m_child_reprioritization_distance; } } + public UpdatePrioritizationSchemes UpdatePrioritizationScheme { get { return m_priorityScheme; } } + public bool IsReprioritizationEnabled { get { return m_reprioritizationEnabled; } } + public double ReprioritizationInterval { get { return m_reprioritizationInterval; } } + public double RootReprioritizationDistance { get { return m_rootReprioritizationDistance; } } + public double ChildReprioritizationDistance { get { return m_childReprioritizationDistance; } } public AgentCircuitManager AuthenticateHandler { @@ -611,6 +604,8 @@ namespace OpenSim.Region.Framework.Scenes m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this); m_asyncSceneObjectDeleter.Enabled = true; + #region Region Settings + // Load region settings m_regInfo.RegionSettings = m_storageManager.DataStore.LoadRegionSettings(m_regInfo.RegionID); if (m_storageManager.EstateDataStore != null) @@ -657,6 +652,8 @@ namespace OpenSim.Region.Framework.Scenes } } + #endregion Region Settings + MainConsole.Instance.Commands.AddCommand("region", false, "reload estate", "reload estate", "Reload the estate data", HandleReloadEstate); @@ -701,6 +698,8 @@ namespace OpenSim.Region.Framework.Scenes m_simulatorVersion = simulatorVersion + " (" + Util.GetRuntimeInformation() + ")"; + #region Region Config + try { // Region config overrides global config @@ -754,38 +753,6 @@ namespace OpenSim.Region.Framework.Scenes m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl); - IConfig interest_management_config = m_config.Configs["InterestManagement"]; - if (interest_management_config != null) - { - string update_prioritization_scheme = interest_management_config.GetString("UpdatePrioritizationScheme", "Time").Trim().ToLower(); - switch (update_prioritization_scheme) - { - case "time": - m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; - break; - case "distance": - m_update_prioritization_scheme = UpdatePrioritizationSchemes.Distance; - break; - case "simpleangulardistance": - m_update_prioritization_scheme = UpdatePrioritizationSchemes.SimpleAngularDistance; - break; - case "frontback": - m_update_prioritization_scheme = UpdatePrioritizationSchemes.FrontBack; - break; - default: - m_log.Warn("[SCENE]: UpdatePrioritizationScheme was not recognized, setting to default settomg of Time"); - m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; - break; - } - - m_reprioritization_enabled = interest_management_config.GetBoolean("ReprioritizationEnabled", true); - m_reprioritization_interval = interest_management_config.GetDouble("ReprioritizationInterval", 5000.0); - m_root_reprioritization_distance = interest_management_config.GetDouble("RootReprioritizationDistance", 10.0); - m_child_reprioritization_distance = interest_management_config.GetDouble("ChildReprioritizationDistance", 20.0); - } - - m_log.Info("[SCENE]: Using the " + m_update_prioritization_scheme + " prioritization scheme"); - #region BinaryStats try @@ -822,6 +789,35 @@ namespace OpenSim.Region.Framework.Scenes { m_log.Warn("[SCENE]: Failed to load StartupConfig"); } + + #endregion Region Config + + #region Interest Management + + IConfig interestConfig = m_config.Configs["InterestManagement"]; + if (interestConfig != null) + { + string update_prioritization_scheme = interestConfig.GetString("UpdatePrioritizationScheme", "Time").Trim().ToLower(); + + try + { + m_priorityScheme = (UpdatePrioritizationSchemes)Enum.Parse(typeof(UpdatePrioritizationSchemes), update_prioritization_scheme, true); + } + catch (Exception) + { + m_log.Warn("[PRIORITIZER]: UpdatePrioritizationScheme was not recognized, setting to default prioritizer Time"); + m_priorityScheme = UpdatePrioritizationSchemes.Time; + } + + m_reprioritizationEnabled = interestConfig.GetBoolean("ReprioritizationEnabled", true); + m_reprioritizationInterval = interestConfig.GetDouble("ReprioritizationInterval", 5000.0); + m_rootReprioritizationDistance = interestConfig.GetDouble("RootReprioritizationDistance", 10.0); + m_childReprioritizationDistance = interestConfig.GetDouble("ChildReprioritizationDistance", 20.0); + } + + m_log.Info("[SCENE]: Using the " + m_priorityScheme + " prioritization scheme"); + + #endregion Interest Management } /// diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 8aefd50977..4453bebec8 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -3602,106 +3602,7 @@ namespace OpenSim.Region.Framework.Scenes SetFromItemID(uuid); } - #endregion - public double GetUpdatePriority(IClientAPI client) - { - switch (Scene.UpdatePrioritizationScheme) - { - case Scene.UpdatePrioritizationSchemes.Time: - return GetPriorityByTime(); - case Scene.UpdatePrioritizationSchemes.Distance: - return GetPriorityByDistance(client); - case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: - return GetPriorityBySimpleAngularDistance(client); - case Scenes.Scene.UpdatePrioritizationSchemes.FrontBack: - return GetPriorityByFrontBack(client); - default: - throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); - } - } - - private double GetPriorityByTime() - { - return DateTime.Now.ToOADate(); - } - - private double GetPriorityByDistance(IClientAPI client) - { - ScenePresence presence = Scene.GetScenePresence(client.AgentId); - if (presence != null) - { - return GetPriorityByDistance((presence.IsChildAgent) ? - presence.AbsolutePosition : presence.CameraPosition); - } - return double.NaN; - } - - private double GetPriorityBySimpleAngularDistance(IClientAPI client) - { - ScenePresence presence = Scene.GetScenePresence(client.AgentId); - if (presence != null) - { - return GetPriorityBySimpleAngularDistance((presence.IsChildAgent) ? - presence.AbsolutePosition : presence.CameraPosition); - } - return double.NaN; - } - - private double GetPriorityByFrontBack(IClientAPI client) - { - ScenePresence presence = Scene.GetScenePresence(client.AgentId); - if (presence != null) - { - return GetPriorityByFrontBack(presence.CameraPosition, presence.CameraAtAxis); - } - return double.NaN; - } - - public double GetPriorityByDistance(Vector3 position) - { - return Vector3.Distance(AbsolutePosition, position); - } - - public double GetPriorityBySimpleAngularDistance(Vector3 position) - { - double distance = Vector3.Distance(position, AbsolutePosition); - if (distance >= double.Epsilon) - { - float height; - Vector3 box = GetAxisAlignedBoundingBox(out height); - - double angle = box.X / distance; - double max = angle; - - angle = box.Y / distance; - if (max < angle) - max = angle; - - angle = box.Z / distance; - if (max < angle) - max = angle; - - return -max; - } - else - return double.MinValue; - } - - public double GetPriorityByFrontBack(Vector3 camPosition, Vector3 camAtAxis) - { - // Distance - double priority = Vector3.Distance(camPosition, AbsolutePosition); - - // Scale - //priority -= GroupScale().Length(); - - // Plane equation - float d = -Vector3.Dot(camPosition, camAtAxis); - float p = Vector3.Dot(camAtAxis, AbsolutePosition) + d; - if (p < 0.0f) priority *= 2.0f; - - return priority; - } + #endregion } } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index ee0eb07265..2ce1b6800d 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -3777,123 +3777,9 @@ namespace OpenSim.Region.Framework.Scenes } } - public double GetUpdatePriority(IClientAPI client) - { - switch (Scene.UpdatePrioritizationScheme) - { - case Scene.UpdatePrioritizationSchemes.Time: - return GetPriorityByTime(); - case Scene.UpdatePrioritizationSchemes.Distance: - return GetPriorityByDistance(client); - case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: - return GetPriorityByDistance(client); - case Scenes.Scene.UpdatePrioritizationSchemes.FrontBack: - return GetPriorityByFrontBack(client); - default: - throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); - } - } - - private double GetPriorityByTime() - { - return DateTime.Now.ToOADate(); - } - - private double GetPriorityByDistance(IClientAPI client) - { - ScenePresence presence = Scene.GetScenePresence(client.AgentId); - if (presence != null) - { - return GetPriorityByDistance((presence.IsChildAgent) ? - presence.AbsolutePosition : presence.CameraPosition); - } - return double.NaN; - } - - private double GetPriorityByFrontBack(IClientAPI client) - { - ScenePresence presence = Scene.GetScenePresence(client.AgentId); - if (presence != null) - { - return GetPriorityByFrontBack(presence.CameraPosition, presence.CameraAtAxis); - } - return double.NaN; - } - - private double GetPriorityByDistance(Vector3 position) - { - return Vector3.Distance(AbsolutePosition, position); - } - - private double GetPriorityByFrontBack(Vector3 camPosition, Vector3 camAtAxis) - { - // Distance - double priority = Vector3.Distance(camPosition, AbsolutePosition); - - // Plane equation - float d = -Vector3.Dot(camPosition, camAtAxis); - float p = Vector3.Dot(camAtAxis, AbsolutePosition) + d; - if (p < 0.0f) priority *= 2.0f; - - return priority; - } - - private double GetSOGUpdatePriority(SceneObjectGroup sog) - { - switch (Scene.UpdatePrioritizationScheme) - { - case Scene.UpdatePrioritizationSchemes.Time: - throw new InvalidOperationException("UpdatePrioritizationScheme for time not supported for reprioritization"); - case Scene.UpdatePrioritizationSchemes.Distance: - return sog.GetPriorityByDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); - case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: - return sog.GetPriorityBySimpleAngularDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); - case Scenes.Scene.UpdatePrioritizationSchemes.FrontBack: - return sog.GetPriorityByFrontBack(CameraPosition, CameraAtAxis); - default: - throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); - } - } - - private double UpdatePriority(UpdatePriorityData data) - { - EntityBase entity; - SceneObjectGroup group; - - if (Scene.Entities.TryGetValue(data.localID, out entity)) - { - group = entity as SceneObjectGroup; - if (group != null) - return GetSOGUpdatePriority(group); - - ScenePresence presence = entity as ScenePresence; - if (presence == null) - throw new InvalidOperationException("entity found is neither SceneObjectGroup nor ScenePresence"); - switch (Scene.UpdatePrioritizationScheme) - { - case Scene.UpdatePrioritizationSchemes.Time: - throw new InvalidOperationException("UpdatePrioritization for time not supported for reprioritization"); - case Scene.UpdatePrioritizationSchemes.Distance: - case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: - return GetPriorityByDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); - case Scenes.Scene.UpdatePrioritizationSchemes.FrontBack: - return GetPriorityByFrontBack(CameraPosition, CameraAtAxis); - default: - throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); - } - } - else - { - group = Scene.GetGroupByPrim(data.localID); - if (group != null) - return GetSOGUpdatePriority(group); - } - return double.NaN; - } - private void ReprioritizeUpdates() { - if (Scene.IsReprioritizationEnabled && Scene.UpdatePrioritizationScheme != Scene.UpdatePrioritizationSchemes.Time) + if (Scene.IsReprioritizationEnabled && Scene.UpdatePrioritizationScheme != UpdatePrioritizationSchemes.Time) { lock (m_reprioritization_timer) { @@ -3907,7 +3793,7 @@ namespace OpenSim.Region.Framework.Scenes private void Reprioritize(object sender, ElapsedEventArgs e) { - m_controllingClient.ReprioritizeUpdates(UpdatePriority); + m_controllingClient.ReprioritizeUpdates(); lock (m_reprioritization_timer) { diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 84faac0e00..27de529e22 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1065,7 +1065,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { } diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 906669187c..d2279c735e 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -638,7 +638,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index edb76428b4..6b0efe920e 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -641,7 +641,7 @@ namespace OpenSim.Tests.Common.Mock { } - public void ReprioritizeUpdates(UpdatePriorityHandler handler) + public void ReprioritizeUpdates() { } From 5287c5f7fbd75ddb36f18a8692adb38cd824aaee Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 21:57:56 +0100 Subject: [PATCH 191/260] rearrange SceneGraph.AddSceneObject() to return earlier if an object with that uuid is already in the scene this means that we don't perform pointless work --- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 110 +++++++++--------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index bb570e74dc..e923a92566 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -278,68 +278,64 @@ namespace OpenSim.Region.Framework.Scenes if (sceneObject == null || sceneObject.RootPart == null || sceneObject.RootPart.UUID == UUID.Zero) return false; - bool newlyAdded = false; - - if (m_parentScene.m_clampPrimSize) - { - foreach (SceneObjectPart part in sceneObject.Children.Values) - { - Vector3 scale = part.Shape.Scale; - - if (scale.X > m_parentScene.m_maxNonphys) - scale.X = m_parentScene.m_maxNonphys; - if (scale.Y > m_parentScene.m_maxNonphys) - scale.Y = m_parentScene.m_maxNonphys; - if (scale.Z > m_parentScene.m_maxNonphys) - scale.Z = m_parentScene.m_maxNonphys; - - part.Shape.Scale = scale; - } - } - - sceneObject.AttachToScene(m_parentScene); - - if (sendClientUpdates) - sceneObject.ScheduleGroupForFullUpdate(); - lock (sceneObject) - { - if (!Entities.ContainsKey(sceneObject.UUID)) - { -// m_log.DebugFormat( -// "[SCENE GRAPH]: Adding object {0} {1} to region {2}", -// sceneObject.Name, sceneObject.UUID, m_parentScene.RegionInfo.RegionName); - - newlyAdded = true; - Entities.Add(sceneObject); - m_numPrim += sceneObject.Children.Count; - - if (attachToBackup) - sceneObject.AttachToBackup(); - - if (OnObjectCreate != null) - OnObjectCreate(sceneObject); - - lock (m_dictionary_lock) - { - SceneObjectGroupsByFullID[sceneObject.UUID] = sceneObject; - SceneObjectGroupsByLocalID[sceneObject.LocalId] = sceneObject; - foreach (SceneObjectPart part in sceneObject.Children.Values) - { - SceneObjectGroupsByFullID[part.UUID] = sceneObject; - SceneObjectGroupsByLocalID[part.LocalId] = sceneObject; - } - } - } -// else -// { + { + if (Entities.ContainsKey(sceneObject.UUID)) + { // m_log.WarnFormat( // "[SCENE GRAPH]: Scene object {0} {1} was already in region {2} on add request", -// sceneObject.Name, sceneObject.UUID, m_parentScene.RegionInfo.RegionName); -// } +// sceneObject.Name, sceneObject.UUID, m_parentScene.RegionInfo.RegionName); + return false; + } + +// m_log.DebugFormat( +// "[SCENE GRAPH]: Adding object {0} {1} to region {2}", +// sceneObject.Name, sceneObject.UUID, m_parentScene.RegionInfo.RegionName); + + if (m_parentScene.m_clampPrimSize) + { + foreach (SceneObjectPart part in sceneObject.Children.Values) + { + Vector3 scale = part.Shape.Scale; + + if (scale.X > m_parentScene.m_maxNonphys) + scale.X = m_parentScene.m_maxNonphys; + if (scale.Y > m_parentScene.m_maxNonphys) + scale.Y = m_parentScene.m_maxNonphys; + if (scale.Z > m_parentScene.m_maxNonphys) + scale.Z = m_parentScene.m_maxNonphys; + + part.Shape.Scale = scale; + } + } + + sceneObject.AttachToScene(m_parentScene); + + if (sendClientUpdates) + sceneObject.ScheduleGroupForFullUpdate(); + + Entities.Add(sceneObject); + m_numPrim += sceneObject.Children.Count; + + if (attachToBackup) + sceneObject.AttachToBackup(); + + if (OnObjectCreate != null) + OnObjectCreate(sceneObject); + + lock (m_dictionary_lock) + { + SceneObjectGroupsByFullID[sceneObject.UUID] = sceneObject; + SceneObjectGroupsByLocalID[sceneObject.LocalId] = sceneObject; + foreach (SceneObjectPart part in sceneObject.Children.Values) + { + SceneObjectGroupsByFullID[part.UUID] = sceneObject; + SceneObjectGroupsByLocalID[part.LocalId] = sceneObject; + } + } } - return newlyAdded; + return true; } /// From abd5d1f7470ba07b784c6dbad4cdcdeaedc58d37 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 21 May 2010 14:04:10 -0700 Subject: [PATCH 192/260] * Added code comments to Prioritizer.cs that document how to add a new update priority policy --- OpenSim/Region/Framework/Scenes/Prioritizer.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs index af250143dd..e6a464206e 100644 --- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs @@ -5,6 +5,18 @@ using Nini.Config; using OpenSim.Framework; using OpenMetaverse; +/* + * Steps to add a new prioritization policy: + * + * - Add a new value to the UpdatePrioritizationSchemes enum. + * - Specify this new value in the [InterestManagement] section of your + * OpenSim.ini. The name in the config file must match the enum value name + * (although it is not case sensitive). + * - Write a new GetPriorityBy*() method in this class. + * - Add a new entry to the switch statement in GetUpdatePriority() that calls + * your method. + */ + namespace OpenSim.Region.Framework.Scenes { public enum UpdatePrioritizationSchemes @@ -35,7 +47,7 @@ namespace OpenSim.Region.Framework.Scenes case UpdatePrioritizationSchemes.Distance: return GetPriorityByDistance(client, entity); case UpdatePrioritizationSchemes.SimpleAngularDistance: - return GetPriorityByDistance(client, entity); + return GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance case UpdatePrioritizationSchemes.FrontBack: return GetPriorityByFrontBack(client, entity); default: From d0eecf03986b1b1bd7458123a2deed522587b6dc Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 21 May 2010 14:17:03 -0700 Subject: [PATCH 193/260] Added a sanity check before using m_config in the Scene constructor --- OpenSim/Region/Framework/Scenes/Scene.cs | 35 +++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f35dffca32..46fbcd3832 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -794,25 +794,28 @@ namespace OpenSim.Region.Framework.Scenes #region Interest Management - IConfig interestConfig = m_config.Configs["InterestManagement"]; - if (interestConfig != null) + if (m_config != null) { - string update_prioritization_scheme = interestConfig.GetString("UpdatePrioritizationScheme", "Time").Trim().ToLower(); - - try + IConfig interestConfig = m_config.Configs["InterestManagement"]; + if (interestConfig != null) { - m_priorityScheme = (UpdatePrioritizationSchemes)Enum.Parse(typeof(UpdatePrioritizationSchemes), update_prioritization_scheme, true); - } - catch (Exception) - { - m_log.Warn("[PRIORITIZER]: UpdatePrioritizationScheme was not recognized, setting to default prioritizer Time"); - m_priorityScheme = UpdatePrioritizationSchemes.Time; - } + string update_prioritization_scheme = interestConfig.GetString("UpdatePrioritizationScheme", "Time").Trim().ToLower(); - m_reprioritizationEnabled = interestConfig.GetBoolean("ReprioritizationEnabled", true); - m_reprioritizationInterval = interestConfig.GetDouble("ReprioritizationInterval", 5000.0); - m_rootReprioritizationDistance = interestConfig.GetDouble("RootReprioritizationDistance", 10.0); - m_childReprioritizationDistance = interestConfig.GetDouble("ChildReprioritizationDistance", 20.0); + try + { + m_priorityScheme = (UpdatePrioritizationSchemes)Enum.Parse(typeof(UpdatePrioritizationSchemes), update_prioritization_scheme, true); + } + catch (Exception) + { + m_log.Warn("[PRIORITIZER]: UpdatePrioritizationScheme was not recognized, setting to default prioritizer Time"); + m_priorityScheme = UpdatePrioritizationSchemes.Time; + } + + m_reprioritizationEnabled = interestConfig.GetBoolean("ReprioritizationEnabled", true); + m_reprioritizationInterval = interestConfig.GetDouble("ReprioritizationInterval", 5000.0); + m_rootReprioritizationDistance = interestConfig.GetDouble("RootReprioritizationDistance", 10.0); + m_childReprioritizationDistance = interestConfig.GetDouble("ChildReprioritizationDistance", 20.0); + } } m_log.Info("[SCENE]: Using the " + m_priorityScheme + " prioritization scheme"); From 4e45718833f72b9149aed6d503e967b8916e5d08 Mon Sep 17 00:00:00 2001 From: Mikko Pallari Date: Thu, 15 Apr 2010 08:23:51 +0300 Subject: [PATCH 194/260] Added overload of SendGenericMessage to LLClientView with string list as parameter. Now modules themselfs don't necessarily need to convert strings to byte arrays. Added this as it was removed in LightShare patch. --- OpenSim/Client/MXP/ClientStack/MXPClientView.cs | 4 ++++ .../Sirikata/ClientStack/SirikataClientView.cs | 4 ++++ .../Client/VWoHTTP/ClientStack/VWHClientView.cs | 4 ++++ OpenSim/Framework/IClientAPI.cs | 1 + .../Region/ClientStack/LindenUDP/LLClientView.cs | 15 +++++++++++++++ .../Examples/SimpleModule/MyNpcCharacter.cs | 4 ++++ .../Server/IRCClientView.cs | 5 +++++ .../Region/OptionalModules/World/NPC/NPCAvatar.cs | 5 +++++ OpenSim/Tests/Common/Mock/TestClient.cs | 5 +++++ 9 files changed, 47 insertions(+) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index 3e926587c2..d742039115 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -931,6 +931,10 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } + public void SendGenericMessage(string method, List message) + { + } + public void SendGenericMessage(string method, List message) { // Need to translate to MXP somehow diff --git a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs index c293480e76..d725943197 100644 --- a/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs +++ b/OpenSim/Client/Sirikata/ClientStack/SirikataClientView.cs @@ -504,6 +504,10 @@ namespace OpenSim.Client.Sirikata.ClientStack throw new System.NotImplementedException(); } + public void SendGenericMessage(string method, List message) + { + } + public void SendGenericMessage(string method, List message) { throw new System.NotImplementedException(); diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index 51026abf80..2eec844b9d 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -510,6 +510,10 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } + public void SendGenericMessage(string method, List message) + { + } + public void SendGenericMessage(string method, List message) { throw new System.NotImplementedException(); diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index f51bf71f5d..c1333fc285 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -983,6 +983,7 @@ namespace OpenSim.Framework void SendInstantMessage(GridInstantMessage im); + void SendGenericMessage(string method, List message); void SendGenericMessage(string method, List message); void SendLayerData(float[] map); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 9eb35fa67b..e67428d3c2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -816,6 +816,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + public void SendGenericMessage(string method, List message) + { + GenericMessagePacket gmp = new GenericMessagePacket(); + gmp.MethodData.Method = Util.StringToBytes256(method); + gmp.ParamList = new GenericMessagePacket.ParamListBlock[message.Count]; + int i = 0; + foreach (string val in message) + { + gmp.ParamList[i] = new GenericMessagePacket.ParamListBlock(); + gmp.ParamList[i++].Parameter = Util.StringToBytes256(val); + } + + OutPacket(gmp, ThrottleOutPacketType.Task); + } + public void SendGenericMessage(string method, List message) { GenericMessagePacket gmp = new GenericMessagePacket(); diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 967438fcf5..2681d4fcf3 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -461,6 +461,10 @@ namespace OpenSim.Region.Examples.SimpleModule } + public void SendGenericMessage(string method, List message) + { + } + public void SendGenericMessage(string method, List message) { diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 27de529e22..7453eae63e 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -965,6 +965,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server // TODO } + public void SendGenericMessage(string method, List message) + { + + } + public void SendGenericMessage(string method, List message) { diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index d2279c735e..146b3d6f32 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -551,6 +551,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC } + public void SendGenericMessage(string method, List message) + { + + } + public void SendGenericMessage(string method, List message) { diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 6b0efe920e..94d9d72b02 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -519,6 +519,11 @@ namespace OpenSim.Tests.Common.Mock } + public void SendGenericMessage(string method, List message) + { + + } + public void SendGenericMessage(string method, List message) { From e5bcd8b341c974afe3f1fbd06a537319bc7c4485 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 23:29:37 +0100 Subject: [PATCH 195/260] Apply adaption of patch in http://opensimulator.org/mantis/view.php?id=4628 This prevents a ground-sitting avatar from being moved about in mouselook Thanks mirceakitsune! --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 2ce1b6800d..ad7c3aecd2 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -1312,8 +1312,7 @@ namespace OpenSim.Region.Framework.Scenes // Setting parent ID would fix this, if we knew what value // to use. Or we could add a m_isSitting variable. //Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED"); - SitGround = true; - + SitGround = true; } // In the future, these values might need to go global. @@ -1330,7 +1329,7 @@ namespace OpenSim.Region.Framework.Scenes bool update_movementflag = false; - if (m_allowMovement) + if (m_allowMovement && !SitGround) { if (agentData.UseClientAgentPosition) { From 074937e0e510e9be6b7a0e5639d93a93be38d6b1 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 23:34:47 +0100 Subject: [PATCH 196/260] Apply http://opensimulator.org/mantis/view.php?id=4627 Adds OwnerId and CreatorId properties to MRM.IObject --- .../Scripting/Minimodule/Interfaces/IObject.cs | 10 ++++++++++ .../OptionalModules/Scripting/Minimodule/SOPObject.cs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs index 30580e7919..29f7f68419 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs @@ -97,6 +97,16 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule /// String Description { get; set; } + /// + /// Returns the UUID of the Owner of the Object. + /// + UUID OwnerId { get; } + + /// + /// Returns the UUID of the Creator of the Object. + /// + UUID CreatorId { get; } + /// /// Returns the root object of a linkset. If this object is the root, it will return itself. /// diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs index 5bfe4bedce..f51498cc3a 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs @@ -169,6 +169,16 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule } } + public UUID OwnerId + { + get { return GetSOP().OwnerID;} + } + + public UUID CreatorId + { + get { return GetSOP().CreatorID;} + } + public IObject[] Children { get From b2197e3b94f3865ba3927a4aaaf38cffe141cd72 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 21 May 2010 23:37:05 +0100 Subject: [PATCH 197/260] Apply http://opensimulator.org/mantis/view.php?id=4632 Adds dialog methods for MRM. Thanks ziah. --- .../Minimodule/Interfaces/IObject.cs | 18 +++++++- .../Scripting/Minimodule/SOPObject.cs | 44 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs index 29f7f68419..e189489d75 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/Interfaces/IObject.cs @@ -189,9 +189,25 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule /// /// The message to send to the user void Say(string msg); - + + /// + /// Causes the object to speak to on a specific channel, + /// equivilent to LSL/OSSL llSay + /// + /// The message to send to the user + /// The channel on which to send the message void Say(string msg,int channel); + /// + /// Opens a Dialog Panel in the Users Viewer, + /// equivilent to LSL/OSSL llDialog + /// + /// The UUID of the Avatar to which the Dialog should be send + /// The Message to display at the top of the Dialog + /// The Strings that act as label/value of the Bottons in the Dialog + /// The channel on which to send the response + void Dialog(UUID avatar, string message, string[] buttons, int chat_channel); + //// /// Grants access to the objects inventory /// diff --git a/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs b/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs index f51498cc3a..96cccb77d0 100644 --- a/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs +++ b/OpenSim/Region/OptionalModules/Scripting/Minimodule/SOPObject.cs @@ -31,6 +31,7 @@ using System.Security; using OpenMetaverse; using OpenMetaverse.Packets; using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.OptionalModules.Scripting.Minimodule.Object; using OpenSim.Region.Physics.Manager; @@ -402,7 +403,48 @@ namespace OpenSim.Region.OptionalModules.Scripting.Minimodule SceneObjectPart sop = GetSOP(); m_rootScene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Say,channel, sop.AbsolutePosition, sop.Name, sop.UUID, false); } - + + public void Dialog(UUID avatar, string message, string[] buttons, int chat_channel) + { + if (!CanEdit()) + return; + + IDialogModule dm = m_rootScene.RequestModuleInterface(); + + if (dm == null) + return; + + if (buttons.Length < 1) + { + Say("ERROR: No less than 1 button can be shown",2147483647); + return; + } + if (buttons.Length > 12) + { + Say("ERROR: No more than 12 buttons can be shown",2147483647); + return; + } + + foreach(string button in buttons) + { + if (button == String.Empty) + { + Say("ERROR: button label cannot be blank",2147483647); + return; + } + if (button.Length > 24) + { + Say("ERROR: button label cannot be longer than 24 characters",2147483647); + return; + } + } + + dm.SendDialogToUser( + avatar, GetSOP().Name, GetSOP().UUID, GetSOP().OwnerID, + message, new UUID("00000000-0000-2222-3333-100000001000"), chat_channel, buttons); + + } + #endregion From f84f0dbd519b75aebc80fea73a8c4fa09592d4a6 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 23 May 2010 06:12:40 +0100 Subject: [PATCH 198/260] Remove an unneeded conditional --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index ad7c3aecd2..3964b0bf61 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -1671,8 +1671,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void StandUp() { - if (SitGround) - SitGround = false; + SitGround = false; if (m_parentID != 0) { From 91ad1f4ee71dc8e945e9be7b85f7d4e0ddcf4156 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Mon, 17 May 2010 12:37:49 +0300 Subject: [PATCH 199/260] Added generic base classes for testing database services These are some generic classes that simplify writing tests for any of the data connectors and databases. Among other things, configuring the connection strings is done once, in a separate resource file. Tests based on the new BasicDataServiceTest class require NUnit 2.5 or better. --- OpenSim/Data/Tests/BasicDataServiceTest.cs | 171 ++++++++++++++++++ OpenSim/Data/Tests/DefaultTestConns.cs | 63 +++++++ .../Tests/Resources/TestDataConnections.ini | 7 + 3 files changed, 241 insertions(+) create mode 100644 OpenSim/Data/Tests/BasicDataServiceTest.cs create mode 100644 OpenSim/Data/Tests/DefaultTestConns.cs create mode 100644 OpenSim/Data/Tests/Resources/TestDataConnections.ini diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs new file mode 100644 index 0000000000..82f29d6a02 --- /dev/null +++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs @@ -0,0 +1,171 @@ +using System; +using System.IO; +using System.Collections.Generic; +using log4net.Config; +using NUnit.Framework; +using NUnit.Framework.Constraints; +using OpenMetaverse; +using OpenSim.Framework; +using log4net; +using System.Data; +using System.Data.Common; +using System.Reflection; + +namespace OpenSim.Data.Tests +{ + /// This is a base class for testing any Data service for any DBMS. + /// Requires NUnit 2.5 or better (to support the generics). + /// + /// + /// + public class BasicDataServiceTest + where TConn : DbConnection, new() + where TService : class, new() + { + protected string m_connStr; + private TService m_service; + private string m_file; + + // TODO: Is this in the right place here? + protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public BasicDataServiceTest() + : this("") + { + } + + public BasicDataServiceTest(string conn) + { + m_connStr = !String.IsNullOrEmpty(conn) ? conn : DefaultTestConns.Get(typeof(TConn)); + + OpenSim.Tests.Common.TestLogging.LogToConsole(); // TODO: Is that right? + } + + /// + /// To be overridden in derived classes. Do whatever init with the m_service, like setting the conn string to it. + /// You'd probably want to to cast the 'service' to a more specific type and store it in a member var. + /// This framework takes care of disposing it, if it's disposable. + /// + /// The service being tested + protected virtual void InitService(object service) + { + } + + [TestFixtureSetUp] + public void Init() + { + // Sorry, some SQLite-specific stuff goes here (not a big deal, as its just some file ops) + if (typeof(TConn).Name.StartsWith("Sqlite")) + { + // SQLite doesn't work on power or z linux + if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) + Assert.Ignore(); + + // for SQLite, if no explicit conn string is specified, use a temp file + if (String.IsNullOrEmpty(m_connStr)) + { + m_file = Path.GetTempFileName() + ".db"; + m_connStr = "URI=file:" + m_file + ",version=3"; + } + } + + if (String.IsNullOrEmpty(m_connStr)) + { + string msg = String.Format("Connection string for {0} is not defined, ignoring tests", typeof(TConn).Name); + m_log.Error(msg); + Assert.Ignore(msg); + } + + // If we manage to connect to the database with the user + // and password above it is our test database, and run + // these tests. If anything goes wrong, ignore these + // tests. + try + { + m_service = new TService(); + InitService(m_service); + } + catch (Exception e) + { + m_log.Error(e.ToString()); + Assert.Ignore(); + } + } + + [TestFixtureTearDown] + public void Cleanup() + { + if (m_service != null) + { + if( m_service is IDisposable) + ((IDisposable)m_service).Dispose(); + m_service = null; + } + + if( !String.IsNullOrEmpty(m_file) && File.Exists(m_file) ) + File.Delete(m_file); + } + + protected virtual DbConnection Connect() + { + DbConnection cnn = new TConn(); + cnn.ConnectionString = m_connStr; + cnn.Open(); + return cnn; + } + + protected virtual void ExecuteSql(string sql) + { + using (DbConnection dbcon = Connect()) + { + using (DbCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + } + } + + protected delegate bool ProcessRow(IDataReader reader); + + protected virtual int ExecQuery(string sql, bool bSingleRow, ProcessRow action) + { + int nRecs = 0; + using (DbConnection dbcon = Connect()) + { + using (DbCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = sql; + CommandBehavior cb = bSingleRow ? CommandBehavior.SingleRow : CommandBehavior.Default; + using (DbDataReader rdr = cmd.ExecuteReader(cb)) + { + while (rdr.Read()) + { + nRecs++; + if (!action(rdr)) + break; + } + } + } + } + return nRecs; + } + + /// Drop tables (listed as parameters). There is no "DROP IF EXISTS" syntax common for all + /// databases, so we just DROP and ignore an exception. + /// + /// + protected virtual void DropTables(params string[] tables) + { + foreach (string tbl in tables) + { + try + { + ExecuteSql("DROP TABLE " + tbl + ";"); + }catch + { + } + } + } + } +} diff --git a/OpenSim/Data/Tests/DefaultTestConns.cs b/OpenSim/Data/Tests/DefaultTestConns.cs new file mode 100644 index 0000000000..7b52af575a --- /dev/null +++ b/OpenSim/Data/Tests/DefaultTestConns.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.IO; +using Nini.Config; + +namespace OpenSim.Data.Tests +{ + /// This static class looks for TestDataConnections.ini file in the /bin directory to obtain + /// a connection string for testing one of the supported databases. + /// The connections must be in the section [TestConnections] with names matching the connection class + /// name for the specific database, e.g.: + /// + /// [TestConnections] + /// MySqlConnection="..." + /// SqlConnection="..." + /// SqliteConnection="..." + /// + /// Note that the conn string may also be set explicitly in the [TestCase()] attribute of test classes + /// based on BasicDataServiceTest.cs. + /// + + static class DefaultTestConns + { + private static Dictionary conns = new Dictionary(); + + public static string Get(Type connType) + { + string sConn; + + if (conns.TryGetValue(connType, out sConn)) + return sConn; + + Assembly asm = Assembly.GetExecutingAssembly(); + string sType = connType.Name; + + // Note: when running from NUnit, the DLL is located in some temp dir, so how do we get + // to the INI file? Ok, so put it into the resources! + // string iniName = Path.Combine(Path.GetDirectoryName(asm.Location), "TestDataConnections.ini"); + + string[] allres = asm.GetManifestResourceNames(); + string sResFile = Array.Find(allres, s => s.Contains("TestDataConnections.ini")); + + if (String.IsNullOrEmpty(sResFile)) + throw new Exception(String.Format("Please add resource TestDataConnections.ini, with section [TestConnections] and settings like {0}=\"...\"", + sType)); + + using (Stream resource = asm.GetManifestResourceStream(sResFile)) + { + IConfigSource source = new IniConfigSource(resource); + var cfg = source.Configs["TestConnections"]; + sConn = cfg.Get(sType, ""); + } + + if (!String.IsNullOrEmpty(sConn)) + conns[connType] = sConn; + + return sConn; + } + } +} diff --git a/OpenSim/Data/Tests/Resources/TestDataConnections.ini b/OpenSim/Data/Tests/Resources/TestDataConnections.ini new file mode 100644 index 0000000000..d149744fd6 --- /dev/null +++ b/OpenSim/Data/Tests/Resources/TestDataConnections.ini @@ -0,0 +1,7 @@ +; The default connections to the test databases. Used by tests based on BasicDataServiceTest.cs. +; Read by code in DefaultTestConns.cs + +[TestConnections] +MySqlConnection="Server=localhost;Port=3306;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;" +;SqlConnection="..." +;SqliteConnection="..." \ No newline at end of file From 7f70ae0ebd686507bc15ac6fc7eeb75ed0b9b64a Mon Sep 17 00:00:00 2001 From: AlexRa Date: Mon, 17 May 2010 15:54:43 +0300 Subject: [PATCH 200/260] All data tests made DBMS-independent --- OpenSim/Data/MSSQL/MSSQLEstateData.cs | 2 +- OpenSim/Data/MySQL/Tests/MySQLEstateTest.cs | 116 ------------------ .../Data/MySQL/Tests/MySQLInventoryTest.cs | 99 --------------- OpenSim/Data/MySQL/Tests/MySQLRegionTest.cs | 112 ----------------- OpenSim/Data/SQLite/Tests/SQLiteEstateTest.cs | 65 ---------- .../Data/SQLite/Tests/SQLiteInventoryTest.cs | 66 ---------- OpenSim/Data/SQLite/Tests/SQLiteRegionTest.cs | 64 ---------- .../{BasicEstateTest.cs => EstateTests.cs} | 51 +++++++- ...asicInventoryTest.cs => InventoryTests.cs} | 92 +++++++------- .../{BasicRegionTest.cs => RegionTests.cs} | 99 +++++++++------ 10 files changed, 158 insertions(+), 608 deletions(-) delete mode 100644 OpenSim/Data/MySQL/Tests/MySQLEstateTest.cs delete mode 100644 OpenSim/Data/MySQL/Tests/MySQLInventoryTest.cs delete mode 100644 OpenSim/Data/MySQL/Tests/MySQLRegionTest.cs delete mode 100644 OpenSim/Data/SQLite/Tests/SQLiteEstateTest.cs delete mode 100644 OpenSim/Data/SQLite/Tests/SQLiteInventoryTest.cs delete mode 100644 OpenSim/Data/SQLite/Tests/SQLiteRegionTest.cs rename OpenSim/Data/Tests/{BasicEstateTest.cs => EstateTests.cs} (92%) rename OpenSim/Data/Tests/{BasicInventoryTest.cs => InventoryTests.cs} (88%) rename OpenSim/Data/Tests/{BasicRegionTest.cs => RegionTests.cs} (96%) diff --git a/OpenSim/Data/MSSQL/MSSQLEstateData.cs b/OpenSim/Data/MSSQL/MSSQLEstateData.cs index 66931e2423..80bf10638d 100644 --- a/OpenSim/Data/MSSQL/MSSQLEstateData.cs +++ b/OpenSim/Data/MSSQL/MSSQLEstateData.cs @@ -37,7 +37,7 @@ using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Data.MSSQL { - public class MSSQLEstateData : IEstateDataStore + public class MSSQLEstateStore : IEstateDataStore { private const string _migrationStore = "EstateStore"; diff --git a/OpenSim/Data/MySQL/Tests/MySQLEstateTest.cs b/OpenSim/Data/MySQL/Tests/MySQLEstateTest.cs deleted file mode 100644 index 01afcae34d..0000000000 --- a/OpenSim/Data/MySQL/Tests/MySQLEstateTest.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 OpenSimulator 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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; -using MySql.Data.MySqlClient; - - -namespace OpenSim.Data.MySQL.Tests -{ - [TestFixture, DatabaseTest] - public class MySQLEstateTest : BasicEstateTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public string connect = "Server=localhost;Port=3306;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;Pooling=false;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - // clear db incase to ensure we are in a clean state - ClearDB(); - - regionDb = new MySQLDataStore(); - regionDb.Initialise(connect); - db = new MySQLEstateStore(); - db.Initialise(connect); - } - catch (Exception e) - { - m_log.Error("Exception {0}", e); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (regionDb != null) - { - regionDb.Dispose(); - } - ClearDB(); - } - - private void ClearDB() - { - // if a new table is added, it has to be dropped here - ExecuteSql("drop table if exists migrations"); - ExecuteSql("drop table if exists prims"); - ExecuteSql("drop table if exists primshapes"); - ExecuteSql("drop table if exists primitems"); - ExecuteSql("drop table if exists terrain"); - ExecuteSql("drop table if exists land"); - ExecuteSql("drop table if exists landaccesslist"); - ExecuteSql("drop table if exists regionban"); - ExecuteSql("drop table if exists regionsettings"); - ExecuteSql("drop table if exists estate_managers"); - ExecuteSql("drop table if exists estate_groups"); - ExecuteSql("drop table if exists estate_users"); - ExecuteSql("drop table if exists estateban"); - ExecuteSql("drop table if exists estate_settings"); - ExecuteSql("drop table if exists estate_map"); - } - - /// - /// Execute a MySqlCommand - /// - /// sql string to execute - private void ExecuteSql(string sql) - { - using (MySqlConnection dbcon = new MySqlConnection(connect)) - { - dbcon.Open(); - - MySqlCommand cmd = new MySqlCommand(sql, dbcon); - cmd.ExecuteNonQuery(); - } - } - } -} diff --git a/OpenSim/Data/MySQL/Tests/MySQLInventoryTest.cs b/OpenSim/Data/MySQL/Tests/MySQLInventoryTest.cs deleted file mode 100644 index 4575493bd7..0000000000 --- a/OpenSim/Data/MySQL/Tests/MySQLInventoryTest.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 OpenSimulator 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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; -using MySql.Data.MySqlClient; - - -namespace OpenSim.Data.MySQL.Tests -{ - [TestFixture, DatabaseTest] - public class MySQLInventoryTest : BasicInventoryTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public string connect = "Server=localhost;Port=3306;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;Pooling=false;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - DropTables(); - db = new MySQLInventoryData(); - db.Initialise(connect); - } - catch (Exception e) - { - m_log.Error("Exception {0}", e); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - DropTables(); - } - - private void DropTables() - { - ExecuteSql("drop table IF EXISTS inventoryitems"); - ExecuteSql("drop table IF EXISTS inventoryfolders"); - ExecuteSql("drop table IF EXISTS migrations"); - } - - /// - /// Execute a MySqlCommand - /// - /// sql string to execute - private void ExecuteSql(string sql) - { - using (MySqlConnection dbcon = new MySqlConnection(connect)) - { - dbcon.Open(); - - MySqlCommand cmd = new MySqlCommand(sql, dbcon); - cmd.ExecuteNonQuery(); - } - } - } -} diff --git a/OpenSim/Data/MySQL/Tests/MySQLRegionTest.cs b/OpenSim/Data/MySQL/Tests/MySQLRegionTest.cs deleted file mode 100644 index e7e57e477e..0000000000 --- a/OpenSim/Data/MySQL/Tests/MySQLRegionTest.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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 OpenSimulator 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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; -using MySql.Data.MySqlClient; - -namespace OpenSim.Data.MySQL.Tests -{ - [TestFixture, DatabaseTest] - public class MySQLRegionTest : BasicRegionTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - public string connect = "Server=localhost;Port=3306;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;Pooling=false;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - // this is important in case a previous run ended badly - ClearDB(); - - db = new MySQLDataStore(); - db.Initialise(connect); - } - catch (Exception e) - { - m_log.Error("Exception {0}", e); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - ClearDB(); - } - - private void ClearDB() - { - ExecuteSql("drop table if exists migrations"); - ExecuteSql("drop table if exists prims"); - ExecuteSql("drop table if exists primshapes"); - ExecuteSql("drop table if exists primitems"); - ExecuteSql("drop table if exists terrain"); - ExecuteSql("drop table if exists land"); - ExecuteSql("drop table if exists landaccesslist"); - ExecuteSql("drop table if exists regionban"); - ExecuteSql("drop table if exists regionsettings"); - ExecuteSql("drop table if exists estate_managers"); - ExecuteSql("drop table if exists estate_groups"); - ExecuteSql("drop table if exists estate_users"); - ExecuteSql("drop table if exists estateban"); - ExecuteSql("drop table if exists estate_settings"); - ExecuteSql("drop table if exists estate_map"); - } - - /// - /// Execute a MySqlCommand - /// - /// sql string to execute - private void ExecuteSql(string sql) - { - using (MySqlConnection dbcon = new MySqlConnection(connect)) - { - dbcon.Open(); - - MySqlCommand cmd = new MySqlCommand(sql, dbcon); - cmd.ExecuteNonQuery(); - } - } - } -} diff --git a/OpenSim/Data/SQLite/Tests/SQLiteEstateTest.cs b/OpenSim/Data/SQLite/Tests/SQLiteEstateTest.cs deleted file mode 100644 index 30f66414a9..0000000000 --- a/OpenSim/Data/SQLite/Tests/SQLiteEstateTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteEstateTest : BasicEstateTest - { - public string file = "regiontest.db"; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - db = new SQLiteEstateStore(); - db.Initialise(connect); - regionDb = new SQLiteRegionData(); - regionDb.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - regionDb.Dispose(); - } - } -} diff --git a/OpenSim/Data/SQLite/Tests/SQLiteInventoryTest.cs b/OpenSim/Data/SQLite/Tests/SQLiteInventoryTest.cs deleted file mode 100644 index 98458a30ec..0000000000 --- a/OpenSim/Data/SQLite/Tests/SQLiteInventoryTest.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteInventoryTest : BasicInventoryTest - { - public string file; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - - db = new SQLiteInventoryStore(); - db.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - db.Dispose(); - File.Delete(file); - } - } -} diff --git a/OpenSim/Data/SQLite/Tests/SQLiteRegionTest.cs b/OpenSim/Data/SQLite/Tests/SQLiteRegionTest.cs deleted file mode 100644 index abb97cfa4f..0000000000 --- a/OpenSim/Data/SQLite/Tests/SQLiteRegionTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteRegionTest : BasicRegionTest - { - public string file = "regiontest.db"; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - db = new SQLiteRegionData(); - db.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - db.Dispose(); - File.Delete(file); - } - } -} diff --git a/OpenSim/Data/Tests/BasicEstateTest.cs b/OpenSim/Data/Tests/EstateTests.cs similarity index 92% rename from OpenSim/Data/Tests/BasicEstateTest.cs rename to OpenSim/Data/Tests/EstateTests.cs index d14d405256..7f13925bcb 100644 --- a/OpenSim/Data/Tests/BasicEstateTest.cs +++ b/OpenSim/Data/Tests/EstateTests.cs @@ -35,13 +35,31 @@ using OpenSim.Region.Framework.Interfaces; using System.Text; using log4net; using System.Reflection; +using System.Data.Common; + + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using System.Data.SqlClient; +using OpenSim.Data.MSSQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; + namespace OpenSim.Data.Tests { - public class BasicEstateTest + [TestFixture(typeof(MySqlConnection), typeof(MySQLEstateStore), Description = "Estate store tests (MySQL)")] + [TestFixture(typeof(SqlConnection), typeof(MSSQLEstateStore), Description = "Estate store tests (MS SQL Server)")] + [TestFixture(typeof(SqliteConnection), typeof(SQLiteEstateStore), Description = "Estate store tests (SQLite)")] + + public class EstateTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TEstateStore : class, IEstateDataStore, new() { public IEstateDataStore db; - public IRegionDataStore regionDb; public static UUID REGION_ID = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed7"); @@ -54,9 +72,34 @@ namespace OpenSim.Data.Tests public static UUID GROUP_ID_1 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed5"); public static UUID GROUP_ID_2 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed6"); - public void SuperInit() + protected override void InitService(object service) { - OpenSim.Tests.Common.TestLogging.LogToConsole(); + db = (IEstateDataStore)service; + db.Initialise(m_connStr); + ClearDB(); + } + + private void ClearDB() + { + // if a new table is added, it has to be dropped here + ExecuteSql("delete from migrations where name='EstateStore';"); + + DropTables( + "prims", + "primshapes", + "primitems", + "terrain", + "land", + "landaccesslist", + "regionban", + "regionsettings", + "estate_managers", + "estate_groups", + "estate_users", + "estateban", + "estate_settings", + "estate_map" + ); } #region 0Tests diff --git a/OpenSim/Data/Tests/BasicInventoryTest.cs b/OpenSim/Data/Tests/InventoryTests.cs similarity index 88% rename from OpenSim/Data/Tests/BasicInventoryTest.cs rename to OpenSim/Data/Tests/InventoryTests.cs index 900186b3b4..876dcf93ce 100644 --- a/OpenSim/Data/Tests/BasicInventoryTest.cs +++ b/OpenSim/Data/Tests/InventoryTests.cs @@ -33,62 +33,70 @@ using OpenMetaverse; using OpenSim.Framework; using log4net; using System.Reflection; +using System.Data.Common; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using System.Data.SqlClient; +using OpenSim.Data.MSSQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; namespace OpenSim.Data.Tests { - public class BasicInventoryTest + [TestFixture(typeof(MySqlConnection), typeof(MySQLInventoryData), Description = "Inventory store tests (MySQL)")] + [TestFixture(typeof(SqlConnection), typeof(MSSQLInventoryData), Description = "Inventory store tests (MS SQL Server)")] + [TestFixture(typeof(SqliteConnection), typeof(SQLiteInventoryStore), Description = "Inventory store tests (SQLite)")] + + public class InventoryTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TInvStore : class, IInventoryDataPlugin, new() { - //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public IInventoryDataPlugin db; + public UUID zero = UUID.Zero; - public UUID folder1; - public UUID folder2; - public UUID folder3; - public UUID owner1; - public UUID owner2; - public UUID owner3; + public UUID folder1 = UUID.Random(); + public UUID folder2 = UUID.Random(); + public UUID folder3 = UUID.Random(); + public UUID owner1 = UUID.Random(); + public UUID owner2 = UUID.Random(); + public UUID owner3 = UUID.Random(); - public UUID item1; - public UUID item2; - public UUID item3; - public UUID asset1; - public UUID asset2; - public UUID asset3; + public UUID item1 = UUID.Random(); + public UUID item2 = UUID.Random(); + public UUID item3 = UUID.Random(); + public UUID asset1 = UUID.Random(); + public UUID asset2 = UUID.Random(); + public UUID asset3 = UUID.Random(); public string name1; - public string name2; - public string name3; - public string niname1; - public string iname1; - public string iname2; - public string iname3; + public string name2 = "First Level folder"; + public string name3 = "First Level folder 2"; + public string niname1 = "My Shirt"; + public string iname1 = "Shirt"; + public string iname2 = "Text Board"; + public string iname3 = "No Pants Barrel"; - public void SuperInit() + public InventoryTests(string conn) : base(conn) { - OpenSim.Tests.Common.TestLogging.LogToConsole(); - - folder1 = UUID.Random(); - folder2 = UUID.Random(); - folder3 = UUID.Random(); - owner1 = UUID.Random(); - owner2 = UUID.Random(); - owner3 = UUID.Random(); - item1 = UUID.Random(); - item2 = UUID.Random(); - item3 = UUID.Random(); - asset1 = UUID.Random(); - asset2 = UUID.Random(); - asset3 = UUID.Random(); - name1 = "Root Folder for " + owner1.ToString(); - name2 = "First Level folder"; - name3 = "First Level folder 2"; - niname1 = "My Shirt"; - iname1 = "Shirt"; - iname2 = "Text Board"; - iname3 = "No Pants Barrel"; + } + protected override void InitService(object service) + { + db = (IInventoryDataPlugin)service; + db.Initialise(m_connStr); + ClearDB(); + } + + private void ClearDB() + { + DropTables("inventoryitems", "inventoryfolders"); + ExecuteSql("delete from migrations where name='Inventory'"); } [Test] diff --git a/OpenSim/Data/Tests/BasicRegionTest.cs b/OpenSim/Data/Tests/RegionTests.cs similarity index 96% rename from OpenSim/Data/Tests/BasicRegionTest.cs rename to OpenSim/Data/Tests/RegionTests.cs index dfbf5228ec..2a97368964 100644 --- a/OpenSim/Data/Tests/BasicRegionTest.cs +++ b/OpenSim/Data/Tests/RegionTests.cs @@ -38,59 +38,80 @@ using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using log4net; using System.Reflection; +using System.Data.Common; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using System.Data.SqlClient; +using OpenSim.Data.MSSQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; namespace OpenSim.Data.Tests { - public class BasicRegionTest + [TestFixture(typeof(MySqlConnection), typeof(MySqlRegionData), Description = "Region store tests (MySQL)")] + [TestFixture(typeof(SqlConnection), typeof(MSSQLRegionData), Description = "Region store tests (MS SQL Server)")] + [TestFixture(typeof(SqliteConnection), typeof(SQLiteRegionData), Description = "Region store tests (SQLite)")] + + public class RegionTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TRegStore : class, IRegionDataStore, new() { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public IRegionDataStore db; public UUID zero = UUID.Zero; - public UUID region1; - public UUID region2; - public UUID region3; - public UUID region4; - public UUID prim1; - public UUID prim2; - public UUID prim3; - public UUID prim4; - public UUID prim5; - public UUID prim6; - public UUID item1; - public UUID item2; - public UUID item3; + public UUID region1 = UUID.Random(); + public UUID region2 = UUID.Random(); + public UUID region3 = UUID.Random(); + public UUID region4 = UUID.Random(); + public UUID prim1 = UUID.Random(); + public UUID prim2 = UUID.Random(); + public UUID prim3 = UUID.Random(); + public UUID prim4 = UUID.Random(); + public UUID prim5 = UUID.Random(); + public UUID prim6 = UUID.Random(); + public UUID item1 = UUID.Random(); + public UUID item2 = UUID.Random(); + public UUID item3 = UUID.Random(); - public static Random random; + public static Random random = new Random(); public string itemname1 = "item1"; - public uint localID; - - public double height1; - public double height2; + public uint localID = 1; - public void SuperInit() + public double height1 = 20; + public double height2 = 100; + + + protected override void InitService(object service) { - OpenSim.Tests.Common.TestLogging.LogToConsole(); - - region1 = UUID.Random(); - region3 = UUID.Random(); - region4 = UUID.Random(); - prim1 = UUID.Random(); - prim2 = UUID.Random(); - prim3 = UUID.Random(); - prim4 = UUID.Random(); - prim5 = UUID.Random(); - prim6 = UUID.Random(); - item1 = UUID.Random(); - item2 = UUID.Random(); - item3 = UUID.Random(); - random = new Random(); - localID = 1; - height1 = 20; - height2 = 100; + db = (IRegionDataStore)service; + db.Initialise(m_connStr); + ClearDB(); } + + private void ClearDB() + { + // if a new table is added, it has to be dropped here + ExecuteSql("delete from migrations where name='RegionStore';"); + + DropTables( + "prims", + "primshapes", + "primitems", + "terrain", + "land", + "landaccesslist", + "regionban", + "regionsettings" + ); + } + + // Test Plan // Prims // - empty test - 001 From f7bf3facff458325a59483d00d3743eba32679f1 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 29 Apr 2010 12:26:09 +0300 Subject: [PATCH 201/260] MSSQLAssetData: fixed some weirdness Fixed unfinished SQL in FetchAssetMetadataSet, fixed SQL in UpdateAsset (must not modify ID). NOT tested! But apparently shouldn't work worse than the previous version, esp. the FetchMetadata thing. --- OpenSim/Data/MSSQL/MSSQLAssetData.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/OpenSim/Data/MSSQL/MSSQLAssetData.cs b/OpenSim/Data/MSSQL/MSSQLAssetData.cs index 8475b22022..5d1e1708d0 100644 --- a/OpenSim/Data/MSSQL/MSSQLAssetData.cs +++ b/OpenSim/Data/MSSQL/MSSQLAssetData.cs @@ -210,7 +210,7 @@ namespace OpenSim.Data.MSSQL /// the asset private void UpdateAsset(AssetBase asset) { - string sql = @"UPDATE assets set id = @id, name = @name, description = @description, assetType = @assetType, + string sql = @"UPDATE assets set name = @name, description = @description, assetType = @assetType, local = @local, temporary = @temporary, data = @data WHERE id = @keyId;"; @@ -231,14 +231,13 @@ namespace OpenSim.Data.MSSQL using (SqlConnection conn = new SqlConnection(m_connectionString)) using (SqlCommand command = new SqlCommand(sql, conn)) { - command.Parameters.Add(m_database.CreateParameter("id", asset.FullID)); + command.Parameters.Add(m_database.CreateParameter("keyId", asset.FullID)); command.Parameters.Add(m_database.CreateParameter("name", assetName)); command.Parameters.Add(m_database.CreateParameter("description", assetDescription)); command.Parameters.Add(m_database.CreateParameter("assetType", asset.Type)); command.Parameters.Add(m_database.CreateParameter("local", asset.Local)); command.Parameters.Add(m_database.CreateParameter("temporary", asset.Temporary)); command.Parameters.Add(m_database.CreateParameter("data", asset.Data)); - command.Parameters.Add(m_database.CreateParameter("@keyId", asset.FullID)); conn.Open(); try { @@ -295,15 +294,21 @@ namespace OpenSim.Data.MSSQL public override List FetchAssetMetadataSet(int start, int count) { List retList = new List(count); - string sql = @"SELECT (name,description,assetType,temporary,id), Row = ROW_NUMBER() - OVER (ORDER BY (some column to order by)) - WHERE Row >= @Start AND Row < @Start + @Count"; + string sql = @"WITH OrderedAssets AS + ( + SELECT id, name, description, assetType, temporary, + Row = ROW_NUMBER() OVER (ORDER BY id) + FROM assets + ) + SELECT * + FROM OrderedAssets + WHERE RowNumber BETWEEN @start AND @stop;"; using (SqlConnection conn = new SqlConnection(m_connectionString)) using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add(m_database.CreateParameter("start", start)); - cmd.Parameters.Add(m_database.CreateParameter("count", count)); + cmd.Parameters.Add(m_database.CreateParameter("stop", start + count - 1)); conn.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { From 49f4cc424624c314066d154de54441344b99916c Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 21:38:29 +0300 Subject: [PATCH 202/260] MSSQLAssetData updated to support [CreatorID], [asset_flags] --- OpenSim/Data/MSSQL/MSSQLAssetData.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/OpenSim/Data/MSSQL/MSSQLAssetData.cs b/OpenSim/Data/MSSQL/MSSQLAssetData.cs index 5d1e1708d0..ec9d4f6f2f 100644 --- a/OpenSim/Data/MSSQL/MSSQLAssetData.cs +++ b/OpenSim/Data/MSSQL/MSSQLAssetData.cs @@ -121,15 +121,16 @@ namespace OpenSim.Data.MSSQL if (reader.Read()) { AssetBase asset = new AssetBase( - new UUID((Guid)reader["id"]), + DBGuid.FromDB(reader["id"]), (string)reader["name"], Convert.ToSByte(reader["assetType"]), - String.Empty + reader["creatorid"].ToString() ); // Region Main asset.Description = (string)reader["description"]; asset.Local = Convert.ToBoolean(reader["local"]); asset.Temporary = Convert.ToBoolean(reader["temporary"]); + asset.Flags = (AssetFlags)(Convert.ToInt32(reader["asset_flags"])); asset.Data = (byte[])reader["data"]; return asset; } @@ -160,10 +161,10 @@ namespace OpenSim.Data.MSSQL string sql = @"INSERT INTO assets ([id], [name], [description], [assetType], [local], - [temporary], [create_time], [access_time], [data]) + [temporary], [create_time], [access_time], [creatorid], [asset_flags], [data]) VALUES (@id, @name, @description, @assetType, @local, - @temporary, @create_time, @access_time, @data)"; + @temporary, @create_time, @access_time, @creatorid, @asset_flags, @data)"; string assetName = asset.Name; if (asset.Name.Length > 64) @@ -191,6 +192,8 @@ namespace OpenSim.Data.MSSQL command.Parameters.Add(m_database.CreateParameter("temporary", asset.Temporary)); command.Parameters.Add(m_database.CreateParameter("access_time", now)); command.Parameters.Add(m_database.CreateParameter("create_time", now)); + command.Parameters.Add(m_database.CreateParameter("asset_flags", (int)asset.Flags)); + command.Parameters.Add(m_database.CreateParameter("creatorid", asset.Metadata.CreatorID)); command.Parameters.Add(m_database.CreateParameter("data", asset.Data)); conn.Open(); try @@ -212,6 +215,7 @@ namespace OpenSim.Data.MSSQL { string sql = @"UPDATE assets set name = @name, description = @description, assetType = @assetType, local = @local, temporary = @temporary, data = @data + , creatorid = @creatorid WHERE id = @keyId;"; string assetName = asset.Name; @@ -238,6 +242,7 @@ namespace OpenSim.Data.MSSQL command.Parameters.Add(m_database.CreateParameter("local", asset.Local)); command.Parameters.Add(m_database.CreateParameter("temporary", asset.Temporary)); command.Parameters.Add(m_database.CreateParameter("data", asset.Data)); + command.Parameters.Add(m_database.CreateParameter("creatorid", asset.Metadata.CreatorID)); conn.Open(); try { @@ -296,7 +301,7 @@ namespace OpenSim.Data.MSSQL List retList = new List(count); string sql = @"WITH OrderedAssets AS ( - SELECT id, name, description, assetType, temporary, + SELECT id, name, description, assetType, temporary, creatorid, Row = ROW_NUMBER() OVER (ORDER BY id) FROM assets ) @@ -320,6 +325,7 @@ namespace OpenSim.Data.MSSQL metadata.Description = (string)reader["description"]; metadata.Type = Convert.ToSByte(reader["assetType"]); metadata.Temporary = Convert.ToBoolean(reader["temporary"]); + metadata.CreatorID = (string)reader["creatorid"]; } } } From 187a98615bd73a0a21b66e7137658939fe4664e2 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 19 May 2010 10:28:10 +0300 Subject: [PATCH 203/260] MSSQL: added asset_flags, CreatorID to migrations --- OpenSim/Data/MSSQL/Resources/AssetStore.migrations | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenSim/Data/MSSQL/Resources/AssetStore.migrations b/OpenSim/Data/MSSQL/Resources/AssetStore.migrations index beb82b9fc2..8664ce985b 100644 --- a/OpenSim/Data/MSSQL/Resources/AssetStore.migrations +++ b/OpenSim/Data/MSSQL/Resources/AssetStore.migrations @@ -97,4 +97,10 @@ COMMIT DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; +:VERSION 6 +ALTER TABLE assets ADD asset_flags INTEGER NOT NULL DEFAULT 0; + +:VERSION 7 + +alter table assets add creatorid varchar(36) not null default ''; From 6322a085b3141bbf91ad11f86e9e8c8d5c5b3be5 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 18 May 2010 14:30:17 +0300 Subject: [PATCH 204/260] Minor corrections in BasicDataServiceTest.cs (added more functions for cleaning up DB from the derived tests) --- OpenSim/Data/Tests/BasicDataServiceTest.cs | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs index 82f29d6a02..e91a45d8bb 100644 --- a/OpenSim/Data/Tests/BasicDataServiceTest.cs +++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs @@ -167,5 +167,46 @@ namespace OpenSim.Data.Tests } } } + + /// Clear tables listed as parameters (without dropping them). + /// + /// + protected virtual void ResetMigrations(params string[] stores) + { + string lst = ""; + foreach (string store in stores) + { + string s = "'" + store + "'"; + if (lst == "") + lst = s; + else + lst += ", " + s; + } + + try + { + ExecuteSql("DELETE FROM `migrations` where name in (" + lst + ");"); + } + catch + { + } + } + + /// Clear tables listed as parameters (without dropping them). + /// + /// + protected virtual void ClearTables(params string[] tables) + { + foreach (string tbl in tables) + { + try + { + ExecuteSql("DELETE FROM " + tbl + ";"); + } + catch + { + } + } + } } } From 94f4c20866a5ddee99c555bec347fbc68b3a99fd Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 18 May 2010 14:33:57 +0300 Subject: [PATCH 205/260] Corrections in RegionTests.cs. It now fully works! The problem was that some tests relied on prior tests to leave the DB in a particular state, but the test class cleared the DB every time. The affected tests have been merged into one to remove the dependencies. tested on all 3 Dbs, all tests green. --- OpenSim/Data/Tests/RegionTests.cs | 129 ++++++++++++++++++------------ 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/OpenSim/Data/Tests/RegionTests.cs b/OpenSim/Data/Tests/RegionTests.cs index 2a97368964..2451ef09e6 100644 --- a/OpenSim/Data/Tests/RegionTests.cs +++ b/OpenSim/Data/Tests/RegionTests.cs @@ -60,6 +60,8 @@ namespace OpenSim.Data.Tests where TConn : DbConnection, new() where TRegStore : class, IRegionDataStore, new() { + bool m_rebuildDB; + public IRegionDataStore db; public UUID zero = UUID.Zero; public UUID region1 = UUID.Random(); @@ -85,6 +87,16 @@ namespace OpenSim.Data.Tests public double height1 = 20; public double height2 = 100; + public RegionTests(string conn, bool rebuild) + : base(conn) + { + m_rebuildDB = rebuild; + } + + public RegionTests() : this("", false) { } + public RegionTests(string conn) : this(conn, false) {} + public RegionTests(bool rebuild): this("", rebuild) {} + protected override void InitService(object service) { @@ -93,22 +105,17 @@ namespace OpenSim.Data.Tests ClearDB(); } - private void ClearDB() { - // if a new table is added, it has to be dropped here - ExecuteSql("delete from migrations where name='RegionStore';"); - - DropTables( - "prims", - "primshapes", - "primitems", - "terrain", - "land", - "landaccesslist", - "regionban", - "regionsettings" - ); + string[] reg_tables = new string[] { + "prims", "primshapes", "primitems", "terrain", "land", "landaccesslist", "regionban", "regionsettings" + }; + if (m_rebuildDB) + { + DropTables(reg_tables); + ResetMigrations("RegionStore"); + }else + ClearTables(reg_tables); } @@ -601,68 +608,88 @@ namespace OpenSim.Data.Tests .IgnoreProperty(x=>x.PassCollision) .IgnoreProperty(x=>x.RootPart)); } + + + private SceneObjectGroup GetMySOG(string name) + { + SceneObjectGroup sog = FindSOG(name, region1); + if (sog == null) + { + sog = NewSOG(name, prim1, region1); + db.StoreObject(sog, region1); + } + return sog; + } + + // NOTE: it is a bad practice to rely on some of the previous tests having been run before. + // If the tests are run manually, one at a time, each starts with full class init (DB cleared). + // Even when all tests are run, NUnit 2.5+ no longer guarantee a specific test order. + // We shouldn't expect to find anything in the DB if we haven't put it there *in the same test*! + [Test] public void T020_PrimInventoryEmpty() { - SceneObjectGroup sog = FindSOG("object1", region1); + SceneObjectGroup sog = GetMySOG("object1"); TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); Assert.That(t, Is.Null); } - [Test] - public void T021_PrimInventoryStore() + // TODO: Is there any point to call StorePrimInventory on a list, rather than on the prim itself? + + private void StoreInventory(SceneObjectGroup sog) { - SceneObjectGroup sog = FindSOG("object1", region1); + List list = new List(); + // TODO: seriously??? this is the way we need to loop to get this? + foreach (UUID uuid in sog.RootPart.Inventory.GetInventoryList()) + { + list.Add(sog.GetInventoryItem(sog.RootPart.LocalId, uuid)); + } + + db.StorePrimInventory(sog.RootPart.UUID, list); + } + + + [Test] + public void T021_PrimInventoryBasic() + { + SceneObjectGroup sog = GetMySOG("object1"); InventoryItemBase i = NewItem(item1, zero, zero, itemname1, zero); Assert.That(sog.AddInventoryItem(null, sog.RootPart.LocalId, i, zero), Is.True); TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); Assert.That(t.Name, Is.EqualTo(itemname1), "Assert.That(t.Name, Is.EqualTo(itemname1))"); - // TODO: seriously??? this is the way we need to loop to get this? + StoreInventory(sog); - List list = new List(); - foreach (UUID uuid in sog.RootPart.Inventory.GetInventoryList()) - { - list.Add(sog.GetInventoryItem(sog.RootPart.LocalId, uuid)); - } - - db.StorePrimInventory(prim1, list); - } + SceneObjectGroup sog1 = FindSOG("object1", region1); + Assert.That(sog1, Is.Not.Null); - [Test] - public void T022_PrimInventoryRetrieve() - { - SceneObjectGroup sog = FindSOG("object1", region1); - TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); + TaskInventoryItem t1 = sog1.GetInventoryItem(sog1.RootPart.LocalId, item1); + Assert.That(t1, Is.Not.Null); + Assert.That(t1.Name, Is.EqualTo(itemname1), "Assert.That(t.Name, Is.EqualTo(itemname1))"); - Assert.That(t.Name, Is.EqualTo(itemname1), "Assert.That(t.Name, Is.EqualTo(itemname1))"); - } - - [Test] - public void T023_PrimInventoryUpdate() - { - SceneObjectGroup sog = FindSOG("object1", region1); - TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); - - t.Name = "My New Name"; - sog.UpdateInventoryItem(t); + // Updating inventory + t1.Name = "My New Name"; + sog1.UpdateInventoryItem(t1); - Assert.That(t.Name, Is.EqualTo("My New Name"), "Assert.That(t.Name, Is.EqualTo(\"My New Name\"))"); + StoreInventory(sog1); - } + SceneObjectGroup sog2 = FindSOG("object1", region1); + TaskInventoryItem t2 = sog2.GetInventoryItem(sog2.RootPart.LocalId, item1); + Assert.That(t2.Name, Is.EqualTo("My New Name"), "Assert.That(t.Name, Is.EqualTo(\"My New Name\"))"); + + // Removing inventory - [Test] - public void T024_PrimInventoryRemove() - { List list = new List(); db.StorePrimInventory(prim1, list); - SceneObjectGroup sog = FindSOG("object1", region1); - TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); + sog = FindSOG("object1", region1); + t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); Assert.That(t, Is.Null); + } + [Test] public void T025_PrimInventoryPersistency() @@ -706,7 +733,7 @@ namespace OpenSim.Data.Tests int creationd = random.Next(); i.CreationDate = creationd; - SceneObjectGroup sog = FindSOG("object1", region1); + SceneObjectGroup sog = GetMySOG("object1"); Assert.That(sog.AddInventoryItem(null, sog.RootPart.LocalId, i, zero), Is.True); TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, id); From f527584ed336d246a77856b06d4ed9536e2b55bb Mon Sep 17 00:00:00 2001 From: AlexRa Date: Tue, 18 May 2010 01:45:21 +0300 Subject: [PATCH 206/260] EstateData tests passing on all DBs --- OpenSim/Data/Tests/EstateTests.cs | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/OpenSim/Data/Tests/EstateTests.cs b/OpenSim/Data/Tests/EstateTests.cs index 7f13925bcb..39ecb89661 100644 --- a/OpenSim/Data/Tests/EstateTests.cs +++ b/OpenSim/Data/Tests/EstateTests.cs @@ -335,8 +335,7 @@ namespace OpenSim.Data.Tests // Letting estate store generate rows to database for us EstateSettings originalSettings = db.LoadEstateSettings(regionId, true); - SetEstateSettings( - originalSettings, + SetEstateSettings(originalSettings, estateName, parentEstateID, billableFactor, @@ -362,30 +361,6 @@ namespace OpenSim.Data.Tests estateOwner ); - originalSettings.EstateName = estateName; - originalSettings.ParentEstateID = parentEstateID; - originalSettings.BillableFactor = billableFactor; - originalSettings.PricePerMeter = pricePerMeter; - originalSettings.RedirectGridX = redirectGridX; - originalSettings.RedirectGridY = redirectGridY; - originalSettings.UseGlobalTime = useGlobalTime; - originalSettings.FixedSun = fixedSun; - originalSettings.SunPosition = sunPosition; - originalSettings.AllowVoice = allowVoice; - originalSettings.AllowDirectTeleport = allowDirectTeleport; - originalSettings.ResetHomeOnTeleport = resetHomeOnTeleport; - originalSettings.DenyAnonymous = denyAnonymous; - originalSettings.DenyIdentified = denyIdentified; - originalSettings.DenyTransacted = denyTransacted; - originalSettings.DenyMinors = denyMinors; - originalSettings.AbuseEmailToEstateOwner = abuseEmailToEstateOwner; - originalSettings.BlockDwell = blockDwell; - originalSettings.EstateSkipScripts = estateSkipScripts; - originalSettings.TaxFree = taxFree; - originalSettings.PublicAccess = publicAccess; - originalSettings.AbuseEmail = abuseEmail; - originalSettings.EstateOwner = estateOwner; - // Saving settings. db.StoreEstateSettings(originalSettings); @@ -393,8 +368,7 @@ namespace OpenSim.Data.Tests EstateSettings loadedSettings = db.LoadEstateSettings(regionId, true); // Checking that loaded values are correct. - ValidateEstateSettings( - loadedSettings, + ValidateEstateSettings(loadedSettings, estateName, parentEstateID, billableFactor, From 749cf0f6eb52ee147b05e120c6763cb8846ce32f Mon Sep 17 00:00:00 2001 From: AlexRa Date: Mon, 17 May 2010 18:40:25 +0300 Subject: [PATCH 207/260] Bugfix in tests (must clear db before migrations, not after) --- OpenSim/Data/Tests/EstateTests.cs | 2 +- OpenSim/Data/Tests/InventoryTests.cs | 2 +- OpenSim/Data/Tests/RegionTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/Tests/EstateTests.cs b/OpenSim/Data/Tests/EstateTests.cs index 39ecb89661..4893cc78f2 100644 --- a/OpenSim/Data/Tests/EstateTests.cs +++ b/OpenSim/Data/Tests/EstateTests.cs @@ -74,9 +74,9 @@ namespace OpenSim.Data.Tests protected override void InitService(object service) { + ClearDB(); db = (IEstateDataStore)service; db.Initialise(m_connStr); - ClearDB(); } private void ClearDB() diff --git a/OpenSim/Data/Tests/InventoryTests.cs b/OpenSim/Data/Tests/InventoryTests.cs index 876dcf93ce..d5f3be46b4 100644 --- a/OpenSim/Data/Tests/InventoryTests.cs +++ b/OpenSim/Data/Tests/InventoryTests.cs @@ -88,9 +88,9 @@ namespace OpenSim.Data.Tests protected override void InitService(object service) { + ClearDB(); db = (IInventoryDataPlugin)service; db.Initialise(m_connStr); - ClearDB(); } private void ClearDB() diff --git a/OpenSim/Data/Tests/RegionTests.cs b/OpenSim/Data/Tests/RegionTests.cs index 2451ef09e6..f38dc4a8e1 100644 --- a/OpenSim/Data/Tests/RegionTests.cs +++ b/OpenSim/Data/Tests/RegionTests.cs @@ -100,9 +100,9 @@ namespace OpenSim.Data.Tests protected override void InitService(object service) { + ClearDB(); db = (IRegionDataStore)service; db.Initialise(m_connStr); - ClearDB(); } private void ClearDB() From b1e6e995065371a88c40284d9d504135c83b6f11 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Thu, 6 May 2010 21:10:01 +0200 Subject: [PATCH 208/260] BasicAssetTest.cs replaced by AssetTests.cs AssetTests: The name has changed to reflect the fact it is no longer a base class, but the complete asset test for all supported databases. The test can also check storing of CreatorID, but the feature is disabled at this commit! --- OpenSim/Data/Tests/AssetTests.cs | 161 +++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 OpenSim/Data/Tests/AssetTests.cs diff --git a/OpenSim/Data/Tests/AssetTests.cs b/OpenSim/Data/Tests/AssetTests.cs new file mode 100644 index 0000000000..acd544925b --- /dev/null +++ b/OpenSim/Data/Tests/AssetTests.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 OpenSimulator 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 log4net.Config; +using NUnit.Framework; +using NUnit.Framework.Constraints; +using OpenMetaverse; +using OpenSim.Framework; +using System.Data.Common; +using log4net; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using System.Data.SqlClient; +using OpenSim.Data.MSSQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; + +namespace OpenSim.Data.Tests +{ + [TestFixture(typeof(MySqlConnection), typeof(MySQLAssetData), Description="Basic Asset store tests (MySQL)")] + [TestFixture(typeof(SqlConnection), typeof(MSSQLAssetData), Description = "Basic Asset store tests (MS SQL Server)")] + [TestFixture(typeof(SqliteConnection), typeof(SQLiteAssetData), Description = "Basic Asset store tests (SQLite)")] + + public class AssetTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TAssetData : AssetDataBase, new() + { + TAssetData m_db; + + const bool COMPARE_CREATOR = false; + + public UUID uuid1 = UUID.Random(); + public UUID uuid2 = UUID.Random(); + public UUID uuid3 = UUID.Random(); + + public UUID critter1 = COMPARE_CREATOR ? UUID.Random() : UUID.Zero; + public UUID critter2 = COMPARE_CREATOR ? UUID.Random() : UUID.Zero; + public UUID critter3 = COMPARE_CREATOR ? UUID.Random() : UUID.Zero; + + public byte[] data1 = new byte[100]; + + PropertyScrambler scrambler = new PropertyScrambler() + .DontScramble(x => x.ID) + .DontScramble(x => x.Type) + .DontScramble(x => x.FullID) + .DontScramble(x => x.Metadata.ID) + .DontScramble(x => x.Metadata.CreatorID) + .DontScramble(x => x.Metadata.ContentType) + .DontScramble(x => x.Metadata.FullID) + .DontScramble(x => x.Data); + + protected override void InitService(object service) + { + m_db = (TAssetData)service; + m_db.Initialise(m_connStr); + } + + [Test] + public void T001_LoadEmpty() + { + Assert.That(m_db.ExistsAsset(uuid1), Is.False); + Assert.That(m_db.ExistsAsset(uuid2), Is.False); + Assert.That(m_db.ExistsAsset(uuid3), Is.False); + } + + [Test] + public void T010_StoreReadVerifyAssets() + { + AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1.ToString()); + AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, critter2.ToString()); + AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, critter3.ToString()); + a1.Data = data1; + a2.Data = data1; + a3.Data = data1; + + scrambler.Scramble(a1); + scrambler.Scramble(a2); + scrambler.Scramble(a3); + + m_db.StoreAsset(a1); + m_db.StoreAsset(a2); + m_db.StoreAsset(a3); + + AssetBase a1a = m_db.GetAsset(uuid1); + Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); + + AssetBase a2a = m_db.GetAsset(uuid2); + Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); + + AssetBase a3a = m_db.GetAsset(uuid3); + Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); + + scrambler.Scramble(a1a); + scrambler.Scramble(a2a); + scrambler.Scramble(a3a); + + m_db.StoreAsset(a1a); + m_db.StoreAsset(a2a); + m_db.StoreAsset(a3a); + + AssetBase a1b = m_db.GetAsset(uuid1); + Assert.That(a1b, Constraints.PropertyCompareConstraint(a1a)); + + AssetBase a2b = m_db.GetAsset(uuid2); + Assert.That(a2b, Constraints.PropertyCompareConstraint(a2a)); + + AssetBase a3b = m_db.GetAsset(uuid3); + Assert.That(a3b, Constraints.PropertyCompareConstraint(a3a)); + + Assert.That(m_db.ExistsAsset(uuid1), Is.True); + Assert.That(m_db.ExistsAsset(uuid2), Is.True); + Assert.That(m_db.ExistsAsset(uuid3), Is.True); + + List metadatas = m_db.FetchAssetMetadataSet(0, 1000); + + Assert.That(metadatas.Count >= 3, "FetchAssetMetadataSet() should have returned at least 3 assets!"); + + // It is possible that the Asset table is filled with data, in which case we don't try to find "our" + // assets there: + if (metadatas.Count < 1000) + { + AssetMetadata metadata = metadatas.Find(x => x.FullID == uuid1); + Assert.That(metadata.Name, Is.EqualTo(a1b.Name)); + Assert.That(metadata.Description, Is.EqualTo(a1b.Description)); + Assert.That(metadata.Type, Is.EqualTo(a1b.Type)); + Assert.That(metadata.Temporary, Is.EqualTo(a1b.Temporary)); + Assert.That(metadata.FullID, Is.EqualTo(a1b.FullID)); + } + } + } +} From 40031e6d379916c507591f4c5032b22afe851020 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Fri, 21 May 2010 14:00:41 +0300 Subject: [PATCH 209/260] Removed MySql and SQLite-specific asset test files --- OpenSim/Data/MySQL/Tests/MySQLAssetTest.cs | 92 -------------------- OpenSim/Data/SQLite/Tests/SQLiteAssetTest.cs | 64 -------------- 2 files changed, 156 deletions(-) delete mode 100644 OpenSim/Data/MySQL/Tests/MySQLAssetTest.cs delete mode 100644 OpenSim/Data/SQLite/Tests/SQLiteAssetTest.cs diff --git a/OpenSim/Data/MySQL/Tests/MySQLAssetTest.cs b/OpenSim/Data/MySQL/Tests/MySQLAssetTest.cs deleted file mode 100644 index a46fdf863f..0000000000 --- a/OpenSim/Data/MySQL/Tests/MySQLAssetTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 OpenSimulator 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 NUnit.Framework; -using OpenSim.Data.Tests; -using log4net; -using System.Reflection; -using OpenSim.Tests.Common; -using MySql.Data.MySqlClient; - -namespace OpenSim.Data.MySQL.Tests -{ - [TestFixture, DatabaseTest] - public class MySQLAssetTest : BasicAssetTest - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public string file; - private string m_connectionString; - public string connect = "Server=localhost;Port=3306;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;Pooling=false;"; - - [TestFixtureSetUp] - public void Init() - { - SuperInit(); - // If we manage to connect to the database with the user - // and password above it is our test database, and run - // these tests. If anything goes wrong, ignore these - // tests. - try - { - db = new MySQLAssetData(); - db.Initialise(connect); - } - catch (Exception e) - { - m_log.Error(e.ToString()); - Assert.Ignore(); - } - } - - [TestFixtureTearDown] - public void Cleanup() - { - if (db != null) - { - db.Dispose(); - } - ExecuteSql("drop table migrations"); - ExecuteSql("drop table assets"); - } - - /// - /// Execute a MySqlCommand - /// - /// sql string to execute - private void ExecuteSql(string sql) - { - using (MySqlConnection dbcon = new MySqlConnection(connect)) - { - dbcon.Open(); - - MySqlCommand cmd = new MySqlCommand(sql, dbcon); - cmd.ExecuteNonQuery(); - } - } - } -} diff --git a/OpenSim/Data/SQLite/Tests/SQLiteAssetTest.cs b/OpenSim/Data/SQLite/Tests/SQLiteAssetTest.cs deleted file mode 100644 index 0c2f5dfefc..0000000000 --- a/OpenSim/Data/SQLite/Tests/SQLiteAssetTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using NUnit.Framework; -using OpenSim.Data.Tests; -using OpenSim.Tests.Common; - -namespace OpenSim.Data.SQLite.Tests -{ - [TestFixture, DatabaseTest] - public class SQLiteAssetTest : BasicAssetTest - { - public string file; - public string connect; - - [TestFixtureSetUp] - public void Init() - { - // SQLite doesn't work on power or z linux - if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) - { - Assert.Ignore(); - } - - SuperInit(); - file = Path.GetTempFileName() + ".db"; - connect = "URI=file:" + file + ",version=3"; - db = new SQLiteAssetData(); - db.Initialise(connect); - } - - [TestFixtureTearDown] - public void Cleanup() - { - db.Dispose(); - File.Delete(file); - } - } -} From 330ad501a5b1933ad041cd13676548701170c56d Mon Sep 17 00:00:00 2001 From: AlexRa Date: Fri, 21 May 2010 15:47:37 +0300 Subject: [PATCH 210/260] Added MS SQL test conn to INI - only as an example, modify before use!!! NOTE that this INI file is currently loaded as a embedded RESOURCE, which is weird and has a disadvantage of having to rebuild the Tests whenever the conn strings are changed. The only reason is that I couldn't figure out a reliable way to put this INI into the correct dir at runtime. If somebody can do it, that would be cool. --- .../Tests/Resources/TestDataConnections.ini | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/OpenSim/Data/Tests/Resources/TestDataConnections.ini b/OpenSim/Data/Tests/Resources/TestDataConnections.ini index d149744fd6..5e68ab0ac1 100644 --- a/OpenSim/Data/Tests/Resources/TestDataConnections.ini +++ b/OpenSim/Data/Tests/Resources/TestDataConnections.ini @@ -1,7 +1,24 @@ -; The default connections to the test databases. Used by tests based on BasicDataServiceTest.cs. -; Read by code in DefaultTestConns.cs +; The default connections to the test databases. Used by all data tests based on BasicDataServiceTest.cs. +; This is read by code in DefaultTestConns.cs. + +; NOTE that this INI file is currently loaded as a embedded RESOURCE, which is weird and has a +; disadvantage of having to rebuild the Tests whenever the conn strings are changed. +; The only reason is that I couldn't figure out a reliable way to put this INI into the correct +; dir at runtime. If somebody can do it, that would be cool. + +; I'm using a local MSDE server for testing. Obviously, you'll have to modify +; the conn string to whatever MS SQL server is available to you. + +; If any of the conn strings is commented out, emty or not valid on your system, +; the relevant tests will be ignored, rather than fail. + +; As to SQLite, if the conn string here is empty, it will work anyway using a temporary +; file for the DB. If you want the resulting DB to persist (e.g. for performance testing, +; when filling up the tables can take a long time), explicitly specify a conn string like this: + +; SqliteConnection="URI=file:,version=3" [TestConnections] MySqlConnection="Server=localhost;Port=3306;Database=opensim-nunit;User ID=opensim-nunit;Password=opensim-nunit;" -;SqlConnection="..." -;SqliteConnection="..." \ No newline at end of file +SqlConnection="Server=.\SQL2008;Database=opensim-nunit;Trusted_Connection=True;" +SqliteConnection="" \ No newline at end of file From 2537acc04db736cdc885e23bbfaade690d56fa5f Mon Sep 17 00:00:00 2001 From: AlexRa Date: Fri, 21 May 2010 15:59:26 +0300 Subject: [PATCH 211/260] Unitests: Asset, Estate, Region (the "legacy" one), Inventory The tests have been modified to work under NUnit 2.4.6 (the one currently used in the project). They will also work with NUnit 2.5+ as is, but will look better if you #define NUNIT25 for them. --- OpenSim/Data/Tests/AssetTests.cs | 62 +++++++++- OpenSim/Data/Tests/BasicAssetTest.cs | 166 --------------------------- OpenSim/Data/Tests/EstateTests.cs | 26 +++++ OpenSim/Data/Tests/InventoryTests.cs | 29 ++++- OpenSim/Data/Tests/RegionTests.cs | 27 ++++- 5 files changed, 137 insertions(+), 173 deletions(-) delete mode 100644 OpenSim/Data/Tests/BasicAssetTest.cs diff --git a/OpenSim/Data/Tests/AssetTests.cs b/OpenSim/Data/Tests/AssetTests.cs index acd544925b..d228c1fdcb 100644 --- a/OpenSim/Data/Tests/AssetTests.cs +++ b/OpenSim/Data/Tests/AssetTests.cs @@ -35,6 +35,10 @@ using OpenSim.Framework; using System.Data.Common; using log4net; +#if !NUNIT25 +using NUnit.Framework.SyntaxHelpers; +#endif + // DBMS-specific: using MySql.Data.MySqlClient; using OpenSim.Data.MySQL; @@ -47,25 +51,46 @@ using OpenSim.Data.SQLite; namespace OpenSim.Data.Tests { + +#if NUNIT25 + [TestFixture(typeof(MySqlConnection), typeof(MySQLAssetData), Description="Basic Asset store tests (MySQL)")] [TestFixture(typeof(SqlConnection), typeof(MSSQLAssetData), Description = "Basic Asset store tests (MS SQL Server)")] [TestFixture(typeof(SqliteConnection), typeof(SQLiteAssetData), Description = "Basic Asset store tests (SQLite)")] +#else + + [TestFixture(Description = "Region store tests (SQLite)")] + public class SQLiteAssetTests : AssetTests + { + } + + [TestFixture(Description = "Region store tests (MySQL)")] + public class MySqlAssetTests : AssetTests + { + } + + [TestFixture(Description = "Region store tests (MS SQL Server)")] + public class MSSQLAssetTests : AssetTests + { + } + +#endif + + public class AssetTests : BasicDataServiceTest where TConn : DbConnection, new() where TAssetData : AssetDataBase, new() { TAssetData m_db; - const bool COMPARE_CREATOR = false; - public UUID uuid1 = UUID.Random(); public UUID uuid2 = UUID.Random(); public UUID uuid3 = UUID.Random(); - public UUID critter1 = COMPARE_CREATOR ? UUID.Random() : UUID.Zero; - public UUID critter2 = COMPARE_CREATOR ? UUID.Random() : UUID.Zero; - public UUID critter3 = COMPARE_CREATOR ? UUID.Random() : UUID.Zero; + public string critter1 = UUID.Random().ToString(); + public string critter2 = UUID.Random().ToString(); + public string critter3 = UUID.Random().ToString(); public byte[] data1 = new byte[100]; @@ -157,5 +182,32 @@ namespace OpenSim.Data.Tests Assert.That(metadata.FullID, Is.EqualTo(a1b.FullID)); } } + + [Test] + public void T020_CheckForWeirdCreatorID() + { + // It is expected that eventually the CreatorID might be an arbitrary string (an URI) + // rather than a valid UUID (?). This test is to make sure that the database layer does not + // attempt to convert CreatorID to GUID, but just passes it both ways as a string. + AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1); + AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, "This is not a GUID!"); + AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, ""); + a1.Data = data1; + a2.Data = data1; + a3.Data = data1; + + m_db.StoreAsset(a1); + m_db.StoreAsset(a2); + m_db.StoreAsset(a3); + + AssetBase a1a = m_db.GetAsset(uuid1); + Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); + + AssetBase a2a = m_db.GetAsset(uuid2); + Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); + + AssetBase a3a = m_db.GetAsset(uuid3); + Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); + } } } diff --git a/OpenSim/Data/Tests/BasicAssetTest.cs b/OpenSim/Data/Tests/BasicAssetTest.cs deleted file mode 100644 index 71d6314690..0000000000 --- a/OpenSim/Data/Tests/BasicAssetTest.cs +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 OpenSimulator 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 log4net.Config; -using NUnit.Framework; -using NUnit.Framework.SyntaxHelpers; -using OpenMetaverse; -using OpenSim.Framework; -using log4net; - -namespace OpenSim.Data.Tests -{ - public class BasicAssetTest - { - public IAssetDataPlugin db; - public UUID uuid1; - public UUID uuid2; - public UUID uuid3; - public string critter1 = UUID.Random().ToString(); - public string critter2 = UUID.Random().ToString(); - public string critter3 = UUID.Random().ToString(); - public byte[] asset1; - PropertyScrambler scrambler; - - public void SuperInit() - { - OpenSim.Tests.Common.TestLogging.LogToConsole(); - - uuid1 = UUID.Random(); - uuid2 = UUID.Random(); - uuid3 = UUID.Random(); - asset1 = new byte[100]; - asset1.Initialize(); - - - scrambler = new PropertyScrambler() - .DontScramble(x => x.ID) - .DontScramble(x => x.FullID) - .DontScramble(x => x.Metadata.ID) - .DontScramble(x => x.Metadata.Type) - .DontScramble(x => x.Metadata.CreatorID) - .DontScramble(x => x.Metadata.ContentType) - .DontScramble(x => x.Metadata.FullID); - } - - [Test] - public void T001_LoadEmpty() - { - Assert.That(db.ExistsAsset(uuid1), Is.False); - Assert.That(db.ExistsAsset(uuid2), Is.False); - Assert.That(db.ExistsAsset(uuid3), Is.False); - } - - [Test] - public void T010_StoreSimpleAsset() - { - AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1); - AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, critter2); - AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, critter3); - a1.Data = asset1; - a2.Data = asset1; - a3.Data = asset1; - - scrambler.Scramble(a1); - scrambler.Scramble(a2); - scrambler.Scramble(a3); - - db.StoreAsset(a1); - db.StoreAsset(a2); - db.StoreAsset(a3); - - AssetBase a1a = db.GetAsset(uuid1); - Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); - - AssetBase a2a = db.GetAsset(uuid2); - Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); - - AssetBase a3a = db.GetAsset(uuid3); - Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); - - scrambler.Scramble(a1a); - scrambler.Scramble(a2a); - scrambler.Scramble(a3a); - - db.StoreAsset(a1a); - db.StoreAsset(a2a); - db.StoreAsset(a3a); - - AssetBase a1b = db.GetAsset(uuid1); - Assert.That(a1b, Constraints.PropertyCompareConstraint(a1a)); - - AssetBase a2b = db.GetAsset(uuid2); - Assert.That(a2b, Constraints.PropertyCompareConstraint(a2a)); - - AssetBase a3b = db.GetAsset(uuid3); - Assert.That(a3b, Constraints.PropertyCompareConstraint(a3a)); - - Assert.That(db.ExistsAsset(uuid1), Is.True); - Assert.That(db.ExistsAsset(uuid2), Is.True); - Assert.That(db.ExistsAsset(uuid3), Is.True); - - List metadatas = db.FetchAssetMetadataSet(0, 1000); - - AssetMetadata metadata = metadatas.Find(x => x.FullID == uuid1); - Assert.That(metadata.Name, Is.EqualTo(a1b.Name)); - Assert.That(metadata.Description, Is.EqualTo(a1b.Description)); - Assert.That(metadata.Type, Is.EqualTo(a1b.Type)); - Assert.That(metadata.Temporary, Is.EqualTo(a1b.Temporary)); - Assert.That(metadata.FullID, Is.EqualTo(a1b.FullID)); - } - - - [Test] - public void T020_CheckForWeirdCreatorID() - { - // It is expected that eventually the CreatorID might be an arbitrary string (an URI) - // rather than a valid UUID (?). This test is to make sure that the database layer does not - // attempt to convert CreatorID to GUID, but just passes it both ways as a string. - AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1); - AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, "This is not a GUID!"); - AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, ""); - a1.Data = asset1; - a2.Data = asset1; - a3.Data = asset1; - - db.StoreAsset(a1); - db.StoreAsset(a2); - db.StoreAsset(a3); - - AssetBase a1a = db.GetAsset(uuid1); - Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); - - AssetBase a2a = db.GetAsset(uuid2); - Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); - - AssetBase a3a = db.GetAsset(uuid3); - Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); - } - } -} diff --git a/OpenSim/Data/Tests/EstateTests.cs b/OpenSim/Data/Tests/EstateTests.cs index 4893cc78f2..2da010db3e 100644 --- a/OpenSim/Data/Tests/EstateTests.cs +++ b/OpenSim/Data/Tests/EstateTests.cs @@ -37,6 +37,10 @@ using log4net; using System.Reflection; using System.Data.Common; +#if !NUNIT25 +using NUnit.Framework.SyntaxHelpers; +#endif + // DBMS-specific: using MySql.Data.MySqlClient; @@ -51,10 +55,32 @@ using OpenSim.Data.SQLite; namespace OpenSim.Data.Tests { + +#if NUNIT25 + [TestFixture(typeof(MySqlConnection), typeof(MySQLEstateStore), Description = "Estate store tests (MySQL)")] [TestFixture(typeof(SqlConnection), typeof(MSSQLEstateStore), Description = "Estate store tests (MS SQL Server)")] [TestFixture(typeof(SqliteConnection), typeof(SQLiteEstateStore), Description = "Estate store tests (SQLite)")] +#else + + [TestFixture(Description = "Estate store tests (SQLite)")] + public class SQLiteEstateTests : EstateTests + { + } + + [TestFixture(Description = "Estate store tests (MySQL)")] + public class MySqlEstateTests : EstateTests + { + } + + [TestFixture(Description = "Estate store tests (MS SQL Server)")] + public class MSSQLEstateTests : EstateTests + { + } + +#endif + public class EstateTests : BasicDataServiceTest where TConn : DbConnection, new() where TEstateStore : class, IEstateDataStore, new() diff --git a/OpenSim/Data/Tests/InventoryTests.cs b/OpenSim/Data/Tests/InventoryTests.cs index d5f3be46b4..93e1eba55d 100644 --- a/OpenSim/Data/Tests/InventoryTests.cs +++ b/OpenSim/Data/Tests/InventoryTests.cs @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// #define NUNIT25 + using System; using log4net.Config; using NUnit.Framework; @@ -35,6 +37,10 @@ using log4net; using System.Reflection; using System.Data.Common; +#if !NUNIT25 +using NUnit.Framework.SyntaxHelpers; +#endif + // DBMS-specific: using MySql.Data.MySqlClient; using OpenSim.Data.MySQL; @@ -47,9 +53,29 @@ using OpenSim.Data.SQLite; namespace OpenSim.Data.Tests { +#if NUNIT25 + + [TestFixture(typeof(SqliteConnection), typeof(SQLiteInventoryStore), Description = "Inventory store tests (SQLite)")] [TestFixture(typeof(MySqlConnection), typeof(MySQLInventoryData), Description = "Inventory store tests (MySQL)")] [TestFixture(typeof(SqlConnection), typeof(MSSQLInventoryData), Description = "Inventory store tests (MS SQL Server)")] - [TestFixture(typeof(SqliteConnection), typeof(SQLiteInventoryStore), Description = "Inventory store tests (SQLite)")] + +#else + + [TestFixture(Description = "Inventory store tests (SQLite)")] + public class SQLiteInventoryTests : InventoryTests + { + } + + [TestFixture(Description = "Inventory store tests (MySQL)")] + public class MySqlInventoryTests : InventoryTests + { + } + + [TestFixture(Description = "Inventory store tests (MS SQL Server)")] + public class MSSQLInventoryTests : InventoryTests + { + } +#endif public class InventoryTests : BasicDataServiceTest where TConn : DbConnection, new() @@ -85,6 +111,7 @@ namespace OpenSim.Data.Tests { name1 = "Root Folder for " + owner1.ToString(); } + public InventoryTests() : this("") { } protected override void InitService(object service) { diff --git a/OpenSim/Data/Tests/RegionTests.cs b/OpenSim/Data/Tests/RegionTests.cs index f38dc4a8e1..5ac2dd0ad1 100644 --- a/OpenSim/Data/Tests/RegionTests.cs +++ b/OpenSim/Data/Tests/RegionTests.cs @@ -40,6 +40,10 @@ using log4net; using System.Reflection; using System.Data.Common; +#if !NUNIT25 +using NUnit.Framework.SyntaxHelpers; +#endif + // DBMS-specific: using MySql.Data.MySqlClient; using OpenSim.Data.MySQL; @@ -52,9 +56,30 @@ using OpenSim.Data.SQLite; namespace OpenSim.Data.Tests { +#if NUNIT25 + + [TestFixture(typeof(SqliteConnection), typeof(SQLiteRegionData), Description = "Region store tests (SQLite)")] [TestFixture(typeof(MySqlConnection), typeof(MySqlRegionData), Description = "Region store tests (MySQL)")] [TestFixture(typeof(SqlConnection), typeof(MSSQLRegionData), Description = "Region store tests (MS SQL Server)")] - [TestFixture(typeof(SqliteConnection), typeof(SQLiteRegionData), Description = "Region store tests (SQLite)")] + +#else + + [TestFixture(Description = "Region store tests (SQLite)")] + public class SQLiteRegionTests : RegionTests + { + } + + [TestFixture(Description = "Region store tests (MySQL)")] + public class MySqlRegionTests : RegionTests + { + } + + [TestFixture(Description = "Region store tests (MS SQL Server)")] + public class MSSQLRegionTests : RegionTests + { + } + +#endif public class RegionTests : BasicDataServiceTest where TConn : DbConnection, new() From 724305c37beab5594fa451461c020f342cc67833 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Fri, 21 May 2010 16:05:20 +0300 Subject: [PATCH 212/260] Prebuild: removed DB-specific test projects, added refs to Data.Tests --- prebuild.xml | 77 +++++----------------------------------------------- 1 file changed, 7 insertions(+), 70 deletions(-) diff --git a/prebuild.xml b/prebuild.xml index 870826cbaf..c0e06bffe6 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -2825,83 +2825,20 @@ - - - - + - - - - ../../../../bin/ - - - - - ../../../../bin/ - - - - ../../../../bin/ - - - - - - - - - - - - - - - - - + + + + + + - - - - ../../../../bin/ - - - - - ../../../../bin/ - - - - ../../../../bin/ - - - - - - - - - - - - - - - - - - - - - - - From ebc2b6d4f6ebb0392ec0081bea913d24e9753786 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Mon, 17 May 2010 17:37:16 +0200 Subject: [PATCH 213/260] Split migrations for RegionStore and EstateStore (see WARNING!) ok, so the estate stores now want their own migration files, but as it happened the SQL definition were inside the Region migrations. It seems better/cleaner to keep each 'store' separately updatable. WARNING: any editing in the middle of the migration scripts (as opposite to just appending to them) has the potential of messing up updates of existing databases. As far as I can see, this one is (probably) safe, the worst that could happen is the EstateStore migration silently fail if the estate the tables are already there. --- .../MySQL/Resources/EstateStore.migrations | 69 +++++++++++++++ .../MySQL/Resources/RegionStore.migrations | 78 ---------------- .../SQLite/Resources/EstateStore.migrations | 88 +++++++++++++++++++ .../SQLite/Resources/RegionStore.migrations | 25 ------ 4 files changed, 157 insertions(+), 103 deletions(-) create mode 100644 OpenSim/Data/MySQL/Resources/EstateStore.migrations create mode 100644 OpenSim/Data/SQLite/Resources/EstateStore.migrations diff --git a/OpenSim/Data/MySQL/Resources/EstateStore.migrations b/OpenSim/Data/MySQL/Resources/EstateStore.migrations new file mode 100644 index 0000000000..2e0d658ea0 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/EstateStore.migrations @@ -0,0 +1,69 @@ +:VERSION 13 + +# The estate migrations used to be in Region store + +CREATE TABLE IF NOT EXISTS `estate_managers` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estate_groups` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estate_users` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estateban` ( + `EstateID` int(10) unsigned NOT NULL, + `bannedUUID` varchar(36) NOT NULL, + `bannedIp` varchar(16) NOT NULL, + `bannedIpHostMask` varchar(16) NOT NULL, + `bannedNameMask` varchar(64) default NULL, + KEY `estateban_EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estate_settings` ( + `EstateID` int(10) unsigned NOT NULL auto_increment, + `EstateName` varchar(64) default NULL, + `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, + `DenyAnonymous` tinyint(4) NOT NULL, + `ResetHomeOnTeleport` tinyint(4) NOT NULL, + `FixedSun` tinyint(4) NOT NULL, + `DenyTransacted` tinyint(4) NOT NULL, + `BlockDwell` tinyint(4) NOT NULL, + `DenyIdentified` tinyint(4) NOT NULL, + `AllowVoice` tinyint(4) NOT NULL, + `UseGlobalTime` tinyint(4) NOT NULL, + `PricePerMeter` int(11) NOT NULL, + `TaxFree` tinyint(4) NOT NULL, + `AllowDirectTeleport` tinyint(4) NOT NULL, + `RedirectGridX` int(11) NOT NULL, + `RedirectGridY` int(11) NOT NULL, + `ParentEstateID` int(10) unsigned NOT NULL, + `SunPosition` double NOT NULL, + `EstateSkipScripts` tinyint(4) NOT NULL, + `BillableFactor` float NOT NULL, + `PublicAccess` tinyint(4) NOT NULL, + `AbuseEmail` varchar(255) not null, + `EstateOwner` varchar(36) not null, + `DenyMinors` tinyint not null, + + PRIMARY KEY (`EstateID`) +) ENGINE=InnoDB AUTO_INCREMENT=100; + +CREATE TABLE IF NOT EXISTS `estate_map` ( + `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `EstateID` int(11) NOT NULL, + PRIMARY KEY (`RegionID`), + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + + + diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations index baeeedd0c2..383c328114 100644 --- a/OpenSim/Data/MySQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -386,84 +386,6 @@ CREATE TABLE `regionsettings` ( PRIMARY KEY (`regionUUID`) ) ENGINE=InnoDB; -CREATE TABLE `estate_managers` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_groups` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_users` ( - `EstateID` int(10) unsigned NOT NULL, - `uuid` char(36) NOT NULL, - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estateban` ( - `EstateID` int(10) unsigned NOT NULL, - `bannedUUID` varchar(36) NOT NULL, - `bannedIp` varchar(16) NOT NULL, - `bannedIpHostMask` varchar(16) NOT NULL, - `bannedNameMask` varchar(64) default NULL, - KEY `estateban_EstateID` (`EstateID`) -) ENGINE=InnoDB; - -CREATE TABLE `estate_settings` ( - `EstateID` int(10) unsigned NOT NULL auto_increment, - `EstateName` varchar(64) default NULL, - `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, - `DenyAnonymous` tinyint(4) NOT NULL, - `ResetHomeOnTeleport` tinyint(4) NOT NULL, - `FixedSun` tinyint(4) NOT NULL, - `DenyTransacted` tinyint(4) NOT NULL, - `BlockDwell` tinyint(4) NOT NULL, - `DenyIdentified` tinyint(4) NOT NULL, - `AllowVoice` tinyint(4) NOT NULL, - `UseGlobalTime` tinyint(4) NOT NULL, - `PricePerMeter` int(11) NOT NULL, - `TaxFree` tinyint(4) NOT NULL, - `AllowDirectTeleport` tinyint(4) NOT NULL, - `RedirectGridX` int(11) NOT NULL, - `RedirectGridY` int(11) NOT NULL, - `ParentEstateID` int(10) unsigned NOT NULL, - `SunPosition` double NOT NULL, - `EstateSkipScripts` tinyint(4) NOT NULL, - `BillableFactor` float NOT NULL, - `PublicAccess` tinyint(4) NOT NULL, - PRIMARY KEY (`EstateID`) -) ENGINE=InnoDB AUTO_INCREMENT=100; - -CREATE TABLE `estate_map` ( - `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', - `EstateID` int(11) NOT NULL, - PRIMARY KEY (`RegionID`), - KEY `EstateID` (`EstateID`) -) ENGINE=InnoDB; - -commit; - -:VERSION 14 #--------------------- - -begin; - -alter table estate_settings add column AbuseEmail varchar(255) not null; - -alter table estate_settings add column EstateOwner varchar(36) not null; - -commit; - - -:VERSION 15 #--------------------- - -begin; - -alter table estate_settings add column DenyMinors tinyint not null; - commit; :VERSION 16 #--------------------- diff --git a/OpenSim/Data/SQLite/Resources/EstateStore.migrations b/OpenSim/Data/SQLite/Resources/EstateStore.migrations new file mode 100644 index 0000000000..62f6464460 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/EstateStore.migrations @@ -0,0 +1,88 @@ +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE estate_groups ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_managers ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_map ( + RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + EstateID int(11) NOT NULL +); + +CREATE TABLE estate_settings ( + EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + EstateName varchar(64) default NULL, + AbuseEmailToEstateOwner tinyint(4) NOT NULL, + DenyAnonymous tinyint(4) NOT NULL, + ResetHomeOnTeleport tinyint(4) NOT NULL, + FixedSun tinyint(4) NOT NULL, + DenyTransacted tinyint(4) NOT NULL, + BlockDwell tinyint(4) NOT NULL, + DenyIdentified tinyint(4) NOT NULL, + AllowVoice tinyint(4) NOT NULL, + UseGlobalTime tinyint(4) NOT NULL, + PricePerMeter int(11) NOT NULL, + TaxFree tinyint(4) NOT NULL, + AllowDirectTeleport tinyint(4) NOT NULL, + RedirectGridX int(11) NOT NULL, + RedirectGridY int(11) NOT NULL, + ParentEstateID int(10) NOT NULL, + SunPosition double NOT NULL, + EstateSkipScripts tinyint(4) NOT NULL, + BillableFactor float NOT NULL, + PublicAccess tinyint(4) NOT NULL +); + +insert into estate_settings ( + EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) + values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); +delete from estate_settings; + +CREATE TABLE estate_users ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estateban ( + EstateID int(10) NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL, + bannedNameMask varchar(64) default NULL +); + +CREATE INDEX estate_ban_estate_id on estateban(EstateID); +CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); +CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); +CREATE INDEX estate_map_estate_id on estate_map(EstateID); +CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); +CREATE INDEX estate_users_estate_id on estate_users(EstateID); + +COMMIT; + + +:VERSION 7 + +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null default ''; + +alter table estate_settings add column EstateOwner varchar(36) not null default ''; + +commit; + +:VERSION 8 + +begin; + +alter table estate_settings add column DenyMinors tinyint not null default 0; + +commit; diff --git a/OpenSim/Data/SQLite/Resources/RegionStore.migrations b/OpenSim/Data/SQLite/Resources/RegionStore.migrations index 7b27378139..c47a85d026 100644 --- a/OpenSim/Data/SQLite/Resources/RegionStore.migrations +++ b/OpenSim/Data/SQLite/Resources/RegionStore.migrations @@ -311,33 +311,8 @@ CREATE TABLE regionsettings ( PRIMARY KEY (regionUUID) ); -CREATE INDEX estate_ban_estate_id on estateban(EstateID); -CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); -CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); -CREATE INDEX estate_map_estate_id on estate_map(EstateID); -CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); -CREATE INDEX estate_users_estate_id on estate_users(EstateID); - COMMIT; -:VERSION 7 - -begin; - -alter table estate_settings add column AbuseEmail varchar(255) not null default ''; - -alter table estate_settings add column EstateOwner varchar(36) not null default ''; - -commit; - -:VERSION 8 - -begin; - -alter table estate_settings add column DenyMinors tinyint not null default 0; - -commit; - :VERSION 9 BEGIN; From 9976cb93ce351f45dea77e3389e0159b866757ae Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 23 May 2010 11:26:53 +0300 Subject: [PATCH 214/260] Further corrections to MS SQL stores (now passes all tests) Besides, AssetData is slightly optimized to StoreAsset in one request ("IF EXISTS() UPDATE ... ELSE INSERT ...") The main change in the MS SQL Inventory implem. is that it now return empty list (or whatever) when called with UUID.Zero, which is consistent with how the code for other DBs work. I did no changes at all in XInventory, as there is no test set for them. --- OpenSim/Data/MSSQL/MSSQLAssetData.cs | 87 +++++------------------- OpenSim/Data/MSSQL/MSSQLInventoryData.cs | 51 ++++++++------ 2 files changed, 49 insertions(+), 89 deletions(-) diff --git a/OpenSim/Data/MSSQL/MSSQLAssetData.cs b/OpenSim/Data/MSSQL/MSSQLAssetData.cs index ec9d4f6f2f..c7488d853c 100644 --- a/OpenSim/Data/MSSQL/MSSQLAssetData.cs +++ b/OpenSim/Data/MSSQL/MSSQLAssetData.cs @@ -145,26 +145,19 @@ namespace OpenSim.Data.MSSQL /// the asset override public void StoreAsset(AssetBase asset) { - if (ExistsAsset(asset.FullID)) - UpdateAsset(asset); - else - InsertAsset(asset); - } - - - private void InsertAsset(AssetBase asset) - { - if (ExistsAsset(asset.FullID)) - { - return; - } - - string sql = @"INSERT INTO assets - ([id], [name], [description], [assetType], [local], - [temporary], [create_time], [access_time], [creatorid], [asset_flags], [data]) - VALUES - (@id, @name, @description, @assetType, @local, - @temporary, @create_time, @access_time, @creatorid, @asset_flags, @data)"; + + string sql = + @"IF EXISTS(SELECT * FROM assets WHERE id=@id) + UPDATE assets set name = @name, description = @description, assetType = @assetType, + local = @local, temporary = @temporary, creatorid = @creatorid, data = @data + WHERE id=@id + ELSE + INSERT INTO assets + ([id], [name], [description], [assetType], [local], + [temporary], [create_time], [access_time], [creatorid], [asset_flags], [data]) + VALUES + (@id, @name, @description, @assetType, @local, + @temporary, @create_time, @access_time, @creatorid, @asset_flags, @data)"; string assetName = asset.Name; if (asset.Name.Length > 64) @@ -202,58 +195,11 @@ namespace OpenSim.Data.MSSQL } catch(Exception e) { - m_log.Error("[ASSET DB]: Error inserting item :" + e.Message); + m_log.Error("[ASSET DB]: Error storing item :" + e.Message); } } } - /// - /// Update asset in m_database - /// - /// the asset - private void UpdateAsset(AssetBase asset) - { - string sql = @"UPDATE assets set name = @name, description = @description, assetType = @assetType, - local = @local, temporary = @temporary, data = @data - , creatorid = @creatorid - WHERE id = @keyId;"; - - string assetName = asset.Name; - if (asset.Name.Length > 64) - { - assetName = asset.Name.Substring(0, 64); - m_log.Warn("[ASSET DB]: Name field truncated from " + asset.Name.Length + " to " + assetName.Length + " characters on update"); - } - - string assetDescription = asset.Description; - if (asset.Description.Length > 64) - { - assetDescription = asset.Description.Substring(0, 64); - m_log.Warn("[ASSET DB]: Description field truncated from " + asset.Description.Length + " to " + assetDescription.Length + " characters on update"); - } - - using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand command = new SqlCommand(sql, conn)) - { - command.Parameters.Add(m_database.CreateParameter("keyId", asset.FullID)); - command.Parameters.Add(m_database.CreateParameter("name", assetName)); - command.Parameters.Add(m_database.CreateParameter("description", assetDescription)); - command.Parameters.Add(m_database.CreateParameter("assetType", asset.Type)); - command.Parameters.Add(m_database.CreateParameter("local", asset.Local)); - command.Parameters.Add(m_database.CreateParameter("temporary", asset.Temporary)); - command.Parameters.Add(m_database.CreateParameter("data", asset.Data)); - command.Parameters.Add(m_database.CreateParameter("creatorid", asset.Metadata.CreatorID)); - conn.Open(); - try - { - command.ExecuteNonQuery(); - } - catch (Exception e) - { - m_log.Error(e.ToString()); - } - } - } // Commented out since currently unused - this probably should be called in GetAsset() // private void UpdateAccessTime(AssetBase asset) @@ -302,7 +248,7 @@ namespace OpenSim.Data.MSSQL string sql = @"WITH OrderedAssets AS ( SELECT id, name, description, assetType, temporary, creatorid, - Row = ROW_NUMBER() OVER (ORDER BY id) + RowNumber = ROW_NUMBER() OVER (ORDER BY id) FROM assets ) SELECT * @@ -320,12 +266,13 @@ namespace OpenSim.Data.MSSQL while (reader.Read()) { AssetMetadata metadata = new AssetMetadata(); - metadata.FullID = new UUID((Guid)reader["id"]); + metadata.FullID = DBGuid.FromDB(reader["id"]); metadata.Name = (string)reader["name"]; metadata.Description = (string)reader["description"]; metadata.Type = Convert.ToSByte(reader["assetType"]); metadata.Temporary = Convert.ToBoolean(reader["temporary"]); metadata.CreatorID = (string)reader["creatorid"]; + retList.Add(metadata); } } } diff --git a/OpenSim/Data/MSSQL/MSSQLInventoryData.cs b/OpenSim/Data/MSSQL/MSSQLInventoryData.cs index 4815700c2b..4d06377452 100644 --- a/OpenSim/Data/MSSQL/MSSQLInventoryData.cs +++ b/OpenSim/Data/MSSQL/MSSQLInventoryData.cs @@ -111,6 +111,9 @@ namespace OpenSim.Data.MSSQL /// A list of folder objects public List getUserRootFolders(UUID user) { + if (user == UUID.Zero) + return new List(); + return getInventoryFolders(UUID.Zero, user); } @@ -184,7 +187,19 @@ namespace OpenSim.Data.MSSQL //Note maybe change this to use a Dataset that loading in all folders of a user and then go throw it that way. //Note this is changed so it opens only one connection to the database and not everytime it wants to get data. + /* NOTE: the implementation below is very inefficient (makes a separate request to get subfolders for + * every found folder, recursively). Inventory code for other DBs has been already rewritten to get ALL + * inventory for a specific user at once. + * + * Meanwhile, one little thing is corrected: getFolderHierarchy(UUID.Zero) doesn't make sense and should never + * be used, so check for that and return an empty list. + */ + List folders = new List(); + + if (parentID == UUID.Zero) + return folders; + string sql = "SELECT * FROM inventoryfolders WHERE parentFolderID = @parentID"; using (SqlConnection conn = new SqlConnection(m_connectionString)) using (SqlCommand cmd = new SqlCommand(sql, conn)) @@ -249,13 +264,12 @@ namespace OpenSim.Data.MSSQL /// Folder to update public void updateInventoryFolder(InventoryFolderBase folder) { - string sql = @"UPDATE inventoryfolders SET folderID = @folderID, - agentID = @agentID, + string sql = @"UPDATE inventoryfolders SET agentID = @agentID, parentFolderID = @parentFolderID, folderName = @folderName, type = @type, version = @version - WHERE folderID = @keyFolderID"; + WHERE folderID = @folderID"; string folderName = folder.Name; if (folderName.Length > 64) @@ -272,7 +286,6 @@ namespace OpenSim.Data.MSSQL cmd.Parameters.Add(database.CreateParameter("folderName", folderName)); cmd.Parameters.Add(database.CreateParameter("type", folder.Type)); cmd.Parameters.Add(database.CreateParameter("version", folder.Version)); - cmd.Parameters.Add(database.CreateParameter("@keyFolderID", folder.ID)); conn.Open(); try { @@ -296,7 +309,7 @@ namespace OpenSim.Data.MSSQL using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); - cmd.Parameters.Add(database.CreateParameter("@folderID", folder.ID)); + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); conn.Open(); try { @@ -489,8 +502,7 @@ namespace OpenSim.Data.MSSQL /// Inventory item to update public void updateInventoryItem(InventoryItemBase item) { - string sql = @"UPDATE inventoryitems SET inventoryID = @inventoryID, - assetID = @assetID, + string sql = @"UPDATE inventoryitems SET assetID = @assetID, assetType = @assetType, parentFolderID = @parentFolderID, avatarID = @avatarID, @@ -502,13 +514,14 @@ namespace OpenSim.Data.MSSQL creatorID = @creatorID, inventoryBasePermissions = @inventoryBasePermissions, inventoryEveryOnePermissions = @inventoryEveryOnePermissions, + inventoryGroupPermissions = @inventoryGroupPermissions, salePrice = @salePrice, saleType = @saleType, creationDate = @creationDate, groupID = @groupID, groupOwned = @groupOwned, flags = @flags - WHERE inventoryID = @keyInventoryID"; + WHERE inventoryID = @inventoryID"; string itemName = item.Name; if (item.Name.Length > 64) @@ -537,16 +550,16 @@ namespace OpenSim.Data.MSSQL command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); command.Parameters.Add(database.CreateParameter("invType", item.InvType)); - command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorIdAsUuid)); + command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorId)); command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryGroupPermissions", item.GroupPermissions)); command.Parameters.Add(database.CreateParameter("salePrice", item.SalePrice)); command.Parameters.Add(database.CreateParameter("saleType", item.SaleType)); command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); command.Parameters.Add(database.CreateParameter("flags", item.Flags)); - command.Parameters.Add(database.CreateParameter("keyInventoryID", item.ID)); conn.Open(); try { @@ -732,9 +745,9 @@ namespace OpenSim.Data.MSSQL try { InventoryFolderBase folder = new InventoryFolderBase(); - folder.Owner = new UUID((Guid)reader["agentID"]); - folder.ParentID = new UUID((Guid)reader["parentFolderID"]); - folder.ID = new UUID((Guid)reader["folderID"]); + folder.Owner = DBGuid.FromDB(reader["agentID"]); + folder.ParentID = DBGuid.FromDB(reader["parentFolderID"]); + folder.ID = DBGuid.FromDB(reader["folderID"]); folder.Name = (string)reader["folderName"]; folder.Type = (short)reader["type"]; folder.Version = Convert.ToUInt16(reader["version"]); @@ -760,24 +773,24 @@ namespace OpenSim.Data.MSSQL { InventoryItemBase item = new InventoryItemBase(); - item.ID = new UUID((Guid)reader["inventoryID"]); - item.AssetID = new UUID((Guid)reader["assetID"]); + item.ID = DBGuid.FromDB(reader["inventoryID"]); + item.AssetID = DBGuid.FromDB(reader["assetID"]); item.AssetType = Convert.ToInt32(reader["assetType"].ToString()); - item.Folder = new UUID((Guid)reader["parentFolderID"]); - item.Owner = new UUID((Guid)reader["avatarID"]); + item.Folder = DBGuid.FromDB(reader["parentFolderID"]); + item.Owner = DBGuid.FromDB(reader["avatarID"]); item.Name = reader["inventoryName"].ToString(); item.Description = reader["inventoryDescription"].ToString(); item.NextPermissions = Convert.ToUInt32(reader["inventoryNextPermissions"]); item.CurrentPermissions = Convert.ToUInt32(reader["inventoryCurrentPermissions"]); item.InvType = Convert.ToInt32(reader["invType"].ToString()); - item.CreatorId = ((Guid)reader["creatorID"]).ToString(); + item.CreatorId = reader["creatorID"].ToString(); item.BasePermissions = Convert.ToUInt32(reader["inventoryBasePermissions"]); item.EveryOnePermissions = Convert.ToUInt32(reader["inventoryEveryOnePermissions"]); item.GroupPermissions = Convert.ToUInt32(reader["inventoryGroupPermissions"]); item.SalePrice = Convert.ToInt32(reader["salePrice"]); item.SaleType = Convert.ToByte(reader["saleType"]); item.CreationDate = Convert.ToInt32(reader["creationDate"]); - item.GroupID = new UUID((Guid)reader["groupID"]); + item.GroupID = DBGuid.FromDB(reader["groupID"]); item.GroupOwned = Convert.ToBoolean(reader["groupOwned"]); item.Flags = Convert.ToUInt32(reader["flags"]); From 89b7c64b6f491c4f703bb8fe28987ee5e3d5c50f Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 23 May 2010 11:31:19 +0300 Subject: [PATCH 215/260] Various minor changes in the data tests --- OpenSim/Data/Tests/AssetTests.cs | 8 ++++++++ OpenSim/Data/Tests/BasicDataServiceTest.cs | 3 ++- OpenSim/Data/Tests/EstateTests.cs | 11 +---------- OpenSim/Data/Tests/InventoryTests.cs | 8 +++++--- OpenSim/Data/Tests/RegionTests.cs | 4 ++-- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/OpenSim/Data/Tests/AssetTests.cs b/OpenSim/Data/Tests/AssetTests.cs index d228c1fdcb..d771053bdd 100644 --- a/OpenSim/Data/Tests/AssetTests.cs +++ b/OpenSim/Data/Tests/AssetTests.cs @@ -106,10 +106,18 @@ namespace OpenSim.Data.Tests protected override void InitService(object service) { + ClearDB(); m_db = (TAssetData)service; m_db.Initialise(m_connStr); } + private void ClearDB() + { + DropTables("assets"); + ResetMigrations("AssetStore"); + } + + [Test] public void T001_LoadEmpty() { diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs index e91a45d8bb..4c7cf280c1 100644 --- a/OpenSim/Data/Tests/BasicDataServiceTest.cs +++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs @@ -183,9 +183,10 @@ namespace OpenSim.Data.Tests lst += ", " + s; } + string sCond = stores.Length > 1 ? ("in (" + lst + ")") : ("=" + lst); try { - ExecuteSql("DELETE FROM `migrations` where name in (" + lst + ");"); + ExecuteSql("DELETE FROM migrations where name " + sCond); } catch { diff --git a/OpenSim/Data/Tests/EstateTests.cs b/OpenSim/Data/Tests/EstateTests.cs index 2da010db3e..d6eed3dadd 100644 --- a/OpenSim/Data/Tests/EstateTests.cs +++ b/OpenSim/Data/Tests/EstateTests.cs @@ -108,17 +108,7 @@ namespace OpenSim.Data.Tests private void ClearDB() { // if a new table is added, it has to be dropped here - ExecuteSql("delete from migrations where name='EstateStore';"); - DropTables( - "prims", - "primshapes", - "primitems", - "terrain", - "land", - "landaccesslist", - "regionban", - "regionsettings", "estate_managers", "estate_groups", "estate_users", @@ -126,6 +116,7 @@ namespace OpenSim.Data.Tests "estate_settings", "estate_map" ); + ResetMigrations("EstateStore"); } #region 0Tests diff --git a/OpenSim/Data/Tests/InventoryTests.cs b/OpenSim/Data/Tests/InventoryTests.cs index 93e1eba55d..c22e26c3c9 100644 --- a/OpenSim/Data/Tests/InventoryTests.cs +++ b/OpenSim/Data/Tests/InventoryTests.cs @@ -123,7 +123,7 @@ namespace OpenSim.Data.Tests private void ClearDB() { DropTables("inventoryitems", "inventoryfolders"); - ExecuteSql("delete from migrations where name='Inventory'"); + ResetMigrations("InventoryStore"); } [Test] @@ -194,8 +194,10 @@ namespace OpenSim.Data.Tests [Test] public void T013_FolderHierarchy() { - Assert.That(db.getFolderHierarchy(zero).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(zero).Count, Is.EqualTo(0))"); - Assert.That(db.getFolderHierarchy(folder1).Count, Is.EqualTo(2), "Assert.That(db.getFolderHierarchy(folder1).Count, Is.EqualTo(2))"); + int n = db.getFolderHierarchy(zero).Count; // (for dbg - easier to see what's returned) + Assert.That(n, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(zero).Count, Is.EqualTo(0))"); + n = db.getFolderHierarchy(folder1).Count; + Assert.That(n, Is.EqualTo(2), "Assert.That(db.getFolderHierarchy(folder1).Count, Is.EqualTo(2))"); Assert.That(db.getFolderHierarchy(folder2).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(folder2).Count, Is.EqualTo(0))"); Assert.That(db.getFolderHierarchy(folder3).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(folder3).Count, Is.EqualTo(0))"); Assert.That(db.getFolderHierarchy(UUID.Random()).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(UUID.Random()).Count, Is.EqualTo(0))"); diff --git a/OpenSim/Data/Tests/RegionTests.cs b/OpenSim/Data/Tests/RegionTests.cs index 5ac2dd0ad1..1f654d316b 100644 --- a/OpenSim/Data/Tests/RegionTests.cs +++ b/OpenSim/Data/Tests/RegionTests.cs @@ -118,8 +118,8 @@ namespace OpenSim.Data.Tests m_rebuildDB = rebuild; } - public RegionTests() : this("", false) { } - public RegionTests(string conn) : this(conn, false) {} + public RegionTests() : this("", true) { } + public RegionTests(string conn) : this(conn, true) {} public RegionTests(bool rebuild): this("", rebuild) {} From 52a3dbd076394a67d165a5b1f852a5bc9ac918d4 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 23 May 2010 11:34:56 +0300 Subject: [PATCH 216/260] MSSQL Migration: CreatorID in InventoryItems changed to VARCHAR(36) Again, the same thing about potentially having non-GUID CreatorID. --- .../MSSQL/Resources/InventoryStore.migrations | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations b/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations index cd5dfdc1ca..e2a8d5709b 100644 --- a/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations +++ b/OpenSim/Data/MSSQL/Resources/InventoryStore.migrations @@ -171,4 +171,74 @@ CREATE NONCLUSTERED INDEX folder ON dbo.inventoryitems COMMIT +:VERSION 5 + +# It would be totally crazy to have to recreate the whole table just to change one column type, +# just because MS SQL treats each DEFAULT as a constraint object that must be dropped +# before anything can be done to the column. Since all defaults here are unnamed, there is +# no easy way to drop them! The hairy piece of code below removes all DEFAULT constraints +# from InventoryItems. + +# SO: anything that's NULLable is by default NULL, so please don't declare DEFAULT(NULL), +# they do nothing but prevent changes to the columns. If you really +# need to have DEFAULTs or other constraints, give them names so they can be dropped when needed! + +BEGIN TRANSACTION +DECLARE @nm varchar(80); +DECLARE x CURSOR LOCAL FORWARD_ONLY READ_ONLY + FOR SELECT name FROM sys.default_constraints where parent_object_id = OBJECT_ID('inventoryitems'); +OPEN x; +FETCH NEXT FROM x INTO @nm; +WHILE @@FETCH_STATUS = 0 +BEGIN + EXEC('alter table inventoryitems drop ' + @nm); + FETCH NEXT FROM x INTO @nm; +END +CLOSE x +DEALLOCATE x +COMMIT + +# all DEFAULTs dropped! + +:GO + +BEGIN TRANSACTION + +# Restoring defaults: +# NOTE: [inventoryID] does NOT need one: it's NOT NULL PK and a unique Guid must be provided every time anyway! + +alter table inventoryitems + add constraint def_baseperm default 0 for inventoryBasePermissions +alter table inventoryitems + add constraint def_allperm default 0 for inventoryEveryOnePermissions +alter table inventoryitems + add constraint def_grpperm default 0 for inventoryGroupPermissions + +COMMIT + +:VERSION 7 + +BEGIN TRANSACTION + +# CreatorID goes back to VARCHAR(36) (???) + +exec sp_rename 'inventoryitems.CreatorID', 'cr_old', 'COLUMN' + +:GO + +alter table inventoryitems + add creatorID varchar(36) NULL + +:GO + +update inventoryitems set creatorID = CONVERT(VARCHAR(36), cr_old) + +alter table inventoryitems + drop column cr_old + +COMMIT + + + + From 05d9ca1b26baaae3e9ab106665163f5beeecd11d Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 23 May 2010 11:36:40 +0300 Subject: [PATCH 217/260] MySQL Migrations: Minor correcton to Region/Estate split (some Estate SQL left behind in the Region migration) --- OpenSim/Data/MySQL/Resources/EstateStore.migrations | 12 ++++++++++++ OpenSim/Data/MySQL/Resources/RegionStore.migrations | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/OpenSim/Data/MySQL/Resources/EstateStore.migrations b/OpenSim/Data/MySQL/Resources/EstateStore.migrations index 2e0d658ea0..df82a2e1f8 100644 --- a/OpenSim/Data/MySQL/Resources/EstateStore.migrations +++ b/OpenSim/Data/MySQL/Resources/EstateStore.migrations @@ -1,6 +1,10 @@ :VERSION 13 # The estate migrations used to be in Region store +# here they will do nothing (bad) if the tables are already there, +# just update the store version. + +BEGIN; CREATE TABLE IF NOT EXISTS `estate_managers` ( `EstateID` int(10) unsigned NOT NULL, @@ -65,5 +69,13 @@ CREATE TABLE IF NOT EXISTS `estate_map` ( KEY `EstateID` (`EstateID`) ) ENGINE=InnoDB; +COMMIT; + +:VERSION 32 #--------------------- (moved from RegionStore migr, just in case) + +BEGIN; +ALTER TABLE estate_settings AUTO_INCREMENT = 100; +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations index 383c328114..d8a279dc71 100644 --- a/OpenSim/Data/MySQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -717,12 +717,6 @@ ALTER TABLE regionsettings ADD COLUMN loaded_creation_datetime int unsigned NOT COMMIT; -:VERSION 32 #--------------------- - -BEGIN; -ALTER TABLE estate_settings AUTO_INCREMENT = 100; -COMMIT; - :VERSION 33 #--------------------- BEGIN; From b9b6d9c4ea1dc3aacda1799d5603a06a5b704db7 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 23 May 2010 11:41:44 +0300 Subject: [PATCH 218/260] Removed (unused?) empty SQL files from MSSQL resource dir There was a whole bunch of these SQL files, all empty and apparently unused. Removing them is just a clean-up, if anybody has a reason for these files to be there, feel free to revert. --- OpenSim/Data/MSSQL/Resources/AvatarAppearance.sql | 0 OpenSim/Data/MSSQL/Resources/CreateAssetsTable.sql | 0 OpenSim/Data/MSSQL/Resources/CreateFoldersTable.sql | 0 OpenSim/Data/MSSQL/Resources/CreateItemsTable.sql | 0 OpenSim/Data/MSSQL/Resources/CreateUserFriendsTable.sql | 0 OpenSim/Data/MSSQL/Resources/Mssql-agents.sql | 0 OpenSim/Data/MSSQL/Resources/Mssql-logs.sql | 0 OpenSim/Data/MSSQL/Resources/Mssql-regions.sql | 0 OpenSim/Data/MSSQL/Resources/Mssql-users.sql | 0 9 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 OpenSim/Data/MSSQL/Resources/AvatarAppearance.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/CreateAssetsTable.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/CreateFoldersTable.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/CreateItemsTable.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/CreateUserFriendsTable.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/Mssql-agents.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/Mssql-logs.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/Mssql-regions.sql delete mode 100644 OpenSim/Data/MSSQL/Resources/Mssql-users.sql diff --git a/OpenSim/Data/MSSQL/Resources/AvatarAppearance.sql b/OpenSim/Data/MSSQL/Resources/AvatarAppearance.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/CreateAssetsTable.sql b/OpenSim/Data/MSSQL/Resources/CreateAssetsTable.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/CreateFoldersTable.sql b/OpenSim/Data/MSSQL/Resources/CreateFoldersTable.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/CreateItemsTable.sql b/OpenSim/Data/MSSQL/Resources/CreateItemsTable.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/CreateUserFriendsTable.sql b/OpenSim/Data/MSSQL/Resources/CreateUserFriendsTable.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/Mssql-agents.sql b/OpenSim/Data/MSSQL/Resources/Mssql-agents.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/Mssql-logs.sql b/OpenSim/Data/MSSQL/Resources/Mssql-logs.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/Mssql-regions.sql b/OpenSim/Data/MSSQL/Resources/Mssql-regions.sql deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OpenSim/Data/MSSQL/Resources/Mssql-users.sql b/OpenSim/Data/MSSQL/Resources/Mssql-users.sql deleted file mode 100644 index e69de29bb2..0000000000 From 57f4729eeaa377f74681bd3d0bbb999680c72441 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Sun, 23 May 2010 12:46:33 +0300 Subject: [PATCH 219/260] Ensured that tests are skipped for wrong conn string, also m_log chng The base test class now tries to connect to DB, ignores all tests in the class if unable to. Also m_log changed to instance field (which in this case shouldn't cause any problems), to avoid having to define it separately in each derived class. Here I touched things that I don't understand well (using log4net), so please review this commit. --- OpenSim/Data/Tests/BasicDataServiceTest.cs | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs index 4c7cf280c1..c261126722 100644 --- a/OpenSim/Data/Tests/BasicDataServiceTest.cs +++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs @@ -27,7 +27,10 @@ namespace OpenSim.Data.Tests private string m_file; // TODO: Is this in the right place here? - protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + // Later: apparently it's not, but does it matter here? +// protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected ILog m_log; // doesn't matter here that it's not static, init to correct type in instance .ctor public BasicDataServiceTest() : this("") @@ -38,6 +41,7 @@ namespace OpenSim.Data.Tests { m_connStr = !String.IsNullOrEmpty(conn) ? conn : DefaultTestConns.Get(typeof(TConn)); + m_log = LogManager.GetLogger(this.GetType()); OpenSim.Tests.Common.TestLogging.LogToConsole(); // TODO: Is that right? } @@ -72,10 +76,27 @@ namespace OpenSim.Data.Tests if (String.IsNullOrEmpty(m_connStr)) { string msg = String.Format("Connection string for {0} is not defined, ignoring tests", typeof(TConn).Name); - m_log.Error(msg); + m_log.Warn(msg); Assert.Ignore(msg); } + // Try the connection, ignore tests if Open() fails + using (TConn conn = new TConn()) + { + conn.ConnectionString = m_connStr; + try + { + conn.Open(); + conn.Close(); + } + catch + { + string msg = String.Format("{0} is unable to connect to the database, ignoring tests", typeof(TConn).Name); + m_log.Warn(msg); + Assert.Ignore(msg); + } + } + // If we manage to connect to the database with the user // and password above it is our test database, and run // these tests. If anything goes wrong, ignore these From 1ab826d67c218947bb89cbc781a589babcaf2666 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 23 May 2010 12:22:47 -0700 Subject: [PATCH 220/260] The 8th migration statement in AssetStore.migrations didn't look right. --- OpenSim/Data/MySQL/Resources/AssetStore.migrations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Data/MySQL/Resources/AssetStore.migrations b/OpenSim/Data/MySQL/Resources/AssetStore.migrations index 3fd58b772c..9c5563038f 100644 --- a/OpenSim/Data/MySQL/Resources/AssetStore.migrations +++ b/OpenSim/Data/MySQL/Resources/AssetStore.migrations @@ -73,5 +73,5 @@ ALTER TABLE assets ADD COLUMN asset_flags INTEGER NOT NULL DEFAULT 0; :VERSION 8 -alter table assets add CreatorID varchar(36) not null default '' +ALTER TABLE assets ADD COLUMN CreatorID varchar(36) NOT NULL DEFAULT ''; From c7c9edd049c505c949a7a0711cac1cf496c61185 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Mon, 24 May 2010 20:11:06 +0100 Subject: [PATCH 221/260] minor: expand upon comments about not scheduling two full updates for attachments --- .../Framework/InventoryAccess/InventoryAccessModule.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 2352cedef4..901dcba707 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -502,8 +502,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess group.RootPart.IsAttachment = true; } - // For attachments, we must make sure that only a single object update occurs after we've finished - // all the necessary operations. + // If we're rezzing an attachment then don't ask AddNewSceneObject() to update the client since + // we'll be doing that later on. Scheduling more than one full update during the attachment + // process causes some clients to fail to display the attachment properly. m_Scene.AddNewSceneObject(group, true, false); // m_log.InfoFormat("ray end point for inventory rezz is {0} {1} {2} ", RayEnd.X, RayEnd.Y, RayEnd.Z); From 7d9b316ce6bdb3eee909f8d2621ed8edcf2836e3 Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 24 May 2010 23:37:47 +0100 Subject: [PATCH 222/260] Change the way alpha is interpreted on prim text. Manris #4723 --- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 71c8018717..2357c6b575 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -3479,7 +3479,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void SetText(string text, Vector3 color, double alpha) { - Color = Color.FromArgb(0xff - (int) (alpha*0xff), + Color = Color.FromArgb((int) (alpha*0xff), (int) (color.X*0xff), (int) (color.Y*0xff), (int) (color.Z*0xff)); From 5e719d13efc5c8cae5f2a2b3066481250cb7e7e5 Mon Sep 17 00:00:00 2001 From: dahlia Date: Mon, 24 May 2010 19:15:04 -0700 Subject: [PATCH 223/260] fix sculpt normal direction for mirrored plane sculpts --- OpenSim/Region/Physics/Meshing/SculptMesh.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs index 6aa8fe49a3..e58eb896bb 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs @@ -310,8 +310,7 @@ namespace PrimMesher sculptType = (SculptType)(((int)sculptType) & 0x07); if (mirror) - if (sculptType == SculptType.plane) - invert = !invert; + invert = !invert; viewerFaces = new List(); From 5d65ef2db3da400a005f2279211de9e9c62641b3 Mon Sep 17 00:00:00 2001 From: AlexRa Date: Wed, 26 May 2010 10:22:59 +0300 Subject: [PATCH 224/260] Minor correction to AssetTests.cs (forgot to change test descriptions, has no effect on running the tests) --- OpenSim/Data/Tests/AssetTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/Tests/AssetTests.cs b/OpenSim/Data/Tests/AssetTests.cs index d771053bdd..800b9bfba2 100644 --- a/OpenSim/Data/Tests/AssetTests.cs +++ b/OpenSim/Data/Tests/AssetTests.cs @@ -60,17 +60,17 @@ namespace OpenSim.Data.Tests #else - [TestFixture(Description = "Region store tests (SQLite)")] + [TestFixture(Description = "Asset store tests (SQLite)")] public class SQLiteAssetTests : AssetTests { } - [TestFixture(Description = "Region store tests (MySQL)")] + [TestFixture(Description = "Asset store tests (MySQL)")] public class MySqlAssetTests : AssetTests { } - [TestFixture(Description = "Region store tests (MS SQL Server)")] + [TestFixture(Description = "Asset store tests (MS SQL Server)")] public class MSSQLAssetTests : AssetTests { } From 9ac8d2de794f2942e028722fb62911d44d97514b Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Wed, 26 May 2010 19:26:30 +0200 Subject: [PATCH 225/260] Just because there is an agent update handler, that doesn't mean there is a pre agent update handler. Null check these separately. --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index e67428d3c2..0945bce5ae 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4918,14 +4918,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP arg.SessionID = x.SessionID; arg.State = x.State; UpdateAgent handlerAgentUpdate = OnAgentUpdate; + UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; lastarg = arg; // save this set of arguments for nexttime - if (handlerAgentUpdate != null) - { + if (handlerPreAgentUpdate != null) OnPreAgentUpdate(this, arg); + if (handlerAgentUpdate != null) OnAgentUpdate(this, arg); - } handlerAgentUpdate = null; + handlerPreAgentUpdate = null; } } From 6b6425aa5ee66df3e4cad3c4453854ff4a087aa8 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 27 May 2010 19:48:21 +0200 Subject: [PATCH 226/260] Comment noisy "CONNECTION DEBUGGING" messages, because they push more important stuff off screen too fast --- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 2 +- .../EntityTransfer/EntityTransferModule.cs | 14 +++++++++++++- .../Server/Handlers/Hypergrid/HomeAgentHandlers.cs | 14 +++++++------- .../Server/Handlers/Simulation/AgentHandlers.cs | 14 +++++++------- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index bdbd2848c6..aa8883628b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -158,7 +158,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// the bucket, otherwise false public bool RemoveTokens(int amount, out bool dripSucceeded) { - if (maxBurst == 0) + if (true) //maxBurst == 0) { dripSucceeded = true; return true; diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index ef37f63622..fa49f9ff3c 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -436,7 +436,19 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer KillEntity(sp.Scene, sp.LocalId); - sp.MakeChildAgent(); + // MT wrapped this in a try because I've been seeing an + // eception here, but no line number. Need to see if SP is + // valid. This may move the exception to another place + // where it can be debugged better. + try + { + sp.MakeChildAgent(); + } + catch(Exception e) + { + m_log.Error("Exception on MakeChildAgent: " + e.ToString()); + } + // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone if (NeedsClosing(oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) diff --git a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs index 17d7850666..e50481a259 100644 --- a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs +++ b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs @@ -61,13 +61,13 @@ namespace OpenSim.Server.Handlers.Hypergrid public Hashtable Handler(Hashtable request) { - m_log.Debug("[CONNECTION DEBUGGING]: HomeAgentHandler Called"); - - m_log.Debug("---------------------------"); - m_log.Debug(" >> uri=" + request["uri"]); - m_log.Debug(" >> content-type=" + request["content-type"]); - m_log.Debug(" >> http-method=" + request["http-method"]); - m_log.Debug("---------------------------\n"); +// m_log.Debug("[CONNECTION DEBUGGING]: HomeAgentHandler Called"); +// +// m_log.Debug("---------------------------"); +// m_log.Debug(" >> uri=" + request["uri"]); +// m_log.Debug(" >> content-type=" + request["content-type"]); +// m_log.Debug(" >> http-method=" + request["http-method"]); +// m_log.Debug("---------------------------\n"); Hashtable responsedata = new Hashtable(); responsedata["content_type"] = "text/html"; diff --git a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs index ab3250d45f..191acc946a 100644 --- a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs +++ b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs @@ -61,13 +61,13 @@ namespace OpenSim.Server.Handlers.Simulation public Hashtable Handler(Hashtable request) { - m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); - - m_log.Debug("---------------------------"); - m_log.Debug(" >> uri=" + request["uri"]); - m_log.Debug(" >> content-type=" + request["content-type"]); - m_log.Debug(" >> http-method=" + request["http-method"]); - m_log.Debug("---------------------------\n"); +// m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); +// +// m_log.Debug("---------------------------"); +// m_log.Debug(" >> uri=" + request["uri"]); +// m_log.Debug(" >> content-type=" + request["content-type"]); +// m_log.Debug(" >> http-method=" + request["http-method"]); +// m_log.Debug("---------------------------\n"); Hashtable responsedata = new Hashtable(); responsedata["content_type"] = "text/html"; From a48d7f62a7e3af1aae2437668c484d4d0d1d92e5 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 27 May 2010 19:02:20 +0100 Subject: [PATCH 227/260] Revert "Comment noisy "CONNECTION DEBUGGING" messages, because they push more" Some other stuff snuck in. This reverts commit 4cc533e7ad94d148351c16f48afd2a688a64c48a. --- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 2 +- .../EntityTransfer/EntityTransferModule.cs | 14 +------------- .../Server/Handlers/Hypergrid/HomeAgentHandlers.cs | 14 +++++++------- .../Server/Handlers/Simulation/AgentHandlers.cs | 14 +++++++------- 4 files changed, 16 insertions(+), 28 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index aa8883628b..bdbd2848c6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -158,7 +158,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// the bucket, otherwise false public bool RemoveTokens(int amount, out bool dripSucceeded) { - if (true) //maxBurst == 0) + if (maxBurst == 0) { dripSucceeded = true; return true; diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index fa49f9ff3c..ef37f63622 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -436,19 +436,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer KillEntity(sp.Scene, sp.LocalId); - // MT wrapped this in a try because I've been seeing an - // eception here, but no line number. Need to see if SP is - // valid. This may move the exception to another place - // where it can be debugged better. - try - { - sp.MakeChildAgent(); - } - catch(Exception e) - { - m_log.Error("Exception on MakeChildAgent: " + e.ToString()); - } - + sp.MakeChildAgent(); // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone if (NeedsClosing(oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) diff --git a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs index e50481a259..17d7850666 100644 --- a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs +++ b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs @@ -61,13 +61,13 @@ namespace OpenSim.Server.Handlers.Hypergrid public Hashtable Handler(Hashtable request) { -// m_log.Debug("[CONNECTION DEBUGGING]: HomeAgentHandler Called"); -// -// m_log.Debug("---------------------------"); -// m_log.Debug(" >> uri=" + request["uri"]); -// m_log.Debug(" >> content-type=" + request["content-type"]); -// m_log.Debug(" >> http-method=" + request["http-method"]); -// m_log.Debug("---------------------------\n"); + m_log.Debug("[CONNECTION DEBUGGING]: HomeAgentHandler Called"); + + m_log.Debug("---------------------------"); + m_log.Debug(" >> uri=" + request["uri"]); + m_log.Debug(" >> content-type=" + request["content-type"]); + m_log.Debug(" >> http-method=" + request["http-method"]); + m_log.Debug("---------------------------\n"); Hashtable responsedata = new Hashtable(); responsedata["content_type"] = "text/html"; diff --git a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs index 191acc946a..ab3250d45f 100644 --- a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs +++ b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs @@ -61,13 +61,13 @@ namespace OpenSim.Server.Handlers.Simulation public Hashtable Handler(Hashtable request) { -// m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); -// -// m_log.Debug("---------------------------"); -// m_log.Debug(" >> uri=" + request["uri"]); -// m_log.Debug(" >> content-type=" + request["content-type"]); -// m_log.Debug(" >> http-method=" + request["http-method"]); -// m_log.Debug("---------------------------\n"); + m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); + + m_log.Debug("---------------------------"); + m_log.Debug(" >> uri=" + request["uri"]); + m_log.Debug(" >> content-type=" + request["content-type"]); + m_log.Debug(" >> http-method=" + request["http-method"]); + m_log.Debug("---------------------------\n"); Hashtable responsedata = new Hashtable(); responsedata["content_type"] = "text/html"; From ecc77e3886d1dbffc06ae728437292a6169e0fa3 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 27 May 2010 20:08:12 +0200 Subject: [PATCH 228/260] Comment noisy "CONNECTION DEBUGGING" messages, because they push more important stuff off screen too fast. Clean this time --- .../Server/Handlers/Hypergrid/HomeAgentHandlers.cs | 14 +++++++------- .../Server/Handlers/Simulation/AgentHandlers.cs | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs index 17d7850666..e50481a259 100644 --- a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs +++ b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs @@ -61,13 +61,13 @@ namespace OpenSim.Server.Handlers.Hypergrid public Hashtable Handler(Hashtable request) { - m_log.Debug("[CONNECTION DEBUGGING]: HomeAgentHandler Called"); - - m_log.Debug("---------------------------"); - m_log.Debug(" >> uri=" + request["uri"]); - m_log.Debug(" >> content-type=" + request["content-type"]); - m_log.Debug(" >> http-method=" + request["http-method"]); - m_log.Debug("---------------------------\n"); +// m_log.Debug("[CONNECTION DEBUGGING]: HomeAgentHandler Called"); +// +// m_log.Debug("---------------------------"); +// m_log.Debug(" >> uri=" + request["uri"]); +// m_log.Debug(" >> content-type=" + request["content-type"]); +// m_log.Debug(" >> http-method=" + request["http-method"]); +// m_log.Debug("---------------------------\n"); Hashtable responsedata = new Hashtable(); responsedata["content_type"] = "text/html"; diff --git a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs index ab3250d45f..191acc946a 100644 --- a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs +++ b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs @@ -61,13 +61,13 @@ namespace OpenSim.Server.Handlers.Simulation public Hashtable Handler(Hashtable request) { - m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); - - m_log.Debug("---------------------------"); - m_log.Debug(" >> uri=" + request["uri"]); - m_log.Debug(" >> content-type=" + request["content-type"]); - m_log.Debug(" >> http-method=" + request["http-method"]); - m_log.Debug("---------------------------\n"); +// m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); +// +// m_log.Debug("---------------------------"); +// m_log.Debug(" >> uri=" + request["uri"]); +// m_log.Debug(" >> content-type=" + request["content-type"]); +// m_log.Debug(" >> http-method=" + request["http-method"]); +// m_log.Debug("---------------------------\n"); Hashtable responsedata = new Hashtable(); responsedata["content_type"] = "text/html"; From 8fb706716ba0162210d6030f3e7b5b6636b35651 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Thu, 27 May 2010 20:08:48 +0200 Subject: [PATCH 229/260] Prevent a null ref --- OpenSim/Region/Framework/Scenes/SceneViewer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/SceneViewer.cs b/OpenSim/Region/Framework/Scenes/SceneViewer.cs index c6cf4ccf39..15bc33d172 100644 --- a/OpenSim/Region/Framework/Scenes/SceneViewer.cs +++ b/OpenSim/Region/Framework/Scenes/SceneViewer.cs @@ -84,6 +84,9 @@ namespace OpenSim.Region.Framework.Scenes while (m_pendingObjects != null && m_pendingObjects.Count > 0) { SceneObjectGroup g = m_pendingObjects.Dequeue(); + // Yes, this can really happen + if (g == null) + continue; // This is where we should check for draw distance // do culling and stuff. Problem with that is that until From d53057c2c7dfc7762fec99144289edf472e05bc6 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 16:26:14 +0100 Subject: [PATCH 230/260] hopefully get "nant test" passing again by replacing data.sqlite/mysql references in .nant/local.include with OpenSim.Data.Tests.dll --- .nant/local.include | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/.nant/local.include b/.nant/local.include index 97353e05f2..0b6f9feafd 100644 --- a/.nant/local.include +++ b/.nant/local.include @@ -127,15 +127,10 @@ - - + + - - - - - - + @@ -297,14 +292,9 @@ - - - - - - - - + + + @@ -313,8 +303,7 @@ - - + From 596001632b346f7b57fef746dfcee2cd4bacb2b4 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 17:53:57 +0100 Subject: [PATCH 231/260] remove redundant ScenePresence.QueuePartForUpdate() - every place in the code calls SceneViewer.QueuePartForUpdate() directly --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 3964b0bf61..45375b0e8c 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -784,15 +784,6 @@ namespace OpenSim.Region.Framework.Scenes #endregion - /// - /// Add the part to the queue of parts for which we need to send an update to the client - /// - /// - public void QueuePartForUpdate(SceneObjectPart part) - { - m_sceneViewer.QueuePartForUpdate(part); - } - public uint GenerateClientFlags(UUID ObjectID) { return m_scene.Permissions.GenerateClientFlags(m_uuid, ObjectID); From 877fe774ef718c96c65d9e56c1229338f94c78cb Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 17:58:51 +0100 Subject: [PATCH 232/260] Simplify AddFullUpdateToAvatars()/AddPartialUpdateToAvatars() by calling the object's corresponding single avatar update method, rather than calling the sceneviewer directly --- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 2357c6b575..b36b9bfff2 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1278,10 +1278,13 @@ namespace OpenSim.Region.Framework.Scenes { m_parentGroup.Scene.ForEachScenePresence(delegate(ScenePresence avatar) { - avatar.SceneViewer.QueuePartForUpdate(this); + AddFullUpdateToAvatar(avatar); }); } + /// + /// Tell the scene presence that it should send updates for this part to its client + /// public void AddFullUpdateToAvatar(ScenePresence presence) { presence.SceneViewer.QueuePartForUpdate(this); @@ -1302,7 +1305,7 @@ namespace OpenSim.Region.Framework.Scenes { m_parentGroup.Scene.ForEachScenePresence(delegate(ScenePresence avatar) { - avatar.SceneViewer.QueuePartForUpdate(this); + AddTerseUpdateToAvatar(avatar); }); } From 565db4635f0e99cbd647cc8ba0ad963e2d1869fc Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 18:30:33 +0100 Subject: [PATCH 233/260] add a smidgen more log debug information in the form of the operating system version --- OpenSim/Framework/Servers/BaseOpenSimServer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 655df9df78..f0f8d0186b 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -286,7 +286,11 @@ namespace OpenSim.Framework.Servers EnhanceVersionInformation(); - m_log.Info("[STARTUP]: Version: " + m_version + "\n"); + m_log.Info("[STARTUP]: OpenSimulator version: " + m_version + Environment.NewLine); + // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and + // the clr version number doesn't match the project version number under Mono. + //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); + m_log.Info("[STARTUP]: Operating system version: " + Environment.OSVersion + Environment.NewLine); StartupSpecific(); From 1042ce72831780869a649bec57e95ec717d7a5a1 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 18:42:25 +0100 Subject: [PATCH 234/260] comment out large chunks of appearance/inventory region access services to eliminate warnings that this code is unused --- .../Rest/Inventory/RestAppearanceServices.cs | 302 +++-- .../Rest/Inventory/RestInventoryServices.cs | 1014 ++++++++--------- 2 files changed, 656 insertions(+), 660 deletions(-) diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs index b70a5116ca..4369216b26 100644 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs +++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs @@ -39,8 +39,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory public class RestAppearanceServices : IRest { - - private static readonly int PARM_USERID = 0; +// private static readonly int PARM_USERID = 0; // private static readonly int PARM_PATH = 1; @@ -64,6 +63,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory { Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId); qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); + qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix); Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix); } @@ -294,31 +294,31 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// /// HTTP service request work area - private void DoGet(AppearanceRequestData rdata) - { - AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID); - - if (adata == null) - { - rdata.Fail(Rest.HttpStatusCodeNoContent, - String.Format("appearance data not found for user {0} {1}", - rdata.userProfile.FirstName, rdata.userProfile.SurName)); - } - rdata.userAppearance = adata.ToAvatarAppearance(rdata.userProfile.ID); - - rdata.initXmlWriter(); - - FormatUserAppearance(rdata); - - // Indicate a successful request - - rdata.Complete(); - - // Send the response to the user. The body will be implicitly - // constructed from the result of the XML writer. - - rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method)); - } +// private void DoGet(AppearanceRequestData rdata) +// { +// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID); +// +// if (adata == null) +// { +// rdata.Fail(Rest.HttpStatusCodeNoContent, +// String.Format("appearance data not found for user {0} {1}", +// rdata.userProfile.FirstName, rdata.userProfile.SurName)); +// } +// rdata.userAppearance = adata.ToAvatarAppearance(rdata.userProfile.ID); +// +// rdata.initXmlWriter(); +// +// FormatUserAppearance(rdata); +// +// // Indicate a successful request +// +// rdata.Complete(); +// +// // Send the response to the user. The body will be implicitly +// // constructed from the result of the XML writer. +// +// rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method)); +// } /// /// POST adds NEW information to the user profile database. @@ -326,112 +326,112 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// characteristics supplied in the request. /// - private void DoExtend(AppearanceRequestData rdata) - { - - bool created = false; - bool modified = false; - string newnode = String.Empty; - - Rest.Log.DebugFormat("{0} POST ENTRY", MsgId); - - //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID); - - rdata.userAppearance = new AvatarAppearance(); - - // Although the following behavior is admitted by HTTP I am becoming - // increasingly doubtful that it is appropriate for REST. If I attempt to - // add a new record, and it already exists, then it seems to me that the - // attempt should fail, rather than update the existing record. - AvatarData adata = null; - if (GetUserAppearance(rdata)) - { - modified = rdata.userAppearance != null; - created = !modified; - adata = new AvatarData(rdata.userAppearance); - Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); - // Rest.UserServices.UpdateUserProfile(rdata.userProfile); - } - else - { - created = true; - adata = new AvatarData(rdata.userAppearance); - Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); - // Rest.UserServices.UpdateUserProfile(rdata.userProfile); - } - - if (created) - { - newnode = String.Format("{0} {1}", rdata.userProfile.FirstName, - rdata.userProfile.SurName); - // Must include a location header with a URI that identifies the new resource. - - rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}", - rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode)); - rdata.Complete(Rest.HttpStatusCodeCreated); - - } - else - { - if (modified) - { - rdata.Complete(Rest.HttpStatusCodeOK); - } - else - { - rdata.Complete(Rest.HttpStatusCodeNoContent); - } - } - - rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); - - } +// private void DoExtend(AppearanceRequestData rdata) +// { +// +// bool created = false; +// bool modified = false; +// string newnode = String.Empty; +// +// Rest.Log.DebugFormat("{0} POST ENTRY", MsgId); +// +// //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID); +// +// rdata.userAppearance = new AvatarAppearance(); +// +// // Although the following behavior is admitted by HTTP I am becoming +// // increasingly doubtful that it is appropriate for REST. If I attempt to +// // add a new record, and it already exists, then it seems to me that the +// // attempt should fail, rather than update the existing record. +// AvatarData adata = null; +// if (GetUserAppearance(rdata)) +// { +// modified = rdata.userAppearance != null; +// created = !modified; +// adata = new AvatarData(rdata.userAppearance); +// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); +// // Rest.UserServices.UpdateUserProfile(rdata.userProfile); +// } +// else +// { +// created = true; +// adata = new AvatarData(rdata.userAppearance); +// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); +// // Rest.UserServices.UpdateUserProfile(rdata.userProfile); +// } +// +// if (created) +// { +// newnode = String.Format("{0} {1}", rdata.userProfile.FirstName, +// rdata.userProfile.SurName); +// // Must include a location header with a URI that identifies the new resource. +// +// rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}", +// rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode)); +// rdata.Complete(Rest.HttpStatusCodeCreated); +// +// } +// else +// { +// if (modified) +// { +// rdata.Complete(Rest.HttpStatusCodeOK); +// } +// else +// { +// rdata.Complete(Rest.HttpStatusCodeNoContent); +// } +// } +// +// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); +// +// } /// /// This updates the user's appearance. not all aspects need to be provided, /// only those supplied will be changed. /// - private void DoUpdate(AppearanceRequestData rdata) - { - - // REFACTORING PROBLEM This was commented out. It doesn't work for 0.7 - - //bool created = false; - //bool modified = false; - - - //rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID); - - //// If the user exists then this is considered a modification regardless - //// of what may, or may not be, specified in the payload. - - //if (rdata.userAppearance != null) - //{ - // modified = true; - // Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance); - // Rest.UserServices.UpdateUserProfile(rdata.userProfile); - //} - - //if (created) - //{ - // rdata.Complete(Rest.HttpStatusCodeCreated); - //} - //else - //{ - // if (modified) - // { - // rdata.Complete(Rest.HttpStatusCodeOK); - // } - // else - // { - // rdata.Complete(Rest.HttpStatusCodeNoContent); - // } - //} - - rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); - - } +// private void DoUpdate(AppearanceRequestData rdata) +// { +// +// // REFACTORING PROBLEM This was commented out. It doesn't work for 0.7 +// +// //bool created = false; +// //bool modified = false; +// +// +// //rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID); +// +// //// If the user exists then this is considered a modification regardless +// //// of what may, or may not be, specified in the payload. +// +// //if (rdata.userAppearance != null) +// //{ +// // modified = true; +// // Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance); +// // Rest.UserServices.UpdateUserProfile(rdata.userProfile); +// //} +// +// //if (created) +// //{ +// // rdata.Complete(Rest.HttpStatusCodeCreated); +// //} +// //else +// //{ +// // if (modified) +// // { +// // rdata.Complete(Rest.HttpStatusCodeOK); +// // } +// // else +// // { +// // rdata.Complete(Rest.HttpStatusCodeNoContent); +// // } +// //} +// +// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); +// +// } /// /// Delete the specified user's appearance. This actually performs a reset @@ -439,31 +439,29 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// Existing ownership is preserved. All prior updates are lost and can not /// be recovered. /// - - private void DoDelete(AppearanceRequestData rdata) - { - AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID); - - if (adata != null) - { - AvatarAppearance old = adata.ToAvatarAppearance(rdata.userProfile.ID); - rdata.userAppearance = new AvatarAppearance(); - rdata.userAppearance.Owner = old.Owner; - adata = new AvatarData(rdata.userAppearance); - - Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); - - rdata.Complete(); - } - else - { - - rdata.Complete(Rest.HttpStatusCodeNoContent); - } - - rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); - - } +// private void DoDelete(AppearanceRequestData rdata) +// { +// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID); +// +// if (adata != null) +// { +// AvatarAppearance old = adata.ToAvatarAppearance(rdata.userProfile.ID); +// rdata.userAppearance = new AvatarAppearance(); +// rdata.userAppearance.Owner = old.Owner; +// adata = new AvatarData(rdata.userAppearance); +// +// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata); +// +// rdata.Complete(); +// } +// else +// { +// +// rdata.Complete(Rest.HttpStatusCodeNoContent); +// } +// +// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method)); +// } #endregion method-specific processing diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs index 10f387df4f..a4135dbaed 100644 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs +++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs @@ -45,10 +45,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory { public class RestInventoryServices : IRest { - private static readonly int PARM_USERID = 0; +// private static readonly int PARM_USERID = 0; private static readonly int PARM_PATH = 1; - private bool enabled = false; +// private bool enabled = false; private string qPrefix = "inventory"; private static readonly string PRIVATE_ROOT_NAME = "My Inventory"; @@ -79,7 +79,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory // Activate if everything went OK - enabled = true; +// enabled = true; Rest.Log.InfoFormat("{0} Inventory services initialization complete", MsgId); } @@ -100,7 +100,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory public void Close() { - enabled = false; +// enabled = false; Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId); } @@ -139,7 +139,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// A consolidated HTTP request work area private void DoInventory(RequestData hdata) { - InventoryRequestData rdata = (InventoryRequestData) hdata; +// InventoryRequestData rdata = (InventoryRequestData) hdata; Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId); @@ -354,32 +354,32 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// corresponding subtree based upon node name. /// /// HTTP service request work area - private void DoGet(InventoryRequestData rdata) - { - rdata.initXmlWriter(); - - rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty); - - // If there are additional parameters, then these represent - // a path relative to the root of the inventory. This path - // must be traversed before we format the sub-tree thus - // identified. - - traverse(rdata, rdata.root, PARM_PATH); - - // Close all open elements - - rdata.writer.WriteFullEndElement(); - - // Indicate a successful request - - rdata.Complete(); - - // Send the response to the user. The body will be implicitly - // constructed from the result of the XML writer. - - rdata.Respond(String.Format("Inventory {0} Normal completion", rdata.method)); - } +// private void DoGet(InventoryRequestData rdata) +// { +// rdata.initXmlWriter(); +// +// rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty); +// +// // If there are additional parameters, then these represent +// // a path relative to the root of the inventory. This path +// // must be traversed before we format the sub-tree thus +// // identified. +// +// traverse(rdata, rdata.root, PARM_PATH); +// +// // Close all open elements +// +// rdata.writer.WriteFullEndElement(); +// +// // Indicate a successful request +// +// rdata.Complete(); +// +// // Send the response to the user. The body will be implicitly +// // constructed from the result of the XML writer. +// +// rdata.Respond(String.Format("Inventory {0} Normal completion", rdata.method)); +// } /// /// In the case of the inventory, and probably in general, @@ -419,210 +419,210 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// context identified by the URI. /// /// HTTP service request work area - private void DoExtend(InventoryRequestData rdata) - { - bool created = false; - bool modified = false; - string newnode = String.Empty; - - // Resolve the context node specified in the URI. Entity - // data will be ADDED beneath this node. rdata already contains - // information about the current content of the user's - // inventory. - - Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill); - - // Processing depends upon the type of inventory node - // identified in the URI. This is the CONTEXT for the - // change. We either got a context or we threw an - // exception. - - // It follows that we can only add information if the URI - // has identified a folder. So only a type of folder is supported - // in this case. - - if (typeof(InventoryFolderBase) == InventoryNode.GetType() || - typeof(InventoryFolderImpl) == InventoryNode.GetType()) - { - // Cast the context node appropriately. - - InventoryFolderBase context = (InventoryFolderBase) InventoryNode; - - Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}", - MsgId, rdata.method, rdata.path); - - // Reconstitute the inventory sub-tree from the XML supplied in the entity. - // The result is a stand-alone inventory subtree, not yet integrated into the - // existing tree. An inventory collection consists of three components: - // [1] A (possibly empty) set of folders. - // [2] A (possibly empty) set of items. - // [3] A (possibly empty) set of assets. - // If all of these are empty, then the POST is a harmless no-operation. - - XmlInventoryCollection entity = ReconstituteEntity(rdata); - - // Inlined assets can be included in entity. These must be incorporated into - // the asset database before we attempt to update the inventory. If anything - // fails, return a failure to requestor. - - if (entity.Assets.Count > 0) - { - Rest.Log.DebugFormat("{0} Adding {1} assets to server", - MsgId, entity.Assets.Count); - - foreach (AssetBase asset in entity.Assets) - { - Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", - MsgId, asset.ID, asset.Type, asset.Name); - Rest.AssetServices.Store(asset); - - created = true; - rdata.appendStatus(String.Format("

Created asset {0}, UUID {1}

", - asset.Name, asset.ID)); - - if (Rest.DEBUG && Rest.DumpAsset) - { - Rest.Dump(asset.Data); - } - } - } - - // Modify the context using the collection of folders and items - // returned in the XmlInventoryCollection. - - foreach (InventoryFolderBase folder in entity.Folders) - { - InventoryFolderBase found; - - // If the parentID is zero, then this folder is going - // into the root folder identified by the URI. The requestor - // may have already set the parent ID explicitly, in which - // case we don't have to do it here. - - if (folder.ParentID == UUID.Zero || folder.ParentID == context.ID) - { - if (newnode != String.Empty) - { - Rest.Log.DebugFormat("{0} Too many resources", MsgId); - rdata.Fail(Rest.HttpStatusCodeBadRequest, "only one root entity is allowed"); - } - folder.ParentID = context.ID; - newnode = folder.Name; - } - - // Search the existing inventory for an existing entry. If - // we have one, we need to decide if it has really changed. - // It could just be present as (unnecessary) context, and we - // don't want to waste time updating the database in that - // case, OR, it could be being moved from another location - // in which case an update is most certainly necessary. - - found = null; - - foreach (InventoryFolderBase xf in rdata.folders) - { - // Compare identifying attribute - if (xf.ID == folder.ID) - { - found = xf; - break; - } - } - - if (found != null && FolderHasChanged(folder,found)) - { - Rest.Log.DebugFormat("{0} Updating existing folder", MsgId); - Rest.InventoryServices.MoveFolder(folder); - - modified = true; - rdata.appendStatus(String.Format("

Created folder {0}, UUID {1}

", - folder.Name, folder.ID)); - } - else - { - Rest.Log.DebugFormat("{0} Adding new folder", MsgId); - Rest.InventoryServices.AddFolder(folder); - - created = true; - rdata.appendStatus(String.Format("

Modified folder {0}, UUID {1}

", - folder.Name, folder.ID)); - } - } - - // Now we repeat a similar process for the items included - // in the entity. - - foreach (InventoryItemBase item in entity.Items) - { - InventoryItemBase found = null; - - // If the parentID is zero, then this is going - // directly into the root identified by the URI. - - if (item.Folder == UUID.Zero) - { - item.Folder = context.ID; - } - - // Determine whether this is a new item or a - // replacement definition. - - foreach (InventoryItemBase xi in rdata.items) - { - // Compare identifying attribute - if (xi.ID == item.ID) - { - found = xi; - break; - } - } - - if (found != null && ItemHasChanged(item, found)) - { - Rest.Log.DebugFormat("{0} Updating item {1} {2} {3} {4} {5}", - MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); - Rest.InventoryServices.UpdateItem(item); - modified = true; - rdata.appendStatus(String.Format("

Modified item {0}, UUID {1}

", item.Name, item.ID)); - } - else - { - Rest.Log.DebugFormat("{0} Adding item {1} {2} {3} {4} {5}", - MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); - Rest.InventoryServices.AddItem(item); - created = true; - rdata.appendStatus(String.Format("

Created item {0}, UUID {1}

", item.Name, item.ID)); - } - } - - if (created) - { - // Must include a location header with a URI that identifies the new resource. - rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}/{3}", - rdata.hostname, rdata.port,rdata.path,newnode)); - rdata.Complete(Rest.HttpStatusCodeCreated); - } - else - { - if (modified) - { - rdata.Complete(Rest.HttpStatusCodeOK); - } - else - { - rdata.Complete(Rest.HttpStatusCodeNoContent); - } - } - - rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); - } - else - { - Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}", - MsgId, rdata.method, rdata.path, InventoryNode.GetType()); - rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid resource context"); - } - } +// private void DoExtend(InventoryRequestData rdata) +// { +// bool created = false; +// bool modified = false; +// string newnode = String.Empty; +// +// // Resolve the context node specified in the URI. Entity +// // data will be ADDED beneath this node. rdata already contains +// // information about the current content of the user's +// // inventory. +// +// Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill); +// +// // Processing depends upon the type of inventory node +// // identified in the URI. This is the CONTEXT for the +// // change. We either got a context or we threw an +// // exception. +// +// // It follows that we can only add information if the URI +// // has identified a folder. So only a type of folder is supported +// // in this case. +// +// if (typeof(InventoryFolderBase) == InventoryNode.GetType() || +// typeof(InventoryFolderImpl) == InventoryNode.GetType()) +// { +// // Cast the context node appropriately. +// +// InventoryFolderBase context = (InventoryFolderBase) InventoryNode; +// +// Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}", +// MsgId, rdata.method, rdata.path); +// +// // Reconstitute the inventory sub-tree from the XML supplied in the entity. +// // The result is a stand-alone inventory subtree, not yet integrated into the +// // existing tree. An inventory collection consists of three components: +// // [1] A (possibly empty) set of folders. +// // [2] A (possibly empty) set of items. +// // [3] A (possibly empty) set of assets. +// // If all of these are empty, then the POST is a harmless no-operation. +// +// XmlInventoryCollection entity = ReconstituteEntity(rdata); +// +// // Inlined assets can be included in entity. These must be incorporated into +// // the asset database before we attempt to update the inventory. If anything +// // fails, return a failure to requestor. +// +// if (entity.Assets.Count > 0) +// { +// Rest.Log.DebugFormat("{0} Adding {1} assets to server", +// MsgId, entity.Assets.Count); +// +// foreach (AssetBase asset in entity.Assets) +// { +// Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", +// MsgId, asset.ID, asset.Type, asset.Name); +// Rest.AssetServices.Store(asset); +// +// created = true; +// rdata.appendStatus(String.Format("

Created asset {0}, UUID {1}

", +// asset.Name, asset.ID)); +// +// if (Rest.DEBUG && Rest.DumpAsset) +// { +// Rest.Dump(asset.Data); +// } +// } +// } +// +// // Modify the context using the collection of folders and items +// // returned in the XmlInventoryCollection. +// +// foreach (InventoryFolderBase folder in entity.Folders) +// { +// InventoryFolderBase found; +// +// // If the parentID is zero, then this folder is going +// // into the root folder identified by the URI. The requestor +// // may have already set the parent ID explicitly, in which +// // case we don't have to do it here. +// +// if (folder.ParentID == UUID.Zero || folder.ParentID == context.ID) +// { +// if (newnode != String.Empty) +// { +// Rest.Log.DebugFormat("{0} Too many resources", MsgId); +// rdata.Fail(Rest.HttpStatusCodeBadRequest, "only one root entity is allowed"); +// } +// folder.ParentID = context.ID; +// newnode = folder.Name; +// } +// +// // Search the existing inventory for an existing entry. If +// // we have one, we need to decide if it has really changed. +// // It could just be present as (unnecessary) context, and we +// // don't want to waste time updating the database in that +// // case, OR, it could be being moved from another location +// // in which case an update is most certainly necessary. +// +// found = null; +// +// foreach (InventoryFolderBase xf in rdata.folders) +// { +// // Compare identifying attribute +// if (xf.ID == folder.ID) +// { +// found = xf; +// break; +// } +// } +// +// if (found != null && FolderHasChanged(folder,found)) +// { +// Rest.Log.DebugFormat("{0} Updating existing folder", MsgId); +// Rest.InventoryServices.MoveFolder(folder); +// +// modified = true; +// rdata.appendStatus(String.Format("

Created folder {0}, UUID {1}

", +// folder.Name, folder.ID)); +// } +// else +// { +// Rest.Log.DebugFormat("{0} Adding new folder", MsgId); +// Rest.InventoryServices.AddFolder(folder); +// +// created = true; +// rdata.appendStatus(String.Format("

Modified folder {0}, UUID {1}

", +// folder.Name, folder.ID)); +// } +// } +// +// // Now we repeat a similar process for the items included +// // in the entity. +// +// foreach (InventoryItemBase item in entity.Items) +// { +// InventoryItemBase found = null; +// +// // If the parentID is zero, then this is going +// // directly into the root identified by the URI. +// +// if (item.Folder == UUID.Zero) +// { +// item.Folder = context.ID; +// } +// +// // Determine whether this is a new item or a +// // replacement definition. +// +// foreach (InventoryItemBase xi in rdata.items) +// { +// // Compare identifying attribute +// if (xi.ID == item.ID) +// { +// found = xi; +// break; +// } +// } +// +// if (found != null && ItemHasChanged(item, found)) +// { +// Rest.Log.DebugFormat("{0} Updating item {1} {2} {3} {4} {5}", +// MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); +// Rest.InventoryServices.UpdateItem(item); +// modified = true; +// rdata.appendStatus(String.Format("

Modified item {0}, UUID {1}

", item.Name, item.ID)); +// } +// else +// { +// Rest.Log.DebugFormat("{0} Adding item {1} {2} {3} {4} {5}", +// MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); +// Rest.InventoryServices.AddItem(item); +// created = true; +// rdata.appendStatus(String.Format("

Created item {0}, UUID {1}

", item.Name, item.ID)); +// } +// } +// +// if (created) +// { +// // Must include a location header with a URI that identifies the new resource. +// rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}/{3}", +// rdata.hostname, rdata.port,rdata.path,newnode)); +// rdata.Complete(Rest.HttpStatusCodeCreated); +// } +// else +// { +// if (modified) +// { +// rdata.Complete(Rest.HttpStatusCodeOK); +// } +// else +// { +// rdata.Complete(Rest.HttpStatusCodeNoContent); +// } +// } +// +// rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); +// } +// else +// { +// Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}", +// MsgId, rdata.method, rdata.path, InventoryNode.GetType()); +// rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid resource context"); +// } +// } ///

/// PUT updates the URI-identified element in the inventory. This @@ -646,243 +646,242 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// during the reconstitution process. /// /// HTTP service request work area - private void DoUpdate(InventoryRequestData rdata) - { - int count = 0; - bool created = false; - bool modified = false; - - // Resolve the inventory node that is to be modified. - // rdata already contains information about the current - // content of the user's inventory. - - Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill); - - // As long as we have a node, then we have something - // meaningful to do, unlike POST. So we reconstitute the - // subtree before doing anything else. Note that we - // etiher got a valid node or we threw an exception. - - XmlInventoryCollection entity = ReconstituteEntity(rdata); - - // Incorporate any inlined assets first. Any failures - // will terminate the request. - - if (entity.Assets.Count > 0) - { - Rest.Log.DebugFormat("{0} Adding {1} assets to server", - MsgId, entity.Assets.Count); - - foreach (AssetBase asset in entity.Assets) - { - Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", - MsgId, asset.ID, asset.Type, asset.Name); - - // The asset was validated during the collection process - - Rest.AssetServices.Store(asset); - - created = true; - rdata.appendStatus(String.Format("

Created asset {0}, UUID {1}

", asset.Name, asset.ID)); - - if (Rest.DEBUG && Rest.DumpAsset) - { - Rest.Dump(asset.Data); - } - } - } - - // The URI specifies either a folder or an item to be updated. - // - // The root node in the entity will replace the node identified - // by the URI. This means the parent will remain the same, but - // any or all attributes associated with the named element - // will change. - // - // If the inventory collection contains an element with a zero - // parent ID, then this is taken to be the replacement for the - // named node. The collection MAY also specify an explicit - // parent ID, in this case it MAY identify the same parent as - // the current node, or it MAY specify a different parent, - // indicating that the folder is being moved in addition to any - // other modifications being made. - - if (typeof(InventoryFolderBase) == InventoryNode.GetType() || - typeof(InventoryFolderImpl) == InventoryNode.GetType()) - { - bool rfound = false; - InventoryFolderBase uri = (InventoryFolderBase) InventoryNode; - InventoryFolderBase xml = null; - - // If the entity to be replaced resolved to be the root - // directory itself (My Inventory), then make sure that - // the supplied data include as appropriately typed and - // named folder. Note that we can;t rule out the possibility - // of a sub-directory being called "My Inventory", so that - // is anticipated. - - if (uri == rdata.root) - { - foreach (InventoryFolderBase folder in entity.Folders) - { - if ((rfound = (folder.Name == PRIVATE_ROOT_NAME))) - { - if ((rfound = (folder.ParentID == UUID.Zero))) - break; - } - } - - if (!rfound) - { - Rest.Log.DebugFormat("{0} {1}: Path <{2}> will result in loss of inventory", - MsgId, rdata.method, rdata.path); - rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid inventory structure"); - } - } - - // Scan the set of folders in the entity collection for an - // entry that matches the context folder. It is assumed that - // the only reliable indicator of this is a zero UUID (using - // implicit context), or the parent's UUID matches that of the - // URI designated node (explicit context). We don't allow - // ambiguity in this case because this is POST and we are - // supposed to be modifying a specific node. - // We assign any element IDs required as an economy; we don't - // want to iterate over the fodler set again if it can be - // helped. - - foreach (InventoryFolderBase folder in entity.Folders) - { - if (folder.ParentID == uri.ParentID || - folder.ParentID == UUID.Zero) - { - folder.ParentID = uri.ParentID; - xml = folder; - count++; - } - } - - // More than one entry is ambiguous. Other folders should be - // added using the POST verb. - - if (count > 1) - { - Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous", - MsgId, rdata.method, rdata.path); - rdata.Fail(Rest.HttpStatusCodeConflict, "context is ambiguous"); - } - - // Exactly one entry means we ARE replacing the node - // identified by the URI. So we delete the old folder - // by moving it to the trash and then purging it. - // We then add all of the folders and items we - // included in the entity. The subtree has been - // modified. - - if (count == 1) - { - InventoryFolderBase TrashCan = GetTrashCan(rdata); - - // All went well, so we generate a UUID is one is - // needed. - - if (xml.ID == UUID.Zero) - { - xml.ID = UUID.Random(); - } - - uri.ParentID = TrashCan.ID; - Rest.InventoryServices.MoveFolder(uri); - Rest.InventoryServices.PurgeFolder(TrashCan); - modified = true; - } - - // Now, regardelss of what they represent, we - // integrate all of the elements in the entity. - - foreach (InventoryFolderBase f in entity.Folders) - { - rdata.appendStatus(String.Format("

Moving folder {0} UUID {1}

", f.Name, f.ID)); - Rest.InventoryServices.MoveFolder(f); - } - - foreach (InventoryItemBase it in entity.Items) - { - rdata.appendStatus(String.Format("

Storing item {0} UUID {1}

", it.Name, it.ID)); - Rest.InventoryServices.AddItem(it); - } - } - - ///

- /// URI specifies an item to be updated - /// - /// - /// The entity must contain a single item node to be - /// updated. ID and Folder ID must be correct. - /// - - else - { - InventoryItemBase uri = (InventoryItemBase) InventoryNode; - InventoryItemBase xml = null; - - if (entity.Folders.Count != 0) - { - Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>", - MsgId, rdata.method, rdata.path); - rdata.Fail(Rest.HttpStatusCodeBadRequest, "folder is not allowed"); - } - - if (entity.Items.Count > 1) - { - Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>", - MsgId, rdata.method, rdata.path); - rdata.Fail(Rest.HttpStatusCodeBadRequest, "too may items"); - } - - xml = entity.Items[0]; - - if (xml.ID == UUID.Zero) - { - xml.ID = UUID.Random(); - } - - // If the folder reference has changed, then this item is - // being moved. Otherwise we'll just delete the old, and - // add in the new. - - // Delete the old item - - List uuids = new List(); - uuids.Add(uri.ID); - Rest.InventoryServices.DeleteItems(uri.Owner, uuids); - - // Add the new item to the inventory - - Rest.InventoryServices.AddItem(xml); - - rdata.appendStatus(String.Format("

Storing item {0} UUID {1}

", xml.Name, xml.ID)); - } - - if (created) - { - rdata.Complete(Rest.HttpStatusCodeCreated); - } - else - { - if (modified) - { - rdata.Complete(Rest.HttpStatusCodeOK); - } - else - { - rdata.Complete(Rest.HttpStatusCodeNoContent); - } - } - - rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); - - } +// private void DoUpdate(InventoryRequestData rdata) +// { +// int count = 0; +// bool created = false; +// bool modified = false; +// +// // Resolve the inventory node that is to be modified. +// // rdata already contains information about the current +// // content of the user's inventory. +// +// Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill); +// +// // As long as we have a node, then we have something +// // meaningful to do, unlike POST. So we reconstitute the +// // subtree before doing anything else. Note that we +// // etiher got a valid node or we threw an exception. +// +// XmlInventoryCollection entity = ReconstituteEntity(rdata); +// +// // Incorporate any inlined assets first. Any failures +// // will terminate the request. +// +// if (entity.Assets.Count > 0) +// { +// Rest.Log.DebugFormat("{0} Adding {1} assets to server", +// MsgId, entity.Assets.Count); +// +// foreach (AssetBase asset in entity.Assets) +// { +// Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", +// MsgId, asset.ID, asset.Type, asset.Name); +// +// // The asset was validated during the collection process +// +// Rest.AssetServices.Store(asset); +// +// created = true; +// rdata.appendStatus(String.Format("

Created asset {0}, UUID {1}

", asset.Name, asset.ID)); +// +// if (Rest.DEBUG && Rest.DumpAsset) +// { +// Rest.Dump(asset.Data); +// } +// } +// } +// +// // The URI specifies either a folder or an item to be updated. +// // +// // The root node in the entity will replace the node identified +// // by the URI. This means the parent will remain the same, but +// // any or all attributes associated with the named element +// // will change. +// // +// // If the inventory collection contains an element with a zero +// // parent ID, then this is taken to be the replacement for the +// // named node. The collection MAY also specify an explicit +// // parent ID, in this case it MAY identify the same parent as +// // the current node, or it MAY specify a different parent, +// // indicating that the folder is being moved in addition to any +// // other modifications being made. +// +// if (typeof(InventoryFolderBase) == InventoryNode.GetType() || +// typeof(InventoryFolderImpl) == InventoryNode.GetType()) +// { +// bool rfound = false; +// InventoryFolderBase uri = (InventoryFolderBase) InventoryNode; +// InventoryFolderBase xml = null; +// +// // If the entity to be replaced resolved to be the root +// // directory itself (My Inventory), then make sure that +// // the supplied data include as appropriately typed and +// // named folder. Note that we can;t rule out the possibility +// // of a sub-directory being called "My Inventory", so that +// // is anticipated. +// +// if (uri == rdata.root) +// { +// foreach (InventoryFolderBase folder in entity.Folders) +// { +// if ((rfound = (folder.Name == PRIVATE_ROOT_NAME))) +// { +// if ((rfound = (folder.ParentID == UUID.Zero))) +// break; +// } +// } +// +// if (!rfound) +// { +// Rest.Log.DebugFormat("{0} {1}: Path <{2}> will result in loss of inventory", +// MsgId, rdata.method, rdata.path); +// rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid inventory structure"); +// } +// } +// +// // Scan the set of folders in the entity collection for an +// // entry that matches the context folder. It is assumed that +// // the only reliable indicator of this is a zero UUID (using +// // implicit context), or the parent's UUID matches that of the +// // URI designated node (explicit context). We don't allow +// // ambiguity in this case because this is POST and we are +// // supposed to be modifying a specific node. +// // We assign any element IDs required as an economy; we don't +// // want to iterate over the fodler set again if it can be +// // helped. +// +// foreach (InventoryFolderBase folder in entity.Folders) +// { +// if (folder.ParentID == uri.ParentID || +// folder.ParentID == UUID.Zero) +// { +// folder.ParentID = uri.ParentID; +// xml = folder; +// count++; +// } +// } +// +// // More than one entry is ambiguous. Other folders should be +// // added using the POST verb. +// +// if (count > 1) +// { +// Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous", +// MsgId, rdata.method, rdata.path); +// rdata.Fail(Rest.HttpStatusCodeConflict, "context is ambiguous"); +// } +// +// // Exactly one entry means we ARE replacing the node +// // identified by the URI. So we delete the old folder +// // by moving it to the trash and then purging it. +// // We then add all of the folders and items we +// // included in the entity. The subtree has been +// // modified. +// +// if (count == 1) +// { +// InventoryFolderBase TrashCan = GetTrashCan(rdata); +// +// // All went well, so we generate a UUID is one is +// // needed. +// +// if (xml.ID == UUID.Zero) +// { +// xml.ID = UUID.Random(); +// } +// +// uri.ParentID = TrashCan.ID; +// Rest.InventoryServices.MoveFolder(uri); +// Rest.InventoryServices.PurgeFolder(TrashCan); +// modified = true; +// } +// +// // Now, regardelss of what they represent, we +// // integrate all of the elements in the entity. +// +// foreach (InventoryFolderBase f in entity.Folders) +// { +// rdata.appendStatus(String.Format("

Moving folder {0} UUID {1}

", f.Name, f.ID)); +// Rest.InventoryServices.MoveFolder(f); +// } +// +// foreach (InventoryItemBase it in entity.Items) +// { +// rdata.appendStatus(String.Format("

Storing item {0} UUID {1}

", it.Name, it.ID)); +// Rest.InventoryServices.AddItem(it); +// } +// } +// +// ///

+// /// URI specifies an item to be updated +// /// +// /// +// /// The entity must contain a single item node to be +// /// updated. ID and Folder ID must be correct. +// /// +// +// else +// { +// InventoryItemBase uri = (InventoryItemBase) InventoryNode; +// InventoryItemBase xml = null; +// +// if (entity.Folders.Count != 0) +// { +// Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>", +// MsgId, rdata.method, rdata.path); +// rdata.Fail(Rest.HttpStatusCodeBadRequest, "folder is not allowed"); +// } +// +// if (entity.Items.Count > 1) +// { +// Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>", +// MsgId, rdata.method, rdata.path); +// rdata.Fail(Rest.HttpStatusCodeBadRequest, "too may items"); +// } +// +// xml = entity.Items[0]; +// +// if (xml.ID == UUID.Zero) +// { +// xml.ID = UUID.Random(); +// } +// +// // If the folder reference has changed, then this item is +// // being moved. Otherwise we'll just delete the old, and +// // add in the new. +// +// // Delete the old item +// +// List uuids = new List(); +// uuids.Add(uri.ID); +// Rest.InventoryServices.DeleteItems(uri.Owner, uuids); +// +// // Add the new item to the inventory +// +// Rest.InventoryServices.AddItem(xml); +// +// rdata.appendStatus(String.Format("

Storing item {0} UUID {1}

", xml.Name, xml.ID)); +// } +// +// if (created) +// { +// rdata.Complete(Rest.HttpStatusCodeCreated); +// } +// else +// { +// if (modified) +// { +// rdata.Complete(Rest.HttpStatusCodeOK); +// } +// else +// { +// rdata.Complete(Rest.HttpStatusCodeNoContent); +// } +// } +// +// rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); +// } ///

/// Arguably the most damaging REST interface. It deletes the inventory @@ -904,42 +903,41 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory /// elements. /// /// HTTP service request work area - - private void DoDelete(InventoryRequestData rdata) - { - Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, false); - - if (typeof(InventoryFolderBase) == InventoryNode.GetType() || - typeof(InventoryFolderImpl) == InventoryNode.GetType()) - { - InventoryFolderBase TrashCan = GetTrashCan(rdata); - - InventoryFolderBase folder = (InventoryFolderBase) InventoryNode; - Rest.Log.DebugFormat("{0} {1}: Folder {2} will be deleted", - MsgId, rdata.method, rdata.path); - folder.ParentID = TrashCan.ID; - Rest.InventoryServices.MoveFolder(folder); - Rest.InventoryServices.PurgeFolder(TrashCan); - - rdata.appendStatus(String.Format("

Deleted folder {0} UUID {1}

", folder.Name, folder.ID)); - } - - // Deleting items is much more straight forward. - - else - { - InventoryItemBase item = (InventoryItemBase) InventoryNode; - Rest.Log.DebugFormat("{0} {1}: Item {2} will be deleted", - MsgId, rdata.method, rdata.path); - List uuids = new List(); - uuids.Add(item.ID); - Rest.InventoryServices.DeleteItems(item.Owner, uuids); - rdata.appendStatus(String.Format("

Deleted item {0} UUID {1}

", item.Name, item.ID)); - } - - rdata.Complete(); - rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); - } +// private void DoDelete(InventoryRequestData rdata) +// { +// Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, false); +// +// if (typeof(InventoryFolderBase) == InventoryNode.GetType() || +// typeof(InventoryFolderImpl) == InventoryNode.GetType()) +// { +// InventoryFolderBase TrashCan = GetTrashCan(rdata); +// +// InventoryFolderBase folder = (InventoryFolderBase) InventoryNode; +// Rest.Log.DebugFormat("{0} {1}: Folder {2} will be deleted", +// MsgId, rdata.method, rdata.path); +// folder.ParentID = TrashCan.ID; +// Rest.InventoryServices.MoveFolder(folder); +// Rest.InventoryServices.PurgeFolder(TrashCan); +// +// rdata.appendStatus(String.Format("

Deleted folder {0} UUID {1}

", folder.Name, folder.ID)); +// } +// +// // Deleting items is much more straight forward. +// +// else +// { +// InventoryItemBase item = (InventoryItemBase) InventoryNode; +// Rest.Log.DebugFormat("{0} {1}: Item {2} will be deleted", +// MsgId, rdata.method, rdata.path); +// List uuids = new List(); +// uuids.Add(item.ID); +// Rest.InventoryServices.DeleteItems(item.Owner, uuids); +// rdata.appendStatus(String.Format("

Deleted item {0} UUID {1}

", item.Name, item.ID)); +// } +// +// rdata.Complete(); +// rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method)); +// } #endregion method-specific processing From 6b568af5658c07212288429d2e5c77849ffab9e5 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 18:49:32 +0100 Subject: [PATCH 235/260] Adjust Scene.DeleteAllSceneObjects() to not delete objects attached to avatars. This is going to be the right behaviour in all cases, I should think. This means that avatars in region when an oar is loaded do not lose their attachments --- OpenSim/Region/Framework/Scenes/Scene.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 46fbcd3832..6d4de90f20 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2121,7 +2121,7 @@ namespace OpenSim.Region.Framework.Scenes } ///

- /// Delete every object from the scene + /// Delete every object from the scene. This does not include attachments worn by avatars. /// public void DeleteAllSceneObjects() { @@ -2132,7 +2132,11 @@ namespace OpenSim.Region.Framework.Scenes foreach (EntityBase e in entities) { if (e is SceneObjectGroup) - DeleteSceneObject((SceneObjectGroup)e, false); + { + SceneObjectGroup sog = (SceneObjectGroup)e; + if (!sog.IsAttachment) + DeleteSceneObject((SceneObjectGroup)e, false); + } } } } From d72435693bf950dde2f3f20e9b5d41c91af60456 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 19:21:00 +0100 Subject: [PATCH 236/260] refactor: move GetStream and URI methods from ArchiveReadRequest -> ArchiveHelpers --- .../Archiver/InventoryArchiveWriteRequest.cs | 1 - .../World/Archiver/ArchiveHelpers.cs | 64 +++++++++++++++++++ .../World/Archiver/ArchiveReadRequest.cs | 64 +------------------ 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs index cfe3caa849..8f3f65b0b6 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs @@ -37,7 +37,6 @@ using OpenSim.Framework; using OpenSim.Framework.Serialization; using OpenSim.Framework.Serialization.External; using OpenSim.Framework.Communications; - using OpenSim.Framework.Communications.Osp; using OpenSim.Region.CoreModules.World.Archiver; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs index 880bd7cdd4..ddc3dd7644 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveHelpers.cs @@ -25,6 +25,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.IO; +using System.Net; using OpenMetaverse; using OpenSim.Framework.Serialization; using OpenSim.Region.Framework.Scenes; @@ -60,5 +63,66 @@ namespace OpenSim.Region.CoreModules.World.Archiver { return ArchiveConstants.CreateOarObjectPath(sog.Name, sog.UUID, sog.AbsolutePosition); } + + /// + /// Resolve path to a working FileStream + /// + /// + /// + public static Stream GetStream(string path) + { + if (File.Exists(path)) + { + return new FileStream(path, FileMode.Open, FileAccess.Read); + } + else + { + try + { + Uri uri = new Uri(path); + if (uri.Scheme == "file") + { + return new FileStream(uri.AbsolutePath, FileMode.Open, FileAccess.Read); + } + else + { + if (uri.Scheme != "http") + throw new Exception(String.Format("Unsupported URI scheme ({0})", path)); + + // OK, now we know we have an HTTP URI to work with + return URIFetch(uri); + } + } + catch (UriFormatException) + { + // In many cases the user will put in a plain old filename that cannot be found so assume that + // this is the problem rather than confusing the issue with a UriFormatException + throw new Exception(String.Format("Cannot find file {0}", path)); + } + } + } + + public static Stream URIFetch(Uri uri) + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); + + // request.Credentials = credentials; + + request.ContentLength = 0; + request.KeepAlive = false; + + WebResponse response = request.GetResponse(); + Stream file = response.GetResponseStream(); + + // justincc: gonna ignore the content type for now and just try anything + //if (response.ContentType != "application/x-oar") + // throw new Exception(String.Format("{0} does not identify an OAR file", uri.ToString())); + + if (response.ContentLength == 0) + throw new Exception(String.Format("{0} returned an empty file", uri.ToString())); + + // return new BufferedStream(file, (int) response.ContentLength); + return new BufferedStream(file, 1000000); + } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs index f97ae5f791..bc653ce5e9 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver try { - m_loadStream = new GZipStream(GetStream(loadPath), CompressionMode.Decompress); + m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress); } catch (EntryPointNotFoundException e) { @@ -470,68 +470,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver return true; } - /// - /// Resolve path to a working FileStream - /// - /// - /// - private Stream GetStream(string path) - { - if (File.Exists(path)) - { - return new FileStream(path, FileMode.Open, FileAccess.Read); - } - else - { - try - { - Uri uri = new Uri(path); - if (uri.Scheme == "file") - { - return new FileStream(uri.AbsolutePath, FileMode.Open, FileAccess.Read); - } - else - { - if (uri.Scheme != "http") - throw new Exception(String.Format("Unsupported URI scheme ({0})", path)); - - // OK, now we know we have an HTTP URI to work with - - return URIFetch(uri); - } - } - catch (UriFormatException) - { - // In many cases the user will put in a plain old filename that cannot be found so assume that - // this is the problem rather than confusing the issue with a UriFormatException - throw new Exception(String.Format("Cannot find file {0}", path)); - } - } - } - - private static Stream URIFetch(Uri uri) - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); - - // request.Credentials = credentials; - - request.ContentLength = 0; - request.KeepAlive = false; - - WebResponse response = request.GetResponse(); - Stream file = response.GetResponseStream(); - - // justincc: gonna ignore the content type for now and just try anything - //if (response.ContentType != "application/x-oar") - // throw new Exception(String.Format("{0} does not identify an OAR file", uri.ToString())); - - if (response.ContentLength == 0) - throw new Exception(String.Format("{0} returned an empty file", uri.ToString())); - - // return new BufferedStream(file, (int) response.ContentLength); - return new BufferedStream(file, 1000000); - } - /// /// Load oar control file /// From b1298fca0abaee46e12807c478a339cb04de13ed Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 19:36:30 +0100 Subject: [PATCH 237/260] tidy up help information on load oar and save oar --- OpenSim/Region/Application/OpenSim.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 139503074f..a09b903bfc 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -251,14 +251,20 @@ namespace OpenSim "Save named prim to XML2", SavePrimsXml2); m_console.Commands.AddCommand("region", false, "load oar", - "load oar [--merge] [--skip-assets] ", - "Load a region's data from OAR archive. --merge will merge the oar with the existing scene. --skip-assets will load the oar but ignore the assets it contains", + "load oar [--merge] [--skip-assets] []", + "Load a region's data from an OAR archive.", + "--merge will merge the OAR with the existing scene." + Environment.NewLine + + "--skip-assets will load the OAR but ignore the assets it contains." + Environment.NewLine + + "The path can be either a filesystem location or a URI." + + " If this is not given then the command looks for an OAR named region.oar in the current directory.", LoadOar); m_console.Commands.AddCommand("region", false, "save oar", - "save oar ", - "Save a region's data to an OAR archive", - "More information on forthcoming options here soon", SaveOar); + "save oar []", + "Save a region's data to an OAR archive.", + "The OAR path must be a filesystem path." + + " If this is not given then the oar is saved to region.oar in the current directory.", + SaveOar); m_console.Commands.AddCommand("region", false, "edit scale", "edit scale ", From 14c39461c2f20db2a79a64d2546cbd4bcb12bee9 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 19:41:13 +0100 Subject: [PATCH 238/260] minor: remove mono compiler warning --- OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index ac6a633413..c6fb18d8f3 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -1014,7 +1014,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap int lastMapRefresh = 0; int twoDays = 172800; - int RefreshSeconds = twoDays; +// int RefreshSeconds = twoDays; try { From fff5459f4d3a6fcb7acae9e092fd8aae3c74dd7b Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 20:07:15 +0100 Subject: [PATCH 239/260] Add ability to load IARs directly from URIs So, something like load iar Justin Clark-Casey / PASSWORD http://justincc.org/downloads/iars/my-great-items.iar Will load my IAR directly from the web. --- OpenSim/Framework/Console/CommandConsole.cs | 3 +-- .../Archiver/InventoryArchiveReadRequest.cs | 3 +-- .../Archiver/InventoryArchiverModule.cs | 21 ++++++++++++--- OpenSim/Region/Framework/Scenes/SceneBase.cs | 26 ++++++++++++++++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs index 66f483cf22..2008613717 100644 --- a/OpenSim/Framework/Console/CommandConsole.cs +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -182,8 +182,7 @@ namespace OpenSim.Framework.Console public void AddCommand(string module, bool shared, string command, string help, string longhelp, CommandDelegate fn) { - AddCommand(module, shared, command, help, longhelp, - String.Empty, fn); + AddCommand(module, shared, command, help, longhelp, String.Empty, fn); } /// diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index dc7439c6da..806aa4f155 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs @@ -37,7 +37,6 @@ using log4net; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications; - using OpenSim.Framework.Communications.Osp; using OpenSim.Framework.Serialization; using OpenSim.Framework.Serialization.External; @@ -72,7 +71,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver scene, userInfo, invPath, - new GZipStream(new FileStream(loadPath, FileMode.Open), CompressionMode.Decompress)) + new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress)) { } diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs index f57099912c..307db974b8 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs @@ -91,13 +91,26 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver scene.AddCommand( this, "load iar", - "load iar []", - "Load user inventory archive.", HandleLoadInvConsoleCommand); + "load iar []", + "Load user inventory archive (IAR).", + " is user's first name." + Environment.NewLine + + " is user's last name." + Environment.NewLine + + " is the path inside the user's inventory where the IAR should be loaded." + Environment.NewLine + + " is the user's password." + Environment.NewLine + + " is the filesystem path or URI from which to load the IAR." + + string.Format(" If this is not given then the filename {0} in the current directory is used", DEFAULT_INV_BACKUP_FILENAME), + HandleLoadInvConsoleCommand); scene.AddCommand( this, "save iar", - "save iar []", - "Save user inventory archive.", HandleSaveInvConsoleCommand); + "save iar []", + "Save user inventory archive (IAR).", + " is the user's first name." + Environment.NewLine + + " is the user's last name." + Environment.NewLine + + " is the path inside the user's inventory for the folder/item to be saved." + Environment.NewLine + + " is the filesystem path at which to save the IAR." + + string.Format(" If this is not given then the filename {0} in the current directory is used", DEFAULT_INV_BACKUP_FILENAME), + HandleSaveInvConsoleCommand); m_aScene = scene; } diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index bfc19b754c..c363a91fc2 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -498,7 +498,30 @@ namespace OpenSim.Region.Framework.Scenes } } + /// + /// Call this from a region module to add a command to the OpenSim console. + /// + /// + /// + /// + /// + /// public void AddCommand(object mod, string command, string shorthelp, string longhelp, CommandDelegate callback) + { + AddCommand(mod, command, shorthelp, longhelp, string.Empty, callback); + } + + /// + /// Call this from a region module to add a command to the OpenSim console. + /// + /// + /// + /// + /// + /// + /// + public void AddCommand( + object mod, string command, string shorthelp, string longhelp, string descriptivehelp, CommandDelegate callback) { if (MainConsole.Instance == null) return; @@ -523,7 +546,8 @@ namespace OpenSim.Region.Framework.Scenes else throw new Exception("AddCommand module parameter must be IRegionModule or IRegionModuleBase"); } - MainConsole.Instance.Commands.AddCommand(modulename, shared, command, shorthelp, longhelp, callback); + MainConsole.Instance.Commands.AddCommand( + modulename, shared, command, shorthelp, longhelp, descriptivehelp, callback); } public virtual ISceneObject DeserializeObject(string representation) From 2e2f73bdd5034febfa67c230257289f75946d8ce Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 20:12:06 +0100 Subject: [PATCH 240/260] minor: remove mono compiler warning --- OpenSim/Services/PresenceService/PresenceService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Services/PresenceService/PresenceService.cs b/OpenSim/Services/PresenceService/PresenceService.cs index 19f636a1c1..601a69f042 100644 --- a/OpenSim/Services/PresenceService/PresenceService.cs +++ b/OpenSim/Services/PresenceService/PresenceService.cs @@ -54,7 +54,8 @@ namespace OpenSim.Services.PresenceService public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) { - PresenceData[] d = m_Database.Get("UserID", userID); + //PresenceData[] d = m_Database.Get("UserID", userID); + m_Database.Get("UserID", userID); PresenceData data = new PresenceData(); From 3c0f34bc2be6bf692b498a92ffb4c0b338b9e4bd Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 20:51:02 +0100 Subject: [PATCH 241/260] If a command has descriptive help, add a line above and below the print out for readability --- OpenSim/Framework/Console/CommandConsole.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs index 2008613717..b17dbc08de 100644 --- a/OpenSim/Framework/Console/CommandConsole.cs +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -141,7 +141,17 @@ namespace OpenSim.Framework.Console CommandInfo commandInfo = (CommandInfo)dict[String.Empty]; help.Add(commandInfo.help_text); help.Add(commandInfo.long_help); + + string descriptiveHelp = commandInfo.descriptive_help; + + // If we do have some descriptive help then insert a spacing line before and after for readability. + if (descriptiveHelp != string.Empty) + help.Add(string.Empty); + help.Add(commandInfo.descriptive_help); + + if (descriptiveHelp != string.Empty) + help.Add(string.Empty); } else { From 505cb82dee27fc1b681fd7c4b6f904ef18315995 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 21:14:15 +0100 Subject: [PATCH 242/260] fission UserAccountService.HandleCreateUser() into two methods, one which handles user command parsing and another which actually does the work --- .../Archiver/Tests/InventoryArchiverTests.cs | 102 ++++++----- OpenSim/Server/ServerMain.cs | 2 +- .../UserAccountService/UserAccountService.cs | 18 +- .../Common/Setup/UserProfileTestUtils.cs | 159 +++++++++--------- 4 files changed, 143 insertions(+), 138 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index 9c95e78f49..e434e46e46 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -38,7 +38,6 @@ using OpenSim.Framework; using OpenSim.Framework.Serialization; using OpenSim.Framework.Serialization.External; using OpenSim.Framework.Communications; - using OpenSim.Framework.Communications.Osp; using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; using OpenSim.Region.CoreModules.World.Serialiser; @@ -541,56 +540,55 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests /// /// Test replication of an archive path to the user's inventory. /// - //[Test] - //public void TestReplicateArchivePathToUserInventory() - //{ - // TestHelper.InMethod(); - - // //log4net.Config.XmlConfigurator.Configure(); - - // Scene scene = SceneSetupHelpers.SetupScene("inventory"); - // CommunicationsManager commsManager = scene.CommsManager; - // CachedUserInfo userInfo; - - // lock (this) - // { - // // !!! REFACTORING PROBLEM. This needs to be rewritten - // userInfo = UserProfileTestUtils.CreateUserWithInventory(commsManager, InventoryReceived); - // Monitor.Wait(this, 60000); - // } - - // //Console.WriteLine("userInfo.RootFolder 1: {0}", userInfo.RootFolder); - - // Dictionary foldersCreated = new Dictionary(); - // List nodesLoaded = new List(); - - // string folder1Name = "a"; - // string folder2Name = "b"; - // string itemName = "c.lsl"; - - // string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1Name, UUID.Random()); - // string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); - // string itemArchiveName = InventoryArchiveWriteRequest.CreateArchiveItemName(itemName, UUID.Random()); - - // string itemArchivePath - // = string.Format( - // "{0}{1}{2}{3}", - // ArchiveConstants.INVENTORY_PATH, folder1ArchiveName, folder2ArchiveName, itemArchiveName); - - // //Console.WriteLine("userInfo.RootFolder 2: {0}", userInfo.RootFolder); - - // new InventoryArchiveReadRequest(scene, userInfo, null, (Stream)null) - // .ReplicateArchivePathToUserInventory( - // itemArchivePath, false, scene.InventoryService.GetRootFolder(userInfo.UserProfile.ID), - // foldersCreated, nodesLoaded); - - // //Console.WriteLine("userInfo.RootFolder 3: {0}", userInfo.RootFolder); - // //InventoryFolderImpl folder1 = userInfo.RootFolder.FindFolderByPath("a"); - // InventoryFolderBase folder1 - // = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userInfo.UserProfile.ID, "a"); - // Assert.That(folder1, Is.Not.Null, "Could not find folder a"); - // InventoryFolderBase folder2 = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, "b"); - // Assert.That(folder2, Is.Not.Null, "Could not find folder b"); - //} +// [Test] +// public void TestReplicateArchivePathToUserInventory() +// { +// TestHelper.InMethod(); +// //log4net.Config.XmlConfigurator.Configure(); +// +// Scene scene = SceneSetupHelpers.SetupScene("inventory"); +// CommunicationsManager commsManager = scene.CommsManager; +// CachedUserInfo userInfo; +// +// lock (this) +// { +// // !!! REFACTORING PROBLEM. This needs to be rewritten +// userInfo = UserProfileTestUtils.CreateUserWithInventory(commsManager, InventoryReceived); +// Monitor.Wait(this, 60000); +// } +// +// //Console.WriteLine("userInfo.RootFolder 1: {0}", userInfo.RootFolder); +// +// Dictionary foldersCreated = new Dictionary(); +// List nodesLoaded = new List(); +// +// string folder1Name = "a"; +// string folder2Name = "b"; +// string itemName = "c.lsl"; +// +// string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1Name, UUID.Random()); +// string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); +// string itemArchiveName = InventoryArchiveWriteRequest.CreateArchiveItemName(itemName, UUID.Random()); +// +// string itemArchivePath +// = string.Format( +// "{0}{1}{2}{3}", +// ArchiveConstants.INVENTORY_PATH, folder1ArchiveName, folder2ArchiveName, itemArchiveName); +// +// //Console.WriteLine("userInfo.RootFolder 2: {0}", userInfo.RootFolder); +// +// new InventoryArchiveReadRequest(scene, userInfo, null, (Stream)null) +// .ReplicateArchivePathToUserInventory( +// itemArchivePath, false, scene.InventoryService.GetRootFolder(userInfo.UserProfile.ID), +// foldersCreated, nodesLoaded); +// +// //Console.WriteLine("userInfo.RootFolder 3: {0}", userInfo.RootFolder); +// //InventoryFolderImpl folder1 = userInfo.RootFolder.FindFolderByPath("a"); +// InventoryFolderBase folder1 +// = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userInfo.UserProfile.ID, "a"); +// Assert.That(folder1, Is.Not.Null, "Could not find folder a"); +// InventoryFolderBase folder2 = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, "b"); +// Assert.That(folder2, Is.Not.Null, "Could not find folder b"); +// } } } diff --git a/OpenSim/Server/ServerMain.cs b/OpenSim/Server/ServerMain.cs index d3e65a4bdd..9503c4cff6 100644 --- a/OpenSim/Server/ServerMain.cs +++ b/OpenSim/Server/ServerMain.cs @@ -61,7 +61,7 @@ namespace OpenSim.Server string connList = serverConfig.GetString("ServiceConnectors", String.Empty); string[] conns = connList.Split(new char[] {',', ' '}); - int i = 0; +// int i = 0; foreach (string c in conns) { if (c == String.Empty) diff --git a/OpenSim/Services/UserAccountService/UserAccountService.cs b/OpenSim/Services/UserAccountService/UserAccountService.cs index 6923293154..3f1744d83d 100644 --- a/OpenSim/Services/UserAccountService/UserAccountService.cs +++ b/OpenSim/Services/UserAccountService/UserAccountService.cs @@ -277,8 +277,9 @@ namespace OpenSim.Services.UserAccountService #endregion #region Console commands + /// - /// Create a new user + /// Handle the create user command from the console. /// /// string array with parameters: firstname, lastname, password, locationX, locationY, email protected void HandleCreateUser(string module, string[] cmdparams) @@ -304,6 +305,18 @@ namespace OpenSim.Services.UserAccountService email = MainConsole.Instance.CmdPrompt("Email", ""); else email = cmdparams[5]; + CreateUser(firstName, lastName, password, email); + } + + /// + /// Create a user + /// + /// + /// + /// + /// + public void CreateUser(string firstName, string lastName, string password, string email) + { UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); if (null == account) { @@ -338,7 +351,6 @@ namespace OpenSim.Services.UserAccountService else m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to set home for account {0} {1}.", firstName, lastName); - } else m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to retrieve home region for account {0} {1}.", @@ -350,7 +362,6 @@ namespace OpenSim.Services.UserAccountService m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to create inventory for account {0} {1}.", firstName, lastName); - m_log.InfoFormat("[USER ACCOUNT SERVICE]: Account {0} {1} created successfully", firstName, lastName); } } @@ -358,7 +369,6 @@ namespace OpenSim.Services.UserAccountService { m_log.ErrorFormat("[USER ACCOUNT SERVICE]: A user with the name {0} {1} already exists!", firstName, lastName); } - } protected void HandleResetUserPassword(string module, string[] cmdparams) diff --git a/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs b/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs index cd61fa6d2d..2b14d97591 100644 --- a/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs +++ b/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs @@ -36,85 +36,82 @@ namespace OpenSim.Tests.Common.Setup /// public static class UserProfileTestUtils { - // REFACTORING PROBLEM - // This needs to be rewritten - - ///// - ///// Create a test user with a standard inventory - ///// - ///// - ///// - ///// Callback to invoke when inventory has been loaded. This is required because - ///// loading may be asynchronous, even on standalone - ///// - ///// - //public static CachedUserInfo CreateUserWithInventory( - // CommunicationsManager commsManager, OnInventoryReceivedDelegate callback) - //{ - // UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000099"); - // return CreateUserWithInventory(commsManager, userId, callback); - //} - - ///// - ///// Create a test user with a standard inventory - ///// - ///// - ///// User ID - ///// - ///// Callback to invoke when inventory has been loaded. This is required because - ///// loading may be asynchronous, even on standalone - ///// - ///// - //public static CachedUserInfo CreateUserWithInventory( - // CommunicationsManager commsManager, UUID userId, OnInventoryReceivedDelegate callback) - //{ - // return CreateUserWithInventory(commsManager, "Bill", "Bailey", userId, callback); - //} - - ///// - ///// Create a test user with a standard inventory - ///// - ///// - ///// First name of user - ///// Last name of user - ///// User ID - ///// - ///// Callback to invoke when inventory has been loaded. This is required because - ///// loading may be asynchronous, even on standalone - ///// - ///// - //public static CachedUserInfo CreateUserWithInventory( - // CommunicationsManager commsManager, string firstName, string lastName, - // UUID userId, OnInventoryReceivedDelegate callback) - //{ - // return CreateUserWithInventory(commsManager, firstName, lastName, "troll", userId, callback); - //} - - ///// - ///// Create a test user with a standard inventory - ///// - ///// - ///// First name of user - ///// Last name of user - ///// Password - ///// User ID - ///// - ///// Callback to invoke when inventory has been loaded. This is required because - ///// loading may be asynchronous, even on standalone - ///// - ///// - //public static CachedUserInfo CreateUserWithInventory( - // CommunicationsManager commsManager, string firstName, string lastName, string password, - // UUID userId, OnInventoryReceivedDelegate callback) - //{ - // LocalUserServices lus = (LocalUserServices)commsManager.UserService; - // lus.AddUser(firstName, lastName, password, "bill@bailey.com", 1000, 1000, userId); - - // CachedUserInfo userInfo = commsManager.UserProfileCacheService.GetUserDetails(userId); - // userInfo.OnInventoryReceived += callback; - // userInfo.FetchInventory(); - - // return userInfo; - //} +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, OnInventoryReceivedDelegate callback) +// { +// UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000099"); +// return CreateUserWithInventory(commsManager, userId, callback); +// } +// +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// User ID +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, UUID userId, OnInventoryReceivedDelegate callback) +// { +// return CreateUserWithInventory(commsManager, "Bill", "Bailey", userId, callback); +// } +// +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// First name of user +// /// Last name of user +// /// User ID +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, string firstName, string lastName, +// UUID userId, OnInventoryReceivedDelegate callback) +// { +// return CreateUserWithInventory(commsManager, firstName, lastName, "troll", userId, callback); +// } +// +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// First name of user +// /// Last name of user +// /// Password +// /// User ID +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, string firstName, string lastName, string password, +// UUID userId, OnInventoryReceivedDelegate callback) +// { +// LocalUserServices lus = (LocalUserServices)commsManager.UserService; +// lus.AddUser(firstName, lastName, password, "bill@bailey.com", 1000, 1000, userId); +// +// CachedUserInfo userInfo = commsManager.UserProfileCacheService.GetUserDetails(userId); +// userInfo.OnInventoryReceived += callback; +// userInfo.FetchInventory(); +// +// return userInfo; +// } } -} +} \ No newline at end of file From 0ef41e62bc1721dc0f6807979d24c609a9d3df6e Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 21:18:55 +0100 Subject: [PATCH 243/260] minor: move a method so that the #regions make more sense --- .../UserAccountService/UserAccountService.cs | 71 +++++++++---------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/OpenSim/Services/UserAccountService/UserAccountService.cs b/OpenSim/Services/UserAccountService/UserAccountService.cs index 3f1744d83d..eb588f0145 100644 --- a/OpenSim/Services/UserAccountService/UserAccountService.cs +++ b/OpenSim/Services/UserAccountService/UserAccountService.cs @@ -308,6 +308,40 @@ namespace OpenSim.Services.UserAccountService CreateUser(firstName, lastName, password, email); } + protected void HandleResetUserPassword(string module, string[] cmdparams) + { + string firstName; + string lastName; + string newPassword; + + if (cmdparams.Length < 4) + firstName = MainConsole.Instance.CmdPrompt("First name"); + else firstName = cmdparams[3]; + + if (cmdparams.Length < 5) + lastName = MainConsole.Instance.CmdPrompt("Last name"); + else lastName = cmdparams[4]; + + if (cmdparams.Length < 6) + newPassword = MainConsole.Instance.PasswdPrompt("New password"); + else newPassword = cmdparams[5]; + + UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); + if (account == null) + m_log.ErrorFormat("[USER ACCOUNT SERVICE]: No such user"); + + bool success = false; + if (m_AuthenticationService != null) + success = m_AuthenticationService.SetPassword(account.PrincipalID, newPassword); + if (!success) + m_log.ErrorFormat("[USER ACCOUNT SERVICE]: Unable to reset password for account {0} {1}.", + firstName, lastName); + else + m_log.InfoFormat("[USER ACCOUNT SERVICE]: Password reset for user {0} {1}", firstName, lastName); + } + + #endregion + /// /// Create a user /// @@ -369,41 +403,6 @@ namespace OpenSim.Services.UserAccountService { m_log.ErrorFormat("[USER ACCOUNT SERVICE]: A user with the name {0} {1} already exists!", firstName, lastName); } - } - - protected void HandleResetUserPassword(string module, string[] cmdparams) - { - string firstName; - string lastName; - string newPassword; - - if (cmdparams.Length < 4) - firstName = MainConsole.Instance.CmdPrompt("First name"); - else firstName = cmdparams[3]; - - if (cmdparams.Length < 5) - lastName = MainConsole.Instance.CmdPrompt("Last name"); - else lastName = cmdparams[4]; - - if (cmdparams.Length < 6) - newPassword = MainConsole.Instance.PasswdPrompt("New password"); - else newPassword = cmdparams[5]; - - UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); - if (account == null) - m_log.ErrorFormat("[USER ACCOUNT SERVICE]: No such user"); - - bool success = false; - if (m_AuthenticationService != null) - success = m_AuthenticationService.SetPassword(account.PrincipalID, newPassword); - if (!success) - m_log.ErrorFormat("[USER ACCOUNT SERVICE]: Unable to reset password for account {0} {1}.", - firstName, lastName); - else - m_log.InfoFormat("[USER ACCOUNT SERVICE]: Password reset for user {0} {1}", firstName, lastName); - } - - #endregion - + } } } From a60ca5236c247b88909a3c0462675627575b334c Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 21:37:48 +0100 Subject: [PATCH 244/260] restore InventoryArchiverTests.TestReplicateArchivePathToUserInventory() to work with the new UserAccountService/InventoryService --- .../Archiver/Tests/InventoryArchiverTests.cs | 88 ++++++++----------- .../Common/Setup/UserProfileTestUtils.cs | 13 ++- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index e434e46e46..d3395435a4 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -540,55 +540,41 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests /// /// Test replication of an archive path to the user's inventory. /// -// [Test] -// public void TestReplicateArchivePathToUserInventory() -// { -// TestHelper.InMethod(); -// //log4net.Config.XmlConfigurator.Configure(); -// -// Scene scene = SceneSetupHelpers.SetupScene("inventory"); -// CommunicationsManager commsManager = scene.CommsManager; -// CachedUserInfo userInfo; -// -// lock (this) -// { -// // !!! REFACTORING PROBLEM. This needs to be rewritten -// userInfo = UserProfileTestUtils.CreateUserWithInventory(commsManager, InventoryReceived); -// Monitor.Wait(this, 60000); -// } -// -// //Console.WriteLine("userInfo.RootFolder 1: {0}", userInfo.RootFolder); -// -// Dictionary foldersCreated = new Dictionary(); -// List nodesLoaded = new List(); -// -// string folder1Name = "a"; -// string folder2Name = "b"; -// string itemName = "c.lsl"; -// -// string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1Name, UUID.Random()); -// string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); -// string itemArchiveName = InventoryArchiveWriteRequest.CreateArchiveItemName(itemName, UUID.Random()); -// -// string itemArchivePath -// = string.Format( -// "{0}{1}{2}{3}", -// ArchiveConstants.INVENTORY_PATH, folder1ArchiveName, folder2ArchiveName, itemArchiveName); -// -// //Console.WriteLine("userInfo.RootFolder 2: {0}", userInfo.RootFolder); -// -// new InventoryArchiveReadRequest(scene, userInfo, null, (Stream)null) -// .ReplicateArchivePathToUserInventory( -// itemArchivePath, false, scene.InventoryService.GetRootFolder(userInfo.UserProfile.ID), -// foldersCreated, nodesLoaded); -// -// //Console.WriteLine("userInfo.RootFolder 3: {0}", userInfo.RootFolder); -// //InventoryFolderImpl folder1 = userInfo.RootFolder.FindFolderByPath("a"); -// InventoryFolderBase folder1 -// = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userInfo.UserProfile.ID, "a"); -// Assert.That(folder1, Is.Not.Null, "Could not find folder a"); -// InventoryFolderBase folder2 = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, "b"); -// Assert.That(folder2, Is.Not.Null, "Could not find folder b"); -// } + [Test] + public void TestReplicateArchivePathToUserInventory() + { + TestHelper.InMethod(); + //log4net.Config.XmlConfigurator.Configure(); + + Scene scene = SceneSetupHelpers.SetupScene("inventory"); + UserAccount ua1 = UserProfileTestUtils.CreateUserWithInventory(scene); + + Dictionary foldersCreated = new Dictionary(); + List nodesLoaded = new List(); + + string folder1Name = "a"; + string folder2Name = "b"; + string itemName = "c.lsl"; + + string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1Name, UUID.Random()); + string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); + string itemArchiveName = InventoryArchiveWriteRequest.CreateArchiveItemName(itemName, UUID.Random()); + + string itemArchivePath + = string.Format( + "{0}{1}{2}{3}", + ArchiveConstants.INVENTORY_PATH, folder1ArchiveName, folder2ArchiveName, itemArchiveName); + + new InventoryArchiveReadRequest(scene, ua1, null, (Stream)null) + .ReplicateArchivePathToUserInventory( + itemArchivePath, false, scene.InventoryService.GetRootFolder(ua1.PrincipalID), + foldersCreated, nodesLoaded); + + InventoryFolderBase folder1 + = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, ua1.PrincipalID, "a"); + Assert.That(folder1, Is.Not.Null, "Could not find folder a"); + InventoryFolderBase folder2 = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, "b"); + Assert.That(folder2, Is.Not.Null, "Could not find folder b"); + } } -} +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs b/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs index 2b14d97591..a6b95207b1 100644 --- a/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs +++ b/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs @@ -27,7 +27,8 @@ using OpenMetaverse; using OpenSim.Framework.Communications; - +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; namespace OpenSim.Tests.Common.Setup { @@ -113,5 +114,15 @@ namespace OpenSim.Tests.Common.Setup // // return userInfo; // } + + public static UserAccount CreateUserWithInventory(Scene scene) + { + UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000099"); + UserAccount ua = new UserAccount(userId) { FirstName = "Bill", LastName = "Bailey" }; + scene.UserAccountService.StoreUserAccount(ua); + scene.InventoryService.CreateUserInventory(ua.PrincipalID); + + return ua; + } } } \ No newline at end of file From 191db0e6a4327e8bf84350a541a9edc579ae1c54 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 28 May 2010 23:14:24 +0100 Subject: [PATCH 245/260] get TestSaveIarV0_1() uncommented but not running as a test yet since I didn't get the authentication server to work and my brain is about to fizzle out my ears --- .../Archiver/Tests/InventoryArchiverTests.cs | 189 +++++++++--------- OpenSim/Region/Framework/Scenes/SceneBase.cs | 2 + .../Tests/Common/Setup/SceneSetupHelpers.cs | 28 ++- .../Common/Setup/UserProfileTestUtils.cs | 13 +- 4 files changed, 130 insertions(+), 102 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs index d3395435a4..c81f295d8c 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs @@ -76,125 +76,118 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests // Commenting for now! The mock inventory service needs more beef, at least for // GetFolderForType // REFACTORING PROBLEM. This needs to be rewritten. + //[Test] + public void TestSaveIarV0_1() + { + TestHelper.InMethod(); + log4net.Config.XmlConfigurator.Configure(); -// [Test] -// public void TestSaveIarV0_1() -// { -// TestHelper.InMethod(); -// //log4net.Config.XmlConfigurator.Configure(); + InventoryArchiverModule archiverModule = new InventoryArchiverModule(true); -// InventoryArchiverModule archiverModule = new InventoryArchiverModule(true); + Scene scene = SceneSetupHelpers.SetupScene("Inventory"); + SceneSetupHelpers.SetupSceneModules(scene, archiverModule); -// Scene scene = SceneSetupHelpers.SetupScene("Inventory"); -// SceneSetupHelpers.SetupSceneModules(scene, archiverModule); -// CommunicationsManager cm = scene.CommsManager; - -// // Create user -// string userFirstName = "Jock"; -// string userLastName = "Stirrup"; -// UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); - -// lock (this) -// { -// UserProfileTestUtils.CreateUserWithInventory( -// cm, userFirstName, userLastName, userId, InventoryReceived); -// Monitor.Wait(this, 60000); -// } + // Create user + string userFirstName = "Jock"; + string userLastName = "Stirrup"; + string userPassword = "troll"; + UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); + UserProfileTestUtils.CreateUserWithInventory(scene, userFirstName, userLastName, userId, userPassword); -// // Create asset -// SceneObjectGroup object1; -// SceneObjectPart part1; -// { -// string partName = "My Little Dog Object"; -// UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); -// PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); -// Vector3 groupPosition = new Vector3(10, 20, 30); -// Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); -// Vector3 offsetPosition = new Vector3(5, 10, 15); + // Create asset + SceneObjectGroup object1; + SceneObjectPart part1; + { + string partName = "My Little Dog Object"; + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); + Vector3 groupPosition = new Vector3(10, 20, 30); + Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 offsetPosition = new Vector3(5, 10, 15); -// part1 -// = new SceneObjectPart( -// ownerId, shape, groupPosition, rotationOffset, offsetPosition); -// part1.Name = partName; + part1 + = new SceneObjectPart( + ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Name = partName; -// object1 = new SceneObjectGroup(part1); -// scene.AddNewSceneObject(object1, false); -// } + object1 = new SceneObjectGroup(part1); + scene.AddNewSceneObject(object1, false); + } -// UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); -// AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); -// scene.AssetService.Store(asset1); + UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); + AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); + scene.AssetService.Store(asset1); -// // Create item -// UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); -// InventoryItemBase item1 = new InventoryItemBase(); -// item1.Name = "My Little Dog"; -// item1.AssetID = asset1.FullID; -// item1.ID = item1Id; -// InventoryFolderBase objsFolder -// = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userId, "Objects"); -// item1.Folder = objsFolder.ID; -// scene.AddInventoryItem(userId, item1); + // Create item + UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); + InventoryItemBase item1 = new InventoryItemBase(); + item1.Name = "My Little Dog"; + item1.AssetID = asset1.FullID; + item1.ID = item1Id; + InventoryFolderBase objsFolder + = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userId, "Objects"); + item1.Folder = objsFolder.ID; + scene.AddInventoryItem(userId, item1); -// MemoryStream archiveWriteStream = new MemoryStream(); -// archiverModule.OnInventoryArchiveSaved += SaveCompleted; + MemoryStream archiveWriteStream = new MemoryStream(); + archiverModule.OnInventoryArchiveSaved += SaveCompleted; -// mre.Reset(); -// archiverModule.ArchiveInventory( -// Guid.NewGuid(), userFirstName, userLastName, "Objects", "troll", archiveWriteStream); -// mre.WaitOne(60000, false); + mre.Reset(); + archiverModule.ArchiveInventory( + Guid.NewGuid(), userFirstName, userLastName, "Objects", userPassword, archiveWriteStream); + mre.WaitOne(60000, false); -// byte[] archive = archiveWriteStream.ToArray(); -// MemoryStream archiveReadStream = new MemoryStream(archive); -// TarArchiveReader tar = new TarArchiveReader(archiveReadStream); + byte[] archive = archiveWriteStream.ToArray(); + MemoryStream archiveReadStream = new MemoryStream(archive); + TarArchiveReader tar = new TarArchiveReader(archiveReadStream); -// //bool gotControlFile = false; -// bool gotObject1File = false; -// //bool gotObject2File = false; -// string expectedObject1FileName = InventoryArchiveWriteRequest.CreateArchiveItemName(item1); -// string expectedObject1FilePath = string.Format( -// "{0}{1}{2}", -// ArchiveConstants.INVENTORY_PATH, -// InventoryArchiveWriteRequest.CreateArchiveFolderName(objsFolder), -// expectedObject1FileName); + //bool gotControlFile = false; + bool gotObject1File = false; + //bool gotObject2File = false; + string expectedObject1FileName = InventoryArchiveWriteRequest.CreateArchiveItemName(item1); + string expectedObject1FilePath = string.Format( + "{0}{1}{2}", + ArchiveConstants.INVENTORY_PATH, + InventoryArchiveWriteRequest.CreateArchiveFolderName(objsFolder), + expectedObject1FileName); -// string filePath; -// TarArchiveReader.TarEntryType tarEntryType; + string filePath; + TarArchiveReader.TarEntryType tarEntryType; // Console.WriteLine("Reading archive"); -// while (tar.ReadEntry(out filePath, out tarEntryType) != null) -// { -// Console.WriteLine("Got {0}", filePath); + while (tar.ReadEntry(out filePath, out tarEntryType) != null) + { + Console.WriteLine("Got {0}", filePath); -//// if (ArchiveConstants.CONTROL_FILE_PATH == filePath) -//// { -//// gotControlFile = true; -//// } - -// if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH) && filePath.EndsWith(".xml")) +// if (ArchiveConstants.CONTROL_FILE_PATH == filePath) // { -//// string fileName = filePath.Remove(0, "Objects/".Length); -//// -//// if (fileName.StartsWith(part1.Name)) -//// { -// Assert.That(expectedObject1FilePath, Is.EqualTo(filePath)); -// gotObject1File = true; -//// } -//// else if (fileName.StartsWith(part2.Name)) -//// { -//// Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); -//// gotObject2File = true; -//// } +// gotControlFile = true; // } -// } + + if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH) && filePath.EndsWith(".xml")) + { +// string fileName = filePath.Remove(0, "Objects/".Length); +// +// if (fileName.StartsWith(part1.Name)) +// { + Assert.That(expectedObject1FilePath, Is.EqualTo(filePath)); + gotObject1File = true; +// } +// else if (fileName.StartsWith(part2.Name)) +// { +// Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); +// gotObject2File = true; +// } + } + } -//// Assert.That(gotControlFile, Is.True, "No control file in archive"); -// Assert.That(gotObject1File, Is.True, "No item1 file in archive"); -//// Assert.That(gotObject2File, Is.True, "No object2 file in archive"); +// Assert.That(gotControlFile, Is.True, "No control file in archive"); + Assert.That(gotObject1File, Is.True, "No item1 file in archive"); +// Assert.That(gotObject2File, Is.True, "No object2 file in archive"); -// // TODO: Test presence of more files and contents of files. -// } + // TODO: Test presence of more files and contents of files. + } /// /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index c363a91fc2..ee17fbf8ed 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -376,6 +376,8 @@ namespace OpenSim.Region.Framework.Scenes /// public void RegisterModuleInterface(M mod) { + m_log.DebugFormat("[SCENE BASE]: Registering interface {0}", typeof(M)); + List l = null; if (!ModuleInterfaces.TryGetValue(typeof(M), out l)) { diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 91cf323fcb..2756324a43 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -42,6 +42,7 @@ using OpenSim.Region.Framework.Scenes; using OpenSim.Region.CoreModules.Agent.Capabilities; using OpenSim.Region.CoreModules.Avatar.Gods; using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Authentication; using OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory; using OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid; using OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts; @@ -58,6 +59,7 @@ namespace OpenSim.Tests.Common.Setup // These static variables in order to allow regions to be linked by shared modules and same // CommunicationsManager. private static ISharedRegionModule m_assetService = null; +// private static ISharedRegionModule m_authenticationService = null; private static ISharedRegionModule m_inventoryService = null; private static ISharedRegionModule m_gridService = null; private static ISharedRegionModule m_userAccountService = null; @@ -177,6 +179,9 @@ namespace OpenSim.Tests.Common.Setup StartAssetService(testScene, true); else StartAssetService(testScene, false); + + // For now, always started a 'real' authenication service + StartAuthenticationService(testScene, true); if (realServices.Contains("inventory")) StartInventoryService(testScene, true); @@ -236,13 +241,34 @@ namespace OpenSim.Tests.Common.Setup else config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Tests.Common.dll:MockAssetService"); config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); - assetService.Initialise(config); + assetService.Initialise(config); assetService.AddRegion(testScene); assetService.RegionLoaded(testScene); testScene.AddRegionModule(assetService.Name, assetService); m_assetService = assetService; } + private static void StartAuthenticationService(Scene testScene, bool real) + { + ISharedRegionModule service = new LocalAuthenticationServicesConnector(); + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.AddConfig("AuthenticationService"); + config.Configs["Modules"].Set("AuthenticationServices", "LocalAuthenticationServicesConnector"); + if (real) + config.Configs["AuthenticationService"].Set( + "LocalServiceModule", "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService"); + else + config.Configs["AuthenticationService"].Set( + "LocalServiceModule", "OpenSim.Tests.Common.dll:MockuthenticationService"); + config.Configs["AuthenticationService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + service.Initialise(config); + service.AddRegion(testScene); + service.RegionLoaded(testScene); + testScene.AddRegionModule(service.Name, service); + //m_authenticationService = service; + } + private static void StartInventoryService(Scene testScene, bool real) { ISharedRegionModule inventoryService = new LocalInventoryServicesConnector(); diff --git a/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs b/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs index a6b95207b1..e6a78182b2 100644 --- a/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs +++ b/OpenSim/Tests/Common/Setup/UserProfileTestUtils.cs @@ -117,12 +117,19 @@ namespace OpenSim.Tests.Common.Setup public static UserAccount CreateUserWithInventory(Scene scene) { - UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000099"); - UserAccount ua = new UserAccount(userId) { FirstName = "Bill", LastName = "Bailey" }; + return CreateUserWithInventory( + scene, "Bill", "Bailey", UUID.Parse("00000000-0000-0000-0000-000000000099"), "troll"); + } + + public static UserAccount CreateUserWithInventory( + Scene scene, string firstName, string lastName, UUID userId, string pw) + { + UserAccount ua = new UserAccount(userId) { FirstName = firstName, LastName = lastName }; scene.UserAccountService.StoreUserAccount(ua); scene.InventoryService.CreateUserInventory(ua.PrincipalID); + scene.AuthenticationService.SetPassword(ua.PrincipalID, pw); return ua; - } + } } } \ No newline at end of file From cd77648f489a08b52093513e0ad3d7965104755d Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Sat, 29 May 2010 05:14:18 +0200 Subject: [PATCH 246/260] Get the user's DOB back from the server response properly. --- OpenSim/Services/Interfaces/IUserAccountService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/Interfaces/IUserAccountService.cs b/OpenSim/Services/Interfaces/IUserAccountService.cs index e316731a52..09d1d874f0 100644 --- a/OpenSim/Services/Interfaces/IUserAccountService.cs +++ b/OpenSim/Services/Interfaces/IUserAccountService.cs @@ -91,7 +91,7 @@ namespace OpenSim.Services.Interfaces UserTitle = kvp["UserTitle"].ToString(); if (kvp.ContainsKey("Created")) - Convert.ToInt32(kvp["Created"].ToString()); + Created = Convert.ToInt32(kvp["Created"].ToString()); if (kvp.ContainsKey("ServiceURLs") && kvp["ServiceURLs"] != null) { ServiceURLs = new Dictionary(); From bfcac0ede824ead5b6809f03eab73450f48075db Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Sun, 30 May 2010 13:46:05 +0200 Subject: [PATCH 247/260] Changes OSSL Api permissions for the case of UUID list. In 0.6.9, the UUIDs would be the IDs of the prim owners in whose prims these functions would run. This changes it so the UUID is the SCRIPT CREATOR instead. Further, osfunctions limited by uuid will not run if the creator and owner differ and the owner has mod rights on the script. There is still a danger in passing moodifiable scripts to others, as they can insert a harmful function, then remove the mod rights to make it runnable. As before, care needs to be taken, but where it was modable prims that were the risk before, modable scripts are the weak spot now. In cases where prim owner == script creator == script owner, nothing will change. --- .../Shared/Api/Implementation/OSSL_Api.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 15469db94f..5b634e0269 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -278,10 +278,25 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (!m_FunctionPerms[function].Contains(UUID.Zero)) { - if (!m_FunctionPerms[function].Contains(m_host.OwnerID)) + TaskInventoryItem ti = m_host.Inventory.GetInventoryItem(m_itemID); + if (ti == null) + { OSSLError( - String.Format("{0} permission denied. Prim owner is not in the list of users allowed to execute this function.", + String.Format("{0} permission error. Can't find script in prim inventory.", function)); + } + if (!m_FunctionPerms[function].Contains(ti.CreatorID)) + OSSLError( + String.Format("{0} permission denied. Script creator is not in the list of users allowed to execute this function.", + function)); + if (ti.CreatorID != ti.OwnerID) + { + if ((ti.CurrentPermissions & (uint)PermissionMask.Modify) != 0) + OSSLError( + String.Format("{0} permission denied. Script permissions error.", + function)); + + } } } } @@ -2137,4 +2152,4 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } -} \ No newline at end of file +} From f1a1d7a5211a250aeb4ed540562be0c79f051e4b Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Sun, 30 May 2010 15:46:54 +0200 Subject: [PATCH 248/260] Changes osFunction permissions again. Allow_ with a list of UUIDs now again refers to prim OWNERS. A new option set, Creators_, is added to allow selection by script creator. For existing installs, this means no functional change. The warning from my prior commit doesn't apply anymore. --- .../Shared/Api/Implementation/OSSL_Api.cs | 69 ++++++++++++++----- bin/OpenSim.ini.example | 6 ++ 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 5b634e0269..7ada738084 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -105,6 +105,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // modification of user data, or allows the compromise of // sensitive data by design. + class FunctionPerms + { + public List AllowedCreators; + public List AllowedOwners; + + public FunctionPerms() + { + AllowedCreators = new List(); + AllowedOwners = new List(); + } + } + [Serializable] public class OSSL_Api : MarshalByRefObject, IOSSL_Api, IScriptApi { @@ -117,7 +129,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api internal ThreatLevel m_MaxThreatLevel = ThreatLevel.VeryLow; internal float m_ScriptDelayFactor = 1.0f; internal float m_ScriptDistanceFactor = 1.0f; - internal Dictionary > m_FunctionPerms = new Dictionary >(); + internal Dictionary m_FunctionPerms = new Dictionary(); public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, uint localID, UUID itemID) { @@ -217,31 +229,33 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (!m_FunctionPerms.ContainsKey(function)) { - string perm = m_ScriptEngine.Config.GetString("Allow_" + function, ""); - if (perm == "") + FunctionPerms perms = new FunctionPerms(); + m_FunctionPerms[function] = perms; + + string ownerPerm = m_ScriptEngine.Config.GetString("Allow_" + function, ""); + string creatorPerm = m_ScriptEngine.Config.GetString("Creators_" + function, ""); + if (ownerPerm == "" && creatorPerm == "") { - m_FunctionPerms[function] = null; // a null value is default + // Default behavior + perms.AllowedOwners = null; + perms.AllowedCreators = null; } else { bool allowed; - if (bool.TryParse(perm, out allowed)) + if (bool.TryParse(ownerPerm, out allowed)) { // Boolean given if (allowed) { - m_FunctionPerms[function] = new List(); - m_FunctionPerms[function].Add(UUID.Zero); + // Allow globally + perms.AllowedOwners.Add(UUID.Zero); } - else - m_FunctionPerms[function] = new List(); // Empty list = none } else { - m_FunctionPerms[function] = new List(); - - string[] ids = perm.Split(new char[] {','}); + string[] ids = ownerPerm.Split(new char[] {','}); foreach (string id in ids) { string current = id.Trim(); @@ -250,7 +264,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (UUID.TryParse(current, out uuid)) { if (uuid != UUID.Zero) - m_FunctionPerms[function].Add(uuid); + perms.AllowedOwners.Add(uuid); + } + } + + ids = creatorPerm.Split(new char[] {','}); + foreach (string id in ids) + { + string current = id.Trim(); + UUID uuid; + + if (UUID.TryParse(current, out uuid)) + { + if (uuid != UUID.Zero) + perms.AllowedCreators.Add(uuid); } } } @@ -266,8 +293,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // // To allow use by anyone, the list contains UUID.Zero // - if (m_FunctionPerms[function] == null) // No list = true + if (m_FunctionPerms[function].AllowedOwners == null) { + // Allow / disallow by threat level if (level > m_MaxThreatLevel) OSSLError( String.Format( @@ -276,8 +304,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - if (!m_FunctionPerms[function].Contains(UUID.Zero)) + if (!m_FunctionPerms[function].AllowedOwners.Contains(UUID.Zero)) { + // Not anyone. Do detailed checks + if (m_FunctionPerms[function].AllowedOwners.Contains(m_host.OwnerID)) + { + // prim owner is in the list of allowed owners + return; + } + TaskInventoryItem ti = m_host.Inventory.GetInventoryItem(m_itemID); if (ti == null) { @@ -285,9 +320,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api String.Format("{0} permission error. Can't find script in prim inventory.", function)); } - if (!m_FunctionPerms[function].Contains(ti.CreatorID)) + if (!m_FunctionPerms[function].AllowedCreators.Contains(ti.CreatorID)) OSSLError( - String.Format("{0} permission denied. Script creator is not in the list of users allowed to execute this function.", + String.Format("{0} permission denied. Script creator is not in the list of users allowed to execute this function and prim owner also has no permission.", function)); if (ti.CreatorID != ti.OwnerID) { diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 7b427a5fd2..2a70e9668e 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -979,6 +979,12 @@ ; Comma separated list of UUIDS allows the function for that list of UUIDS ; Allow_osSetRegionWaterHeight = 888760cb-a3cf-43ac-8ea4-8732fd3ee2bb + ; You can also use script creators as the uuid + ; Creators_osSetRegionWaterHeight = , ... + + ; If both Allow_ and Creators_ are given, effective permissions + ; are the union of the two. + ; Allow for llCreateLink and llBreakLink to work without asking for permission ; only enable this in a trusted environment otherwise you may be subject to hijacking ; AutomaticLinkPermission = false From 8df9f272eb0bd25c8965453ef8a88e3c998eeec8 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 31 May 2010 01:02:04 +0200 Subject: [PATCH 249/260] Fix a nullref in EventManager caused by RegionReady not setting the scene --- .../Scripting/RegionReadyModule/RegionReadyModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs index c653e98401..672109b773 100644 --- a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs @@ -146,6 +146,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady c.Position = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 30); c.Sender = null; c.SenderUUID = UUID.Zero; + c.Scene = m_scene; m_log.InfoFormat("[RegionReady]: Region \"{0}\" is ready: \"{1}\" on channel {2}", m_scene.RegionInfo.RegionName, c.Message, m_channelNotify); From e515467c5e9a03de8d0df8e24d1ca70636f6e099 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Mon, 31 May 2010 19:00:02 +0200 Subject: [PATCH 250/260] Fix create selection getting overwritten by multiple updates for the same prim. --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 4 ++-- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 2 +- OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs | 2 +- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 9 ++++++++- .../ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 0945bce5ae..a516a54ca4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4558,11 +4558,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (recipientID == data.OwnerID) { - if ((data.Flags & PrimFlags.CreateSelected) != 0) + if (data.CreateSelected) { // Only send this flag once, then unset it flags |= PrimFlags.CreateSelected; - data.Flags &= ~PrimFlags.CreateSelected; + data.CreateSelected = false; } } diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index e923a92566..a02f614387 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -1500,7 +1500,7 @@ namespace OpenSim.Region.Framework.Scenes // We need to explicitly resend the newly link prim's object properties since no other actions // occur on link to invoke this elsewhere (such as object selection) - parentGroup.RootPart.AddFlag(PrimFlags.CreateSelected); + parentGroup.RootPart.CreateSelected = true; parentGroup.TriggerScriptChangedEvent(Changed.LINK); parentGroup.HasGroupChanged = true; parentGroup.ScheduleGroupForFullUpdate(); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 4453bebec8..ab7e3e99f6 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -2254,7 +2254,7 @@ namespace OpenSim.Region.Framework.Scenes linkPart.LinkNum = 2; linkPart.SetParent(this); - linkPart.AddFlag(PrimFlags.CreateSelected); + linkPart.CreateSelected = true; //if (linkPart.PhysActor != null) //{ diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index b36b9bfff2..ce1972fd91 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -387,7 +387,6 @@ namespace OpenSim.Region.Framework.Scenes // the prim into an agent inventory (Linden client reports that the "Object not found for drop" in its log _flags = 0; - _flags |= PrimFlags.CreateSelected; TrimPermissions(); //m_undo = new UndoStack(ParentGroup.GetSceneMaxUndo()); @@ -417,6 +416,7 @@ namespace OpenSim.Region.Framework.Scenes private PrimFlags _flags = 0; private DateTime m_expires; private DateTime m_rezzed; + private bool m_createSelected = true; public UUID CreatorID { @@ -967,6 +967,13 @@ namespace OpenSim.Region.Framework.Scenes set { m_updateFlag = value; } } + [XmlIgnore] + public bool CreateSelected + { + get { return m_createSelected; } + set { m_createSelected = value; } + } + #endregion //--------------- diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 7a9a92d175..6e9a823c87 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -3506,7 +3506,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } parentPrim.TriggerScriptChangedEvent(Changed.LINK); - parentPrim.RootPart.AddFlag(PrimFlags.CreateSelected); + parentPrim.RootPart.CreateSelected = true; parentPrim.HasGroupChanged = true; parentPrim.ScheduleGroupForFullUpdate(); From be69259981f0452f1f7fe7d084fde0f4bc2789cf Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 02:45:14 +0200 Subject: [PATCH 251/260] Change the handling of CreateSelected. Only send it on real creation, not for each prim coming into view. --- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index ce1972fd91..38b2dc2f68 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -387,6 +387,7 @@ namespace OpenSim.Region.Framework.Scenes // the prim into an agent inventory (Linden client reports that the "Object not found for drop" in its log _flags = 0; + CreateSelected = true; TrimPermissions(); //m_undo = new UndoStack(ParentGroup.GetSceneMaxUndo()); @@ -416,7 +417,7 @@ namespace OpenSim.Region.Framework.Scenes private PrimFlags _flags = 0; private DateTime m_expires; private DateTime m_rezzed; - private bool m_createSelected = true; + private bool m_createSelected = false; public UUID CreatorID { From 2fce7d9bcf001094e3d88516e7ea44e9a077da1b Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 01:07:46 +0200 Subject: [PATCH 252/260] Split GetAxisAlignedBoundingBox into two methods to allow calculation of combined bounding boxes and offsets --- .../Framework/Scenes/SceneObjectGroup.cs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ab7e3e99f6..0ad05e77b8 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -652,10 +652,16 @@ namespace OpenSim.Region.Framework.Scenes /// offsetHeight is the offset in the Z axis from the centre of the bounding box to the centre of the root prim /// /// - public Vector3 GetAxisAlignedBoundingBox(out float offsetHeight) + public void GetAxisAlignedBoundingBoxRaw(out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ) { - float maxX = -256f, maxY = -256f, maxZ = -256f, minX = 256f, minY = 256f, minZ = 256f; - lock (m_parts) + maxX = -256f; + maxY = -256f; + maxZ = -256f; + minX = 256f; + minY = 256f; + minZ = 256f; + + lock(m_parts); { foreach (SceneObjectPart part in m_parts.Values) { @@ -888,7 +894,18 @@ namespace OpenSim.Region.Framework.Scenes minZ = backBottomLeft.Z; } } + } + public Vector3 GetAxisAlignedBoundingBox(out float offsetHeight) + { + float minX; + float maxX; + float minY; + float maxY; + float minZ; + float maxZ; + + GetAxisAlignedBoundingBoxRaw(out minX, out maxX, out minY, out maxY, out minZ, out maxZ); Vector3 boundingBox = new Vector3(maxX - minX, maxY - minY, maxZ - minZ); offsetHeight = 0; From bde01e26e159e3e10fd606e316ab68a199af5934 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 01:25:24 +0200 Subject: [PATCH 253/260] Add a method to get the bounding box and root prim offsets within it for a group of prims. --- OpenSim/Region/Framework/Scenes/Scene.cs | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 6d4de90f20..6300665b29 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -5189,5 +5189,49 @@ namespace OpenSim.Region.Framework.Scenes ReloadEstateData(); } } + + public Vector3[] GetCombinedBoundingBox(List objects, out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ) + { + minX = 256; + maxX = -256; + minY = 256; + maxY = -256; + minZ = 8192; + maxZ = -256; + + List offsets = new List(); + + foreach (SceneObjectGroup g in objects) + { + float ominX, ominY, ominZ, omaxX, omaxY, omaxZ; + + g.GetAxisAlignedBoundingBoxRaw(out ominX, out omaxX, out ominY, out omaxY, out ominZ, out omaxZ); + + if (minX > ominX) + minX = ominX; + if (minY > ominY) + minY = ominY; + if (minZ > ominZ) + minZ = ominZ; + if (maxX < omaxX) + maxX = omaxX; + if (maxY < omaxY) + maxY = omaxY; + if (maxZ < omaxZ) + maxZ = omaxZ; + } + + foreach (SceneObjectGroup g in objects) + { + Vector3 vec = g.AbsolutePosition; + vec.X -= minX; + vec.Y -= minY; + vec.Z -= minZ; + + offsets.Add(vec); + } + + return offsets.ToArray(); + } } } From f29cb57bf183c0530ead35890163f39903c8f410 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 02:27:30 +0200 Subject: [PATCH 254/260] Continuing refactor. Refactor DeRezObject to deal with multiple objects --- .../HGInventoryAccessModule.cs | 17 +- .../InventoryAccess/InventoryAccessModule.cs | 14 ++ .../Interfaces/IInventoryAccessModule.cs | 2 +- .../Scenes/AsyncSceneObjectGroupDeleter.cs | 18 ++- .../Framework/Scenes/Scene.Inventory.cs | 146 ++++++++++-------- 5 files changed, 122 insertions(+), 75 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs index 93aeb9440f..2ab46aa33f 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs @@ -119,9 +119,22 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess /// /// DeleteToInventory /// - public override UUID DeleteToInventory(DeRezAction action, UUID folderID, SceneObjectGroup objectGroup, IClientAPI remoteClient) + public override UUID DeleteToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient) { - UUID assetID = base.DeleteToInventory(action, folderID, objectGroup, remoteClient); + UUID ret = UUID.Zero; + + // HACK: Only works for lists of length one. + // Intermediate version, just to make things compile + foreach (SceneObjectGroup g in objectGroups) + ret = DeleteToInventory(action, folderID, g, remoteClient); + + return ret; + } + + public virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, + SceneObjectGroup objectGroup, IClientAPI remoteClient) + { + UUID assetID = base.DeleteToInventory(action, folderID, new List() {objectGroup}, remoteClient); if (!assetID.Equals(UUID.Zero)) { diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 901dcba707..3035d889e7 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -188,6 +188,20 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess /// /// /// + public virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, + List objectGroups, IClientAPI remoteClient) + { + // HACK: This is only working for lists containing a single item! + // It's just a hack to make this WIP compile and run. Nothing + // currently calls this with multiple items. + UUID ret = UUID.Zero; + + foreach (SceneObjectGroup g in objectGroups) + ret = DeleteToInventory(action, folderID, g, remoteClient); + + return ret; + } + public virtual UUID DeleteToInventory(DeRezAction action, UUID folderID, SceneObjectGroup objectGroup, IClientAPI remoteClient) { diff --git a/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs b/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs index 81852583c6..97f4188751 100644 --- a/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs @@ -38,7 +38,7 @@ namespace OpenSim.Region.Framework.Interfaces public interface IInventoryAccessModule { UUID CapsUpdateInventoryItemAsset(IClientAPI remoteClient, UUID itemID, byte[] data); - UUID DeleteToInventory(DeRezAction action, UUID folderID, SceneObjectGroup objectGroup, IClientAPI remoteClient); + UUID DeleteToInventory(DeRezAction action, UUID folderID, List objectGroups, IClientAPI remoteClient); SceneObjectGroup RezObject(IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, bool RezSelected, bool RemoveItem, UUID fromTaskID, bool attachment); diff --git a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs index c08b961373..241cac0f11 100644 --- a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs +++ b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs @@ -40,7 +40,7 @@ namespace OpenSim.Region.Framework.Scenes { public DeRezAction action; public IClientAPI remoteClient; - public SceneObjectGroup objectGroup; + public List objectGroups; public UUID folderID; public bool permissionToDelete; } @@ -75,7 +75,7 @@ namespace OpenSim.Region.Framework.Scenes /// Delete the given object from the scene /// public void DeleteToInventory(DeRezAction action, UUID folderID, - SceneObjectGroup objectGroup, IClientAPI remoteClient, + List objectGroups, IClientAPI remoteClient, bool permissionToDelete) { if (Enabled) @@ -87,7 +87,7 @@ namespace OpenSim.Region.Framework.Scenes DeleteToInventoryHolder dtis = new DeleteToInventoryHolder(); dtis.action = action; dtis.folderID = folderID; - dtis.objectGroup = objectGroup; + dtis.objectGroups = objectGroups; dtis.remoteClient = remoteClient; dtis.permissionToDelete = permissionToDelete; @@ -103,7 +103,10 @@ namespace OpenSim.Region.Framework.Scenes // This is not ideal since the object will still be available for manipulation when it should be, but it's // better than losing the object for now. if (permissionToDelete) - objectGroup.DeleteGroup(false); + { + foreach (SceneObjectGroup g in objectGroups) + g.DeleteGroup(false); + } } private void InventoryRunDeleteTimer(object sender, ElapsedEventArgs e) @@ -140,9 +143,12 @@ namespace OpenSim.Region.Framework.Scenes { IInventoryAccessModule invAccess = m_scene.RequestModuleInterface(); if (invAccess != null) - invAccess.DeleteToInventory(x.action, x.folderID, x.objectGroup, x.remoteClient); + invAccess.DeleteToInventory(x.action, x.folderID, x.objectGroups, x.remoteClient); if (x.permissionToDelete) - m_scene.DeleteSceneObject(x.objectGroup, false); + { + foreach (SceneObjectGroup g in x.objectGroups) + m_scene.DeleteSceneObject(g, false); + } } catch (Exception e) { diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 96f22a4255..bad92a00cd 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1531,79 +1531,98 @@ namespace OpenSim.Region.Framework.Scenes public virtual void DeRezObject(IClientAPI remoteClient, uint localID, UUID groupID, DeRezAction action, UUID destinationID) { - SceneObjectPart part = GetSceneObjectPart(localID); - if (part == null) - return; + DeRezObjects(remoteClient, new List() { localID} , groupID, action, destinationID); + } - if (part.ParentGroup == null || part.ParentGroup.IsDeleted) - return; + public virtual void DeRezObjects(IClientAPI remoteClient, List localIDs, + UUID groupID, DeRezAction action, UUID destinationID) + { + // First, see of we can perform the requested action and + // build a list of eligible objects + List deleteIDs = new List(); + List deleteGroups = new List(); - // Can't delete child prims - if (part != part.ParentGroup.RootPart) - return; + // Start with true for both, then remove the flags if objects + // that we can't derez are part of the selection + bool permissionToTake = true; + bool permissionToTakeCopy = true; + bool permissionToDelete = true; - SceneObjectGroup grp = part.ParentGroup; - - //force a database backup/update on this SceneObjectGroup - //So that we know the database is upto date, for when deleting the object from it - ForceSceneObjectBackup(grp); - - bool permissionToTake = false; - bool permissionToDelete = false; - - if (action == DeRezAction.SaveToExistingUserInventoryItem) + foreach (uint localID in localIDs) { - if (grp.OwnerID == remoteClient.AgentId && grp.RootPart.FromUserInventoryItemID != UUID.Zero) - { - permissionToTake = true; + // Invalid id + SceneObjectPart part = GetSceneObjectPart(localID); + if (part == null) + continue; + + // Already deleted by someone else + if (part.ParentGroup == null || part.ParentGroup.IsDeleted) + continue; + + // Can't delete child prims + if (part != part.ParentGroup.RootPart) + continue; + + SceneObjectGroup grp = part.ParentGroup; + + deleteIDs.Add(localID); + deleteGroups.Add(grp); + + // Force a database backup/update on this SceneObjectGroup + // So that we know the database is upto date, + // for when deleting the object from it + ForceSceneObjectBackup(grp); + + if (!Permissions.CanTakeCopyObject(grp.UUID, remoteClient.AgentId)) + permissionToTakeCopy = false; + if (!Permissions.CanTakeObject(grp.UUID, remoteClient.AgentId)) + permissionToTake = false; + + if (!Permissions.CanDeleteObject(grp.UUID, remoteClient.AgentId)) permissionToDelete = false; - } - } - else if (action == DeRezAction.TakeCopy) - { - permissionToTake = - Permissions.CanTakeCopyObject( - grp.UUID, - remoteClient.AgentId); - } - else if (action == DeRezAction.GodTakeCopy) - { - permissionToTake = - Permissions.IsGod( - remoteClient.AgentId); - } - else if (action == DeRezAction.Take) - { - permissionToTake = - Permissions.CanTakeObject( - grp.UUID, - remoteClient.AgentId); - //If they can take, they can delete! - permissionToDelete = permissionToTake; } - else if (action == DeRezAction.Delete) + + // Handle god perms + if (Permissions.IsGod(remoteClient.AgentId)) { - permissionToTake = - Permissions.CanDeleteObject( - grp.UUID, - remoteClient.AgentId); - permissionToDelete = permissionToTake; + permissionToTake = true; + permissionToTakeCopy = true; + permissionToDelete = true; } - else if (action == DeRezAction.Return) + + // If we're re-saving, we don't even want to delete + if (action == DeRezAction.SaveToExistingUserInventoryItem) + permissionToDelete = false; + + // if we want to take a copy,, we also don't want to delete + // Note: after this point, the permissionToTakeCopy flag + // becomes irrelevant. It already includes the permissionToTake + // permission and after excluding no copy items here, we can + // just use that. + if (action == DeRezAction.TakeCopy) + { + // If we don't have permission, stop right here + if (!permissionToTakeCopy) + return; + + // Don't delete + permissionToDelete = false; + } + + if (action == DeRezAction.Return) { if (remoteClient != null) { - permissionToTake = - Permissions.CanReturnObjects( + if (Permissions.CanReturnObjects( null, remoteClient.AgentId, - new List() {grp}); - permissionToDelete = permissionToTake; - - if (permissionToDelete) + deleteGroups)) + foreach (SceneObjectGroup g in deleteGroups) { - AddReturn(grp.OwnerID, grp.Name, grp.AbsolutePosition, "parcel owner return"); + AddReturn(g.OwnerID, g.Name, g.AbsolutePosition, "parcel owner return"); + DeleteSceneObject(g, false); + return; } } else // Auto return passes through here with null agent @@ -1612,22 +1631,17 @@ namespace OpenSim.Region.Framework.Scenes permissionToDelete = true; } } - else - { - m_log.DebugFormat( - "[AGENT INVENTORY]: Ignoring unexpected derez action {0} for {1}", action, remoteClient.Name); - return; - } if (permissionToTake) { m_asyncSceneObjectDeleter.DeleteToInventory( - action, destinationID, grp, remoteClient, + action, destinationID, deleteGroups, remoteClient, permissionToDelete); } else if (permissionToDelete) { - DeleteSceneObject(grp, false); + foreach (SceneObjectGroup g in deleteGroups) + DeleteSceneObject(g, false); } } From a5728cc91c6737e6abef75c4b1da2f78a8317d84 Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 03:04:49 +0200 Subject: [PATCH 255/260] Fix prim returns I broke earlier --- OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index bad92a00cd..cc7b6485f7 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1618,11 +1618,14 @@ namespace OpenSim.Region.Framework.Scenes null, remoteClient.AgentId, deleteGroups)) - foreach (SceneObjectGroup g in deleteGroups) { - AddReturn(g.OwnerID, g.Name, g.AbsolutePosition, "parcel owner return"); - DeleteSceneObject(g, false); - return; + permissionToTake = true; + permissionToDelete = true; + + foreach (SceneObjectGroup g in deleteGroups) + { + AddReturn(g.OwnerID, g.Name, g.AbsolutePosition, "parcel owner return"); + } } } else // Auto return passes through here with null agent From 4867dd135dcdfece6af653906cb3bb088412110c Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 03:04:49 +0200 Subject: [PATCH 256/260] Fix prim returns I broke earlier --- OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 0ad05e77b8..837d3a2142 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -659,7 +659,7 @@ namespace OpenSim.Region.Framework.Scenes maxZ = -256f; minX = 256f; minY = 256f; - minZ = 256f; + minZ = 8192f; lock(m_parts); { From 5270e5426867877e84e4c148759dad8c20de76ea Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 1 Jun 2010 14:19:26 +0100 Subject: [PATCH 257/260] Comment and remove JScript support. Mono 2.7Dev and 2.8 no longer include the needed libraries --- .../ScriptEngine/Shared/CodeTools/Compiler.cs | 38 +++++++++---------- prebuild.xml | 7 ---- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs index f7196835bb..cd8c67e601 100644 --- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs +++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Compiler.cs @@ -32,7 +32,7 @@ using System.Globalization; using System.Reflection; using System.IO; using Microsoft.CSharp; -using Microsoft.JScript; +//using Microsoft.JScript; using Microsoft.VisualBasic; using log4net; using OpenSim.Region.Framework.Interfaces; @@ -82,7 +82,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools private static CSharpCodeProvider CScodeProvider = new CSharpCodeProvider(); private static VBCodeProvider VBcodeProvider = new VBCodeProvider(); - private static JScriptCodeProvider JScodeProvider = new JScriptCodeProvider(); +// private static JScriptCodeProvider JScodeProvider = new JScriptCodeProvider(); private static CSharpCodeProvider YPcodeProvider = new CSharpCodeProvider(); // YP is translated into CSharp private static YP2CSConverter YP_Converter = new YP2CSConverter(); @@ -398,9 +398,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools case enumCompileType.vb: compileScript = CreateVBCompilerScript(compileScript); break; - case enumCompileType.js: - compileScript = CreateJSCompilerScript(compileScript); - break; +// case enumCompileType.js: +// compileScript = CreateJSCompilerScript(compileScript); +// break; case enumCompileType.yp: compileScript = CreateYPCompilerScript(compileScript); break; @@ -423,16 +423,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools } } - private static string CreateJSCompilerScript(string compileScript) - { - compileScript = String.Empty + - "import OpenSim.Region.ScriptEngine.Shared; import System.Collections.Generic;\r\n" + - "package SecondLife {\r\n" + - "class Script extends OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" + - compileScript + - "} }\r\n"; - return compileScript; - } +// private static string CreateJSCompilerScript(string compileScript) +// { +// compileScript = String.Empty + +// "import OpenSim.Region.ScriptEngine.Shared; import System.Collections.Generic;\r\n" + +// "package SecondLife {\r\n" + +// "class Script extends OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" + +// compileScript + +// "} }\r\n"; +// return compileScript; +// } private static string CreateCSCompilerScript(string compileScript) { @@ -583,10 +583,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools } } while (!complete); break; - case enumCompileType.js: - results = JScodeProvider.CompileAssemblyFromSource( - parameters, Script); - break; +// case enumCompileType.js: +// results = JScodeProvider.CompileAssemblyFromSource( +// parameters, Script); +// break; case enumCompileType.yp: results = YPcodeProvider.CompileAssemblyFromSource( parameters, Script); diff --git a/prebuild.xml b/prebuild.xml index c0e06bffe6..1eb8fee64b 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -767,7 +767,6 @@ - @@ -1406,7 +1405,6 @@ - @@ -1489,7 +1487,6 @@ - @@ -2417,7 +2414,6 @@ ../../../../../bin/ - @@ -2554,7 +2550,6 @@ - @@ -2969,7 +2964,6 @@ - @@ -3033,7 +3027,6 @@ - From 9c3c020697cf5710781bb6e491a88bea72652c87 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 1 Jun 2010 15:08:45 +0100 Subject: [PATCH 258/260] Lock the object queue when dequeueing --- .../Region/Framework/Scenes/SceneViewer.cs | 175 +++++++++--------- 1 file changed, 89 insertions(+), 86 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneViewer.cs b/OpenSim/Region/Framework/Scenes/SceneViewer.cs index 15bc33d172..5cbd8d94ed 100644 --- a/OpenSim/Region/Framework/Scenes/SceneViewer.cs +++ b/OpenSim/Region/Framework/Scenes/SceneViewer.cs @@ -67,105 +67,108 @@ namespace OpenSim.Region.Framework.Scenes public void SendPrimUpdates() { - if (m_pendingObjects == null) + lock(m_pendingObjects) { - if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor)) + if (m_pendingObjects == null) { - m_pendingObjects = new Queue(); - - foreach (EntityBase e in m_presence.Scene.Entities) + if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor)) { - if (e is SceneObjectGroup) - m_pendingObjects.Enqueue((SceneObjectGroup)e); + m_pendingObjects = new Queue(); + + foreach (EntityBase e in m_presence.Scene.Entities) + { + if (e != null && e is SceneObjectGroup) + m_pendingObjects.Enqueue((SceneObjectGroup)e); + } } } - } - while (m_pendingObjects != null && m_pendingObjects.Count > 0) - { - SceneObjectGroup g = m_pendingObjects.Dequeue(); - // Yes, this can really happen - if (g == null) - continue; - - // This is where we should check for draw distance - // do culling and stuff. Problem with that is that until - // we recheck in movement, that won't work right. - // So it's not implemented now. - // - - // Don't even queue if we have sent this one - // - if (!m_updateTimes.ContainsKey(g.UUID)) - g.ScheduleFullUpdateToAvatar(m_presence); - } - - while (m_partsUpdateQueue.Count > 0) - { - SceneObjectPart part = m_partsUpdateQueue.Dequeue(); - - if (part.ParentGroup == null || part.ParentGroup.IsDeleted) - continue; - - if (m_updateTimes.ContainsKey(part.UUID)) + while (m_pendingObjects != null && m_pendingObjects.Count > 0) { - ScenePartUpdate update = m_updateTimes[part.UUID]; + SceneObjectGroup g = m_pendingObjects.Dequeue(); + // Yes, this can really happen + if (g == null) + continue; - // We deal with the possibility that two updates occur at - // the same unix time at the update point itself. + // This is where we should check for draw distance + // do culling and stuff. Problem with that is that until + // we recheck in movement, that won't work right. + // So it's not implemented now. + // - if ((update.LastFullUpdateTime < part.TimeStampFull) || - part.IsAttachment) + // Don't even queue if we have sent this one + // + if (!m_updateTimes.ContainsKey(g.UUID)) + g.ScheduleFullUpdateToAvatar(m_presence); + } + + while (m_partsUpdateQueue.Count > 0) + { + SceneObjectPart part = m_partsUpdateQueue.Dequeue(); + + if (part.ParentGroup == null || part.ParentGroup.IsDeleted) + continue; + + if (m_updateTimes.ContainsKey(part.UUID)) { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Fully updating prim {0}, {1} - part timestamp {2}", -// part.Name, part.UUID, part.TimeStampFull); + ScenePartUpdate update = m_updateTimes[part.UUID]; + + // We deal with the possibility that two updates occur at + // the same unix time at the update point itself. + + if ((update.LastFullUpdateTime < part.TimeStampFull) || + part.IsAttachment) + { + // m_log.DebugFormat( + // "[SCENE PRESENCE]: Fully updating prim {0}, {1} - part timestamp {2}", + // part.Name, part.UUID, part.TimeStampFull); + + part.SendFullUpdate(m_presence.ControllingClient, + m_presence.GenerateClientFlags(part.UUID)); + + // We'll update to the part's timestamp rather than + // the current time to avoid the race condition + // whereby the next tick occurs while we are doing + // this update. If this happened, then subsequent + // updates which occurred on the same tick or the + // next tick of the last update would be ignored. + + update.LastFullUpdateTime = part.TimeStampFull; + + } + else if (update.LastTerseUpdateTime <= part.TimeStampTerse) + { + // m_log.DebugFormat( + // "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}", + // part.Name, part.UUID, part.TimeStampTerse); + + part.SendTerseUpdateToClient(m_presence.ControllingClient); + + update.LastTerseUpdateTime = part.TimeStampTerse; + } + } + else + { + //never been sent to client before so do full update + ScenePartUpdate update = new ScenePartUpdate(); + update.FullID = part.UUID; + update.LastFullUpdateTime = part.TimeStampFull; + m_updateTimes.Add(part.UUID, update); + + // Attachment handling + // + if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0) + { + if (part != part.ParentGroup.RootPart) + continue; + + part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient); + continue; + } part.SendFullUpdate(m_presence.ControllingClient, - m_presence.GenerateClientFlags(part.UUID)); - - // We'll update to the part's timestamp rather than - // the current time to avoid the race condition - // whereby the next tick occurs while we are doing - // this update. If this happened, then subsequent - // updates which occurred on the same tick or the - // next tick of the last update would be ignored. - - update.LastFullUpdateTime = part.TimeStampFull; - + m_presence.GenerateClientFlags(part.UUID)); } - else if (update.LastTerseUpdateTime <= part.TimeStampTerse) - { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}", -// part.Name, part.UUID, part.TimeStampTerse); - - part.SendTerseUpdateToClient(m_presence.ControllingClient); - - update.LastTerseUpdateTime = part.TimeStampTerse; - } - } - else - { - //never been sent to client before so do full update - ScenePartUpdate update = new ScenePartUpdate(); - update.FullID = part.UUID; - update.LastFullUpdateTime = part.TimeStampFull; - m_updateTimes.Add(part.UUID, update); - - // Attachment handling - // - if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0) - { - if (part != part.ParentGroup.RootPart) - continue; - - part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient); - continue; - } - - part.SendFullUpdate(m_presence.ControllingClient, - m_presence.GenerateClientFlags(part.UUID)); } } } From a863eb9da3eba3644933b7622239dde641e7050c Mon Sep 17 00:00:00 2001 From: Melanie Thielker Date: Tue, 1 Jun 2010 19:01:21 +0200 Subject: [PATCH 259/260] One should not lock null objects. --- OpenSim/Region/Framework/Scenes/SceneViewer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneViewer.cs b/OpenSim/Region/Framework/Scenes/SceneViewer.cs index 5cbd8d94ed..f478a4a770 100644 --- a/OpenSim/Region/Framework/Scenes/SceneViewer.cs +++ b/OpenSim/Region/Framework/Scenes/SceneViewer.cs @@ -67,14 +67,14 @@ namespace OpenSim.Region.Framework.Scenes public void SendPrimUpdates() { - lock(m_pendingObjects) + if (m_pendingObjects == null) { - if (m_pendingObjects == null) + if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor)) { - if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor)) - { - m_pendingObjects = new Queue(); + m_pendingObjects = new Queue(); + lock(m_pendingObjects) + { foreach (EntityBase e in m_presence.Scene.Entities) { if (e != null && e is SceneObjectGroup) @@ -82,7 +82,10 @@ namespace OpenSim.Region.Framework.Scenes } } } + } + lock(m_pendingObjects) + { while (m_pendingObjects != null && m_pendingObjects.Count > 0) { SceneObjectGroup g = m_pendingObjects.Dequeue(); From d740035ef4414cc3543251d43c74ebb68fe08baf Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 1 Jun 2010 13:32:14 -0700 Subject: [PATCH 260/260] Applying patch from coyled to fix IAR support with the SimianGrid connectors --- .../SimianGrid/SimianAuthenticationServiceConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs index b19135e0c5..de3ee4ea56 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs @@ -116,7 +116,7 @@ namespace OpenSim.Services.Connectors.SimianGrid { string credential = identity["Credential"].AsString(); - if (password == credential || "$1$" + Utils.MD5String(password) == credential || Utils.MD5String(password) == credential) + if (password == credential || "$1$" + password == credential || "$1$" + Utils.MD5String(password) == credential || Utils.MD5String(password) == credential) return Authorize(principalID); md5hashFound = true;