diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/World')
67 files changed, 15719 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs new file mode 100644 index 0000000..179d1a2 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveConstants.cs | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Collections.Generic; | ||
29 | using OpenMetaverse; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
32 | { | ||
33 | /// <summary> | ||
34 | /// Constants for the archiving module | ||
35 | /// </summary> | ||
36 | public class ArchiveConstants | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// The location of the archive control file | ||
40 | /// </summary> | ||
41 | public static readonly string CONTROL_FILE_PATH = "archive.xml"; | ||
42 | |||
43 | /// <summary> | ||
44 | /// Path for the assets held in an archive | ||
45 | /// </summary> | ||
46 | public static readonly string ASSETS_PATH = "assets/"; | ||
47 | |||
48 | /// <summary> | ||
49 | /// Path for the assets metadata file | ||
50 | /// </summary> | ||
51 | //public static readonly string ASSETS_METADATA_PATH = "assets.xml"; | ||
52 | |||
53 | /// <summary> | ||
54 | /// Path for the prims file | ||
55 | /// </summary> | ||
56 | public static readonly string OBJECTS_PATH = "objects/"; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Path for terrains. Technically these may be assets, but I think it's quite nice to split them out. | ||
60 | /// </summary> | ||
61 | public static readonly string TERRAINS_PATH = "terrains/"; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Path for region settings. | ||
65 | /// </summary> | ||
66 | public static readonly string SETTINGS_PATH = "settings/"; | ||
67 | |||
68 | /// <summary> | ||
69 | /// The character the separates the uuid from extension information in an archived asset filename | ||
70 | /// </summary> | ||
71 | public static readonly string ASSET_EXTENSION_SEPARATOR = "_"; | ||
72 | |||
73 | /// <summary> | ||
74 | /// Extensions used for asset types in the archive | ||
75 | /// </summary> | ||
76 | public static readonly IDictionary<sbyte, string> ASSET_TYPE_TO_EXTENSION = new Dictionary<sbyte, string>(); | ||
77 | public static readonly IDictionary<string, sbyte> EXTENSION_TO_ASSET_TYPE = new Dictionary<string, sbyte>(); | ||
78 | |||
79 | static ArchiveConstants() | ||
80 | { | ||
81 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Animation] = ASSET_EXTENSION_SEPARATOR + "animation.bvh"; | ||
82 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Bodypart] = ASSET_EXTENSION_SEPARATOR + "bodypart.txt"; | ||
83 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.CallingCard] = ASSET_EXTENSION_SEPARATOR + "callingcard.txt"; | ||
84 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Clothing] = ASSET_EXTENSION_SEPARATOR + "clothing.txt"; | ||
85 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Folder] = ASSET_EXTENSION_SEPARATOR + "folder.txt"; // Not sure if we'll ever see this | ||
86 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Gesture] = ASSET_EXTENSION_SEPARATOR + "gesture.txt"; | ||
87 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageJPEG] = ASSET_EXTENSION_SEPARATOR + "image.jpg"; | ||
88 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageTGA] = ASSET_EXTENSION_SEPARATOR + "image.tga"; | ||
89 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Landmark] = ASSET_EXTENSION_SEPARATOR + "landmark.txt"; | ||
90 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LostAndFoundFolder] = ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"; // Not sure if we'll ever see this | ||
91 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLBytecode] = ASSET_EXTENSION_SEPARATOR + "bytecode.lso"; | ||
92 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLText] = ASSET_EXTENSION_SEPARATOR + "script.lsl"; | ||
93 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Notecard] = ASSET_EXTENSION_SEPARATOR + "notecard.txt"; | ||
94 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Object] = ASSET_EXTENSION_SEPARATOR + "object.xml"; | ||
95 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.RootFolder] = ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"; // Not sure if we'll ever see this | ||
96 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Simstate] = ASSET_EXTENSION_SEPARATOR + "simstate.bin"; // Not sure if we'll ever see this | ||
97 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SnapshotFolder] = ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"; // Not sure if we'll ever see this | ||
98 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Sound] = ASSET_EXTENSION_SEPARATOR + "sound.ogg"; | ||
99 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV] = ASSET_EXTENSION_SEPARATOR + "sound.wav"; | ||
100 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2"; | ||
101 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga"; | ||
102 | ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TrashFolder] = ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"; // Not sure if we'll ever see this | ||
103 | |||
104 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = (sbyte)AssetType.Animation; | ||
105 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = (sbyte)AssetType.Bodypart; | ||
106 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "callingcard.txt"] = (sbyte)AssetType.CallingCard; | ||
107 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "clothing.txt"] = (sbyte)AssetType.Clothing; | ||
108 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "folder.txt"] = (sbyte)AssetType.Folder; | ||
109 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "gesture.txt"] = (sbyte)AssetType.Gesture; | ||
110 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.jpg"] = (sbyte)AssetType.ImageJPEG; | ||
111 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.tga"] = (sbyte)AssetType.ImageTGA; | ||
112 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "landmark.txt"] = (sbyte)AssetType.Landmark; | ||
113 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"] = (sbyte)AssetType.LostAndFoundFolder; | ||
114 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bytecode.lso"] = (sbyte)AssetType.LSLBytecode; | ||
115 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "script.lsl"] = (sbyte)AssetType.LSLText; | ||
116 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "notecard.txt"] = (sbyte)AssetType.Notecard; | ||
117 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "object.xml"] = (sbyte)AssetType.Object; | ||
118 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"] = (sbyte)AssetType.RootFolder; | ||
119 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "simstate.bin"] = (sbyte)AssetType.Simstate; | ||
120 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"] = (sbyte)AssetType.SnapshotFolder; | ||
121 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.ogg"] = (sbyte)AssetType.Sound; | ||
122 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.wav"] = (sbyte)AssetType.SoundWAV; | ||
123 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = (sbyte)AssetType.Texture; | ||
124 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA; | ||
125 | EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; | ||
126 | } | ||
127 | } | ||
128 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs new file mode 100644 index 0000000..3218abc --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs | |||
@@ -0,0 +1,460 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.IO.Compression; | ||
32 | using System.Reflection; | ||
33 | using System.Xml; | ||
34 | using System.Net; | ||
35 | using OpenMetaverse; | ||
36 | using log4net; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Communications.Cache; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | using OpenSim.Region.CoreModules.World.Terrain; | ||
42 | |||
43 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
44 | { | ||
45 | /// <summary> | ||
46 | /// Handles an individual archive read request | ||
47 | /// </summary> | ||
48 | public class ArchiveReadRequest | ||
49 | { | ||
50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
51 | |||
52 | private static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding(); | ||
53 | |||
54 | private Scene m_scene; | ||
55 | private Stream m_loadStream; | ||
56 | private string m_errorMessage; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Used to cache lookups for valid uuids. | ||
60 | /// </summary> | ||
61 | private IDictionary<UUID, bool> m_validUserUuids = new Dictionary<UUID, bool>(); | ||
62 | |||
63 | public ArchiveReadRequest(Scene scene, string loadPath) | ||
64 | { | ||
65 | m_scene = scene; | ||
66 | m_loadStream = new GZipStream(GetStream(loadPath), CompressionMode.Decompress); | ||
67 | m_errorMessage = String.Empty; | ||
68 | } | ||
69 | |||
70 | public ArchiveReadRequest(Scene scene, Stream loadStream) | ||
71 | { | ||
72 | m_scene = scene; | ||
73 | m_loadStream = loadStream; | ||
74 | } | ||
75 | |||
76 | /// <summary> | ||
77 | /// Dearchive the region embodied in this request. | ||
78 | /// </summary> | ||
79 | public void DearchiveRegion() | ||
80 | { | ||
81 | // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions | ||
82 | DearchiveRegion0DotStar(); | ||
83 | } | ||
84 | |||
85 | private void DearchiveRegion0DotStar() | ||
86 | { | ||
87 | int successfulAssetRestores = 0; | ||
88 | int failedAssetRestores = 0; | ||
89 | List<string> serialisedSceneObjects = new List<string>(); | ||
90 | |||
91 | try | ||
92 | { | ||
93 | TarArchiveReader archive = new TarArchiveReader(m_loadStream); | ||
94 | |||
95 | //AssetsDearchiver dearchiver = new AssetsDearchiver(m_scene.AssetCache); | ||
96 | |||
97 | string filePath = "ERROR"; | ||
98 | |||
99 | byte[] data; | ||
100 | TarArchiveReader.TarEntryType entryType; | ||
101 | |||
102 | while ((data = archive.ReadEntry(out filePath, out entryType)) != null) | ||
103 | { | ||
104 | //m_log.DebugFormat( | ||
105 | // "[ARCHIVER]: Successfully read {0} ({1} bytes)}", filePath, data.Length); | ||
106 | if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType) | ||
107 | { | ||
108 | m_log.WarnFormat("[ARCHIVER]: Ignoring directory entry {0}", | ||
109 | filePath); | ||
110 | } | ||
111 | else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) | ||
112 | { | ||
113 | serialisedSceneObjects.Add(m_asciiEncoding.GetString(data)); | ||
114 | } | ||
115 | // else if (filePath.Equals(ArchiveConstants.ASSETS_METADATA_PATH)) | ||
116 | // { | ||
117 | // string xml = m_asciiEncoding.GetString(data); | ||
118 | // dearchiver.AddAssetMetadata(xml); | ||
119 | // } | ||
120 | else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH)) | ||
121 | { | ||
122 | if (LoadAsset(filePath, data)) | ||
123 | successfulAssetRestores++; | ||
124 | else | ||
125 | failedAssetRestores++; | ||
126 | } | ||
127 | else if (filePath.StartsWith(ArchiveConstants.TERRAINS_PATH)) | ||
128 | { | ||
129 | LoadTerrain(filePath, data); | ||
130 | } | ||
131 | else if (filePath.StartsWith(ArchiveConstants.SETTINGS_PATH)) | ||
132 | { | ||
133 | LoadRegionSettings(filePath, data); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | //m_log.Debug("[ARCHIVER]: Reached end of archive"); | ||
138 | |||
139 | archive.Close(); | ||
140 | } | ||
141 | catch (Exception e) | ||
142 | { | ||
143 | m_log.ErrorFormat( | ||
144 | "[ARCHIVER]: Error loading oar file. Exception was: {0}", e); | ||
145 | m_errorMessage += e.ToString(); | ||
146 | m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage); | ||
147 | return; | ||
148 | } | ||
149 | |||
150 | m_log.InfoFormat("[ARCHIVER]: Restored {0} assets", successfulAssetRestores); | ||
151 | |||
152 | if (failedAssetRestores > 0) | ||
153 | { | ||
154 | m_log.ErrorFormat("[ARCHIVER]: Failed to load {0} assets", failedAssetRestores); | ||
155 | m_errorMessage += String.Format("Failed to load {0} assets", failedAssetRestores); | ||
156 | } | ||
157 | |||
158 | m_log.Info("[ARCHIVER]: Clearing all existing scene objects"); | ||
159 | m_scene.DeleteAllSceneObjects(); | ||
160 | |||
161 | // Reload serialized prims | ||
162 | m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count); | ||
163 | |||
164 | IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface<IRegionSerialiserModule>(); | ||
165 | ICollection<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | ||
166 | |||
167 | foreach (string serialisedSceneObject in serialisedSceneObjects) | ||
168 | { | ||
169 | SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject); | ||
170 | |||
171 | // For now, give all incoming scene objects new uuids. This will allow scenes to be cloned | ||
172 | // on the same region server and multiple examples a single object archive to be imported | ||
173 | // to the same scene (when this is possible). | ||
174 | sceneObject.ResetIDs(); | ||
175 | |||
176 | // Try to retain the original creator/owner/lastowner if their uuid is present on this grid | ||
177 | // otherwise, use the master avatar uuid instead | ||
178 | UUID masterAvatarId = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
179 | |||
180 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
181 | masterAvatarId = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
182 | |||
183 | foreach (SceneObjectPart part in sceneObject.Children.Values) | ||
184 | { | ||
185 | if (!resolveUserUuid(part.CreatorID)) | ||
186 | part.CreatorID = masterAvatarId; | ||
187 | |||
188 | if (!resolveUserUuid(part.OwnerID)) | ||
189 | part.OwnerID = masterAvatarId; | ||
190 | |||
191 | if (!resolveUserUuid(part.LastOwnerID)) | ||
192 | part.LastOwnerID = masterAvatarId; | ||
193 | |||
194 | // And zap any troublesome sit target information | ||
195 | part.SitTargetOrientation = new Quaternion(0, 0, 0, 1); | ||
196 | part.SitTargetPosition = new Vector3(0, 0, 0); | ||
197 | |||
198 | // Fix ownership/creator of inventory items | ||
199 | // Not doing so results in inventory items | ||
200 | // being no copy/no mod for everyone | ||
201 | TaskInventoryDictionary inv = part.TaskInventory; | ||
202 | foreach (KeyValuePair<UUID, TaskInventoryItem> kvp in inv) | ||
203 | { | ||
204 | if (!resolveUserUuid(kvp.Value.OwnerID)) | ||
205 | { | ||
206 | kvp.Value.OwnerID = masterAvatarId; | ||
207 | } | ||
208 | if (!resolveUserUuid(kvp.Value.CreatorID)) | ||
209 | { | ||
210 | kvp.Value.CreatorID = masterAvatarId; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | if (m_scene.AddRestoredSceneObject(sceneObject, true, false)) | ||
216 | { | ||
217 | sceneObjects.Add(sceneObject); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | m_log.InfoFormat("[ARCHIVER]: Restored {0} scene objects to the scene", sceneObjects.Count); | ||
222 | |||
223 | int ignoredObjects = serialisedSceneObjects.Count - sceneObjects.Count; | ||
224 | |||
225 | if (ignoredObjects > 0) | ||
226 | m_log.WarnFormat("[ARCHIVER]: Ignored {0} scene objects that already existed in the scene", ignoredObjects); | ||
227 | |||
228 | m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive"); | ||
229 | |||
230 | m_log.Debug("[ARCHIVER]: Starting scripts"); | ||
231 | |||
232 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
233 | { | ||
234 | sceneObject.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 0); | ||
235 | } | ||
236 | |||
237 | m_scene.EventManager.TriggerOarFileLoaded(m_errorMessage); | ||
238 | } | ||
239 | |||
240 | /// <summary> | ||
241 | /// Look up the given user id to check whether it's one that is valid for this grid. | ||
242 | /// </summary> | ||
243 | /// <param name="uuid"></param> | ||
244 | /// <returns></returns> | ||
245 | private bool resolveUserUuid(UUID uuid) | ||
246 | { | ||
247 | if (!m_validUserUuids.ContainsKey(uuid)) | ||
248 | { | ||
249 | CachedUserInfo profile = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(uuid); | ||
250 | if (profile != null && profile.UserProfile != null) | ||
251 | m_validUserUuids.Add(uuid, true); | ||
252 | else | ||
253 | m_validUserUuids.Add(uuid, false); | ||
254 | } | ||
255 | |||
256 | if (m_validUserUuids[uuid]) | ||
257 | return true; | ||
258 | else | ||
259 | return false; | ||
260 | } | ||
261 | |||
262 | /// <summary> | ||
263 | /// Load an asset | ||
264 | /// </summary> | ||
265 | /// <param name="assetFilename"></param> | ||
266 | /// <param name="data"></param> | ||
267 | /// <returns>true if asset was successfully loaded, false otherwise</returns> | ||
268 | private bool LoadAsset(string assetPath, byte[] data) | ||
269 | { | ||
270 | // Right now we're nastily obtaining the UUID from the filename | ||
271 | string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); | ||
272 | int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR); | ||
273 | |||
274 | if (i == -1) | ||
275 | { | ||
276 | m_log.ErrorFormat( | ||
277 | "[ARCHIVER]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping", | ||
278 | assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR); | ||
279 | |||
280 | return false; | ||
281 | } | ||
282 | |||
283 | string extension = filename.Substring(i); | ||
284 | string uuid = filename.Remove(filename.Length - extension.Length); | ||
285 | |||
286 | if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension)) | ||
287 | { | ||
288 | sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension]; | ||
289 | |||
290 | //m_log.DebugFormat("[ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType); | ||
291 | |||
292 | AssetBase asset = new AssetBase(new UUID(uuid), String.Empty); | ||
293 | asset.Metadata.Type = assetType; | ||
294 | asset.Data = data; | ||
295 | |||
296 | m_scene.AssetCache.AddAsset(asset); | ||
297 | |||
298 | /** | ||
299 | * Create layers on decode for image assets. This is likely to significantly increase the time to load archives so | ||
300 | * it might be best done when dearchive takes place on a separate thread | ||
301 | if (asset.Type=AssetType.Texture) | ||
302 | { | ||
303 | IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>(); | ||
304 | if (cacheLayerDecode != null) | ||
305 | cacheLayerDecode.syncdecode(asset.FullID, asset.Data); | ||
306 | } | ||
307 | */ | ||
308 | |||
309 | return true; | ||
310 | } | ||
311 | else | ||
312 | { | ||
313 | m_log.ErrorFormat( | ||
314 | "[ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}", | ||
315 | assetPath, extension); | ||
316 | |||
317 | return false; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | /// <summary> | ||
322 | /// Load region settings data | ||
323 | /// </summary> | ||
324 | /// <param name="settingsPath"></param> | ||
325 | /// <param name="data"></param> | ||
326 | /// <returns> | ||
327 | /// true if settings were loaded successfully, false otherwise | ||
328 | /// </returns> | ||
329 | private bool LoadRegionSettings(string settingsPath, byte[] data) | ||
330 | { | ||
331 | RegionSettings loadedRegionSettings; | ||
332 | |||
333 | try | ||
334 | { | ||
335 | loadedRegionSettings = RegionSettingsSerializer.Deserialize(data); | ||
336 | } | ||
337 | catch (Exception e) | ||
338 | { | ||
339 | m_log.ErrorFormat( | ||
340 | "[ARCHIVER]: Could not parse region settings file {0}. Ignoring. Exception was {1}", | ||
341 | settingsPath, e); | ||
342 | return false; | ||
343 | } | ||
344 | |||
345 | RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings; | ||
346 | |||
347 | currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit; | ||
348 | currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage; | ||
349 | currentRegionSettings.AllowLandJoinDivide = loadedRegionSettings.AllowLandJoinDivide; | ||
350 | currentRegionSettings.AllowLandResell = loadedRegionSettings.AllowLandResell; | ||
351 | currentRegionSettings.BlockFly = loadedRegionSettings.BlockFly; | ||
352 | currentRegionSettings.BlockShowInSearch = loadedRegionSettings.BlockShowInSearch; | ||
353 | currentRegionSettings.BlockTerraform = loadedRegionSettings.BlockTerraform; | ||
354 | currentRegionSettings.DisableCollisions = loadedRegionSettings.DisableCollisions; | ||
355 | currentRegionSettings.DisablePhysics = loadedRegionSettings.DisablePhysics; | ||
356 | currentRegionSettings.DisableScripts = loadedRegionSettings.DisableScripts; | ||
357 | currentRegionSettings.Elevation1NE = loadedRegionSettings.Elevation1NE; | ||
358 | currentRegionSettings.Elevation1NW = loadedRegionSettings.Elevation1NW; | ||
359 | currentRegionSettings.Elevation1SE = loadedRegionSettings.Elevation1SE; | ||
360 | currentRegionSettings.Elevation1SW = loadedRegionSettings.Elevation1SW; | ||
361 | currentRegionSettings.Elevation2NE = loadedRegionSettings.Elevation2NE; | ||
362 | currentRegionSettings.Elevation2NW = loadedRegionSettings.Elevation2NW; | ||
363 | currentRegionSettings.Elevation2SE = loadedRegionSettings.Elevation2SE; | ||
364 | currentRegionSettings.Elevation2SW = loadedRegionSettings.Elevation2SW; | ||
365 | currentRegionSettings.FixedSun = loadedRegionSettings.FixedSun; | ||
366 | currentRegionSettings.ObjectBonus = loadedRegionSettings.ObjectBonus; | ||
367 | currentRegionSettings.RestrictPushing = loadedRegionSettings.RestrictPushing; | ||
368 | currentRegionSettings.TerrainLowerLimit = loadedRegionSettings.TerrainLowerLimit; | ||
369 | currentRegionSettings.TerrainRaiseLimit = loadedRegionSettings.TerrainRaiseLimit; | ||
370 | currentRegionSettings.TerrainTexture1 = loadedRegionSettings.TerrainTexture1; | ||
371 | currentRegionSettings.TerrainTexture2 = loadedRegionSettings.TerrainTexture2; | ||
372 | currentRegionSettings.TerrainTexture3 = loadedRegionSettings.TerrainTexture3; | ||
373 | currentRegionSettings.TerrainTexture4 = loadedRegionSettings.TerrainTexture4; | ||
374 | currentRegionSettings.UseEstateSun = loadedRegionSettings.UseEstateSun; | ||
375 | currentRegionSettings.WaterHeight = loadedRegionSettings.WaterHeight; | ||
376 | |||
377 | IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>(); | ||
378 | estateModule.sendRegionHandshakeToAll(); | ||
379 | |||
380 | return true; | ||
381 | } | ||
382 | |||
383 | /// <summary> | ||
384 | /// Load terrain data | ||
385 | /// </summary> | ||
386 | /// <param name="terrainPath"></param> | ||
387 | /// <param name="data"></param> | ||
388 | /// <returns> | ||
389 | /// true if terrain was resolved successfully, false otherwise. | ||
390 | /// </returns> | ||
391 | private bool LoadTerrain(string terrainPath, byte[] data) | ||
392 | { | ||
393 | ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>(); | ||
394 | |||
395 | MemoryStream ms = new MemoryStream(data); | ||
396 | terrainModule.LoadFromStream(terrainPath, ms); | ||
397 | ms.Close(); | ||
398 | |||
399 | m_log.DebugFormat("[ARCHIVER]: Restored terrain {0}", terrainPath); | ||
400 | |||
401 | return true; | ||
402 | } | ||
403 | |||
404 | /// <summary> | ||
405 | /// Resolve path to a working FileStream | ||
406 | /// </summary> | ||
407 | private Stream GetStream(string path) | ||
408 | { | ||
409 | try | ||
410 | { | ||
411 | if (File.Exists(path)) | ||
412 | { | ||
413 | return new FileStream(path, FileMode.Open); | ||
414 | } | ||
415 | else | ||
416 | { | ||
417 | Uri uri = new Uri(path); // throw exception if not valid URI | ||
418 | if (uri.Scheme == "file") | ||
419 | { | ||
420 | return new FileStream(uri.AbsolutePath, FileMode.Open); | ||
421 | } | ||
422 | else | ||
423 | { | ||
424 | if (uri.Scheme != "http") | ||
425 | throw new Exception(String.Format("Unsupported URI scheme ({0})", path)); | ||
426 | |||
427 | // OK, now we know we have an HTTP URI to work with | ||
428 | |||
429 | return URIFetch(uri); | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | catch (Exception e) | ||
434 | { | ||
435 | throw new Exception(String.Format("Unable to create file input stream for {0}: {1}", path, e)); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | private static Stream URIFetch(Uri uri) | ||
440 | { | ||
441 | HttpWebRequest request = (HttpWebRequest) WebRequest.Create(uri); | ||
442 | |||
443 | // request.Credentials = credentials; | ||
444 | |||
445 | request.ContentLength = 0; | ||
446 | |||
447 | WebResponse response = request.GetResponse(); | ||
448 | Stream file = response.GetResponseStream(); | ||
449 | |||
450 | if (response.ContentType != "application/x-oar") | ||
451 | throw new Exception(String.Format("{0} does not identify an OAR file", uri.ToString())); | ||
452 | |||
453 | if (response.ContentLength == 0) | ||
454 | throw new Exception(String.Format("{0} returned an empty file", uri.ToString())); | ||
455 | |||
456 | // return new BufferedStream(file, (int) response.ContentLength); | ||
457 | return new BufferedStream(file, 1000000); | ||
458 | } | ||
459 | } | ||
460 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs new file mode 100644 index 0000000..d3c2cd1 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs | |||
@@ -0,0 +1,161 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using System.Xml; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
39 | using OpenSim.Region.CoreModules.World.Terrain; | ||
40 | |||
41 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
42 | { | ||
43 | /// <summary> | ||
44 | /// Method called when all the necessary assets for an archive request have been received. | ||
45 | /// </summary> | ||
46 | public delegate void AssetsRequestCallback(IDictionary<UUID, AssetBase> assetsFound, ICollection<UUID> assetsNotFoundUuids); | ||
47 | |||
48 | /// <summary> | ||
49 | /// Execute the write of an archive once we have received all the necessary data | ||
50 | /// </summary> | ||
51 | public class ArchiveWriteRequestExecution | ||
52 | { | ||
53 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
54 | |||
55 | protected ITerrainModule m_terrainModule; | ||
56 | protected IRegionSerialiserModule m_serialiser; | ||
57 | protected List<SceneObjectGroup> m_sceneObjects; | ||
58 | protected Scene m_scene; | ||
59 | protected Stream m_saveStream; | ||
60 | |||
61 | public ArchiveWriteRequestExecution( | ||
62 | List<SceneObjectGroup> sceneObjects, | ||
63 | ITerrainModule terrainModule, | ||
64 | IRegionSerialiserModule serialiser, | ||
65 | Scene scene, | ||
66 | Stream saveStream) | ||
67 | { | ||
68 | m_sceneObjects = sceneObjects; | ||
69 | m_terrainModule = terrainModule; | ||
70 | m_serialiser = serialiser; | ||
71 | m_scene = scene; | ||
72 | m_saveStream = saveStream; | ||
73 | } | ||
74 | |||
75 | protected internal void ReceivedAllAssets( | ||
76 | IDictionary<UUID, AssetBase> assetsFound, ICollection<UUID> assetsNotFoundUuids) | ||
77 | { | ||
78 | foreach (UUID uuid in assetsNotFoundUuids) | ||
79 | { | ||
80 | m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid); | ||
81 | } | ||
82 | |||
83 | m_log.InfoFormat( | ||
84 | "[ARCHIVER]: Received {0} of {1} assets requested", | ||
85 | assetsFound.Count, assetsFound.Count + assetsNotFoundUuids.Count); | ||
86 | |||
87 | m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time."); | ||
88 | |||
89 | TarArchiveWriter archive = new TarArchiveWriter(); | ||
90 | |||
91 | // Write out control file | ||
92 | archive.AddFile(ArchiveConstants.CONTROL_FILE_PATH, Create0p2ControlFile()); | ||
93 | |||
94 | // Write out region settings | ||
95 | string settingsPath | ||
96 | = String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName); | ||
97 | archive.AddFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings)); | ||
98 | |||
99 | // Write out terrain | ||
100 | string terrainPath | ||
101 | = String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName); | ||
102 | |||
103 | MemoryStream ms = new MemoryStream(); | ||
104 | m_terrainModule.SaveToStream(terrainPath, ms); | ||
105 | archive.AddFile(terrainPath, ms.ToArray()); | ||
106 | ms.Close(); | ||
107 | |||
108 | // Write out scene object metadata | ||
109 | foreach (SceneObjectGroup sceneObject in m_sceneObjects) | ||
110 | { | ||
111 | //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType()); | ||
112 | |||
113 | Vector3 position = sceneObject.AbsolutePosition; | ||
114 | |||
115 | string serializedObject = m_serialiser.SaveGroupToXml2(sceneObject); | ||
116 | string filename | ||
117 | = string.Format( | ||
118 | "{0}{1}_{2:000}-{3:000}-{4:000}__{5}.xml", | ||
119 | ArchiveConstants.OBJECTS_PATH, sceneObject.Name, | ||
120 | Math.Round(position.X), Math.Round(position.Y), Math.Round(position.Z), | ||
121 | sceneObject.UUID); | ||
122 | |||
123 | archive.AddFile(filename, serializedObject); | ||
124 | } | ||
125 | |||
126 | // Write out assets | ||
127 | AssetsArchiver assetsArchiver = new AssetsArchiver(assetsFound); | ||
128 | assetsArchiver.Archive(archive); | ||
129 | |||
130 | archive.WriteTar(m_saveStream); | ||
131 | |||
132 | m_log.InfoFormat("[ARCHIVER]: Wrote out OpenSimulator archive for {0}", m_scene.RegionInfo.RegionName); | ||
133 | |||
134 | m_scene.EventManager.TriggerOarFileSaved(String.Empty); | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Create the control file for a 0.2 version archive | ||
139 | /// </summary> | ||
140 | /// <returns></returns> | ||
141 | public static string Create0p2ControlFile() | ||
142 | { | ||
143 | StringWriter sw = new StringWriter(); | ||
144 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
145 | xtw.Formatting = Formatting.Indented; | ||
146 | xtw.WriteStartDocument(); | ||
147 | xtw.WriteStartElement("archive"); | ||
148 | xtw.WriteAttributeString("major_version", "0"); | ||
149 | xtw.WriteAttributeString("minor_version", "2"); | ||
150 | xtw.WriteEndElement(); | ||
151 | |||
152 | xtw.Flush(); | ||
153 | xtw.Close(); | ||
154 | |||
155 | String s = sw.ToString(); | ||
156 | sw.Close(); | ||
157 | |||
158 | return s; | ||
159 | } | ||
160 | } | ||
161 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs new file mode 100644 index 0000000..ee0ec69 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs | |||
@@ -0,0 +1,333 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Framework; | ||
29 | using OpenSim.Framework.Communications.Cache; | ||
30 | using OpenSim.Region.Framework.Interfaces; | ||
31 | using OpenSim.Region.Framework.Scenes; | ||
32 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
33 | using OpenSim.Region.CoreModules.World.Terrain; | ||
34 | using System; | ||
35 | using System.Collections.Generic; | ||
36 | using System.IO; | ||
37 | using System.IO.Compression; | ||
38 | using System.Reflection; | ||
39 | using System.Text.RegularExpressions; | ||
40 | using System.Threading; | ||
41 | using OpenMetaverse; | ||
42 | using log4net; | ||
43 | using Nini.Config; | ||
44 | |||
45 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
46 | { | ||
47 | /// <summary> | ||
48 | /// Prepare to write out an archive. | ||
49 | /// </summary> | ||
50 | public class ArchiveWriteRequestPreparation | ||
51 | { | ||
52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | protected Scene m_scene; | ||
55 | protected Stream m_saveStream; | ||
56 | |||
57 | /// <summary> | ||
58 | /// Used as a temporary store of an asset which represents an object. This can be a null if no appropriate | ||
59 | /// asset was found by the asset service. | ||
60 | /// </summary> | ||
61 | protected AssetBase m_requestedObjectAsset; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Signal whether we are currently waiting for the asset service to deliver an asset. | ||
65 | /// </summary> | ||
66 | protected bool m_waitingForObjectAsset; | ||
67 | |||
68 | /// <summary> | ||
69 | /// Constructor | ||
70 | /// </summary> | ||
71 | public ArchiveWriteRequestPreparation(Scene scene, string savePath) | ||
72 | { | ||
73 | m_scene = scene; | ||
74 | m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress); | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Constructor. | ||
79 | /// </summary> | ||
80 | /// <param name="scene"></param> | ||
81 | /// <param name="saveStream">The stream to which to save data.</param> | ||
82 | public ArchiveWriteRequestPreparation(Scene scene, Stream saveStream) | ||
83 | { | ||
84 | m_scene = scene; | ||
85 | m_saveStream = saveStream; | ||
86 | } | ||
87 | |||
88 | /// <summary> | ||
89 | /// The callback made when we request the asset for an object from the asset service. | ||
90 | /// </summary> | ||
91 | public void AssetRequestCallback(UUID assetID, AssetBase asset) | ||
92 | { | ||
93 | lock (this) | ||
94 | { | ||
95 | m_requestedObjectAsset = asset; | ||
96 | m_waitingForObjectAsset = false; | ||
97 | Monitor.Pulse(this); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /// <summary> | ||
102 | /// Get an asset synchronously, potentially using an asynchronous callback. If the | ||
103 | /// asynchronous callback is used, we will wait for it to complete. | ||
104 | /// </summary> | ||
105 | /// <param name="uuid"></param> | ||
106 | /// <returns></returns> | ||
107 | protected AssetBase GetAsset(UUID uuid) | ||
108 | { | ||
109 | m_waitingForObjectAsset = true; | ||
110 | m_scene.AssetCache.GetAsset(uuid, AssetRequestCallback, true); | ||
111 | |||
112 | // The asset cache callback can either | ||
113 | // | ||
114 | // 1. Complete on the same thread (if the asset is already in the cache) or | ||
115 | // 2. Come in via a different thread (if we need to go fetch it). | ||
116 | // | ||
117 | // The code below handles both these alternatives. | ||
118 | lock (this) | ||
119 | { | ||
120 | if (m_waitingForObjectAsset) | ||
121 | { | ||
122 | Monitor.Wait(this); | ||
123 | m_waitingForObjectAsset = false; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return m_requestedObjectAsset; | ||
128 | } | ||
129 | |||
130 | /// <summary> | ||
131 | /// Record the asset uuids embedded within the given script. | ||
132 | /// </summary> | ||
133 | /// <param name="scriptUuid"></param> | ||
134 | /// <param name="assetUuids">Dictionary in which to record the references</param> | ||
135 | protected void GetScriptAssetUuids(UUID scriptUuid, IDictionary<UUID, int> assetUuids) | ||
136 | { | ||
137 | AssetBase scriptAsset = GetAsset(scriptUuid); | ||
138 | |||
139 | if (null != scriptAsset) | ||
140 | { | ||
141 | string script = Utils.BytesToString(scriptAsset.Data); | ||
142 | //m_log.DebugFormat("[ARCHIVER]: Script {0}", script); | ||
143 | MatchCollection uuidMatches = Util.UUIDPattern.Matches(script); | ||
144 | //m_log.DebugFormat("[ARCHIVER]: Found {0} matches in script", uuidMatches.Count); | ||
145 | |||
146 | foreach (Match uuidMatch in uuidMatches) | ||
147 | { | ||
148 | UUID uuid = new UUID(uuidMatch.Value); | ||
149 | //m_log.DebugFormat("[ARCHIVER]: Recording {0} in script", uuid); | ||
150 | assetUuids[uuid] = 1; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /// <summary> | ||
156 | /// Record the uuids referenced by the given wearable asset | ||
157 | /// </summary> | ||
158 | /// <param name="wearableAssetUuid"></param> | ||
159 | /// <param name="assetUuids">Dictionary in which to record the references</param> | ||
160 | protected void GetWearableAssetUuids(UUID wearableAssetUuid, IDictionary<UUID, int> assetUuids) | ||
161 | { | ||
162 | AssetBase assetBase = GetAsset(wearableAssetUuid); | ||
163 | //m_log.Debug(new System.Text.ASCIIEncoding().GetString(bodypartAsset.Data)); | ||
164 | AssetWearable wearableAsset = new AssetBodypart(wearableAssetUuid, assetBase.Data); | ||
165 | wearableAsset.Decode(); | ||
166 | |||
167 | //m_log.DebugFormat( | ||
168 | // "[ARCHIVER]: Wearable asset {0} references {1} assets", wearableAssetUuid, wearableAsset.Textures.Count); | ||
169 | |||
170 | foreach (UUID uuid in wearableAsset.Textures.Values) | ||
171 | { | ||
172 | //m_log.DebugFormat("[ARCHIVER]: Got bodypart uuid {0}", uuid); | ||
173 | assetUuids[uuid] = 1; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /// <summary> | ||
178 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
179 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
180 | /// within this object). | ||
181 | /// </summary> | ||
182 | /// <param name="sceneObject"></param> | ||
183 | /// <param name="assetUuids"></param> | ||
184 | protected void GetSceneObjectAssetUuids(UUID sceneObjectUuid, IDictionary<UUID, int> assetUuids) | ||
185 | { | ||
186 | AssetBase objectAsset = GetAsset(sceneObjectUuid); | ||
187 | |||
188 | if (null != objectAsset) | ||
189 | { | ||
190 | string xml = Utils.BytesToString(objectAsset.Data); | ||
191 | SceneObjectGroup sog = new SceneObjectGroup(xml, true); | ||
192 | GetSceneObjectAssetUuids(sog, assetUuids); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | /// <summary> | ||
197 | /// Get all the asset uuids associated with a given object. This includes both those directly associated with | ||
198 | /// it (e.g. face textures) and recursively, those of items within it's inventory (e.g. objects contained | ||
199 | /// within this object). | ||
200 | /// </summary> | ||
201 | /// <param name="sceneObject"></param> | ||
202 | /// <param name="assetUuids"></param> | ||
203 | protected void GetSceneObjectAssetUuids(SceneObjectGroup sceneObject, IDictionary<UUID, int> assetUuids) | ||
204 | { | ||
205 | m_log.DebugFormat( | ||
206 | "[ARCHIVER]: Getting assets for object {0}, {1}", sceneObject.Name, sceneObject.UUID); | ||
207 | |||
208 | foreach (SceneObjectPart part in sceneObject.GetParts()) | ||
209 | { | ||
210 | //m_log.DebugFormat( | ||
211 | // "[ARCHIVER]: Getting part {0}, {1} for object {2}", part.Name, part.UUID, sceneObject.UUID); | ||
212 | |||
213 | try | ||
214 | { | ||
215 | Primitive.TextureEntry textureEntry = part.Shape.Textures; | ||
216 | |||
217 | // Get the prim's default texture. This will be used for faces which don't have their own texture | ||
218 | assetUuids[textureEntry.DefaultTexture.TextureID] = 1; | ||
219 | |||
220 | // XXX: Not a great way to iterate through face textures, but there's no | ||
221 | // other method available to tell how many faces there actually are | ||
222 | //int i = 0; | ||
223 | foreach (Primitive.TextureEntryFace texture in textureEntry.FaceTextures) | ||
224 | { | ||
225 | if (texture != null) | ||
226 | { | ||
227 | //m_log.DebugFormat("[ARCHIVER]: Got face {0}", i++); | ||
228 | assetUuids[texture.TextureID] = 1; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | // If the prim is a sculpt then preserve this information too | ||
233 | if (part.Shape.SculptTexture != UUID.Zero) | ||
234 | assetUuids[part.Shape.SculptTexture] = 1; | ||
235 | |||
236 | // Now analyze this prim's inventory items to preserve all the uuids that they reference | ||
237 | foreach (TaskInventoryItem tii in part.TaskInventory.Values) | ||
238 | { | ||
239 | //m_log.DebugFormat("[ARCHIVER]: Analysing item asset type {0}", tii.Type); | ||
240 | |||
241 | if (!assetUuids.ContainsKey(tii.AssetID)) | ||
242 | { | ||
243 | assetUuids[tii.AssetID] = 1; | ||
244 | |||
245 | if ((int)AssetType.Bodypart == tii.Type || ((int)AssetType.Clothing == tii.Type)) | ||
246 | { | ||
247 | GetWearableAssetUuids(tii.AssetID, assetUuids); | ||
248 | } | ||
249 | else if ((int)AssetType.LSLText == tii.Type) | ||
250 | { | ||
251 | GetScriptAssetUuids(tii.AssetID, assetUuids); | ||
252 | } | ||
253 | else if ((int)AssetType.Object == tii.Type) | ||
254 | { | ||
255 | GetSceneObjectAssetUuids(tii.AssetID, assetUuids); | ||
256 | } | ||
257 | //else | ||
258 | //{ | ||
259 | //m_log.DebugFormat("[ARCHIVER]: Recording asset {0} in object {1}", tii.AssetID, part.UUID); | ||
260 | //} | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | catch (Exception e) | ||
265 | { | ||
266 | m_log.ErrorFormat("[ARCHIVER]: Failed to get part - {0}", e); | ||
267 | m_log.DebugFormat("[ARCHIVER]: Texture entry length for prim was {0} (min is 46)", part.Shape.TextureEntry.Length); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | /// <summary> | ||
273 | /// Archive the region requested. | ||
274 | /// </summary> | ||
275 | /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception> | ||
276 | public void ArchiveRegion() | ||
277 | { | ||
278 | Dictionary<UUID, int> assetUuids = new Dictionary<UUID, int>(); | ||
279 | |||
280 | List<EntityBase> entities = m_scene.GetEntities(); | ||
281 | List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | ||
282 | |||
283 | // Filter entities so that we only have scene objects. | ||
284 | // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods | ||
285 | // end up having to do this | ||
286 | foreach (EntityBase entity in entities) | ||
287 | { | ||
288 | if (entity is SceneObjectGroup) | ||
289 | { | ||
290 | SceneObjectGroup sceneObject = (SceneObjectGroup)entity; | ||
291 | |||
292 | if (!sceneObject.IsDeleted && !sceneObject.IsAttachment) | ||
293 | sceneObjects.Add((SceneObjectGroup)entity); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | foreach (SceneObjectGroup sceneObject in sceneObjects) | ||
298 | { | ||
299 | GetSceneObjectAssetUuids(sceneObject, assetUuids); | ||
300 | } | ||
301 | |||
302 | m_log.DebugFormat( | ||
303 | "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets", | ||
304 | sceneObjects.Count, assetUuids.Count); | ||
305 | |||
306 | // Make sure that we also request terrain texture assets | ||
307 | RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings; | ||
308 | |||
309 | if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1) | ||
310 | assetUuids[regionSettings.TerrainTexture1] = 1; | ||
311 | |||
312 | if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2) | ||
313 | assetUuids[regionSettings.TerrainTexture2] = 1; | ||
314 | |||
315 | if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3) | ||
316 | assetUuids[regionSettings.TerrainTexture3] = 1; | ||
317 | |||
318 | if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4) | ||
319 | assetUuids[regionSettings.TerrainTexture4] = 1; | ||
320 | |||
321 | // Asynchronously request all the assets required to perform this archive operation | ||
322 | ArchiveWriteRequestExecution awre | ||
323 | = new ArchiveWriteRequestExecution( | ||
324 | sceneObjects, | ||
325 | m_scene.RequestModuleInterface<ITerrainModule>(), | ||
326 | m_scene.RequestModuleInterface<IRegionSerialiserModule>(), | ||
327 | m_scene, | ||
328 | m_saveStream); | ||
329 | |||
330 | new AssetsRequest(assetUuids.Keys, m_scene.AssetCache, awre.ReceivedAllAssets).Execute(); | ||
331 | } | ||
332 | } | ||
333 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs new file mode 100644 index 0000000..c1f5b18 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Collections.Generic; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Threading; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Framework.Communications.Cache; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
39 | |||
40 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
41 | { | ||
42 | /// <summary> | ||
43 | /// This module loads and saves OpenSimulator archives | ||
44 | /// </summary> | ||
45 | public class ArchiverModule : IRegionModule, IRegionArchiverModule | ||
46 | { | ||
47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
48 | |||
49 | private Scene m_scene; | ||
50 | |||
51 | public string Name { get { return "Archiver Module"; } } | ||
52 | |||
53 | public bool IsSharedModule { get { return false; } } | ||
54 | |||
55 | public void Initialise(Scene scene, IConfigSource source) | ||
56 | { | ||
57 | m_scene = scene; | ||
58 | m_scene.RegisterModuleInterface<IRegionArchiverModule>(this); | ||
59 | } | ||
60 | |||
61 | public void PostInitialise() | ||
62 | { | ||
63 | } | ||
64 | |||
65 | public void Close() | ||
66 | { | ||
67 | } | ||
68 | |||
69 | public void ArchiveRegion(string savePath) | ||
70 | { | ||
71 | m_log.InfoFormat( | ||
72 | "[ARCHIVER]: Writing archive for region {0} to {1}", m_scene.RegionInfo.RegionName, savePath); | ||
73 | |||
74 | new ArchiveWriteRequestPreparation(m_scene, savePath).ArchiveRegion(); | ||
75 | } | ||
76 | |||
77 | public void ArchiveRegion(Stream saveStream) | ||
78 | { | ||
79 | new ArchiveWriteRequestPreparation(m_scene, saveStream).ArchiveRegion(); | ||
80 | } | ||
81 | |||
82 | public void DearchiveRegion(string loadPath) | ||
83 | { | ||
84 | m_log.InfoFormat( | ||
85 | "[ARCHIVER]: Loading archive to region {0} from {1}", m_scene.RegionInfo.RegionName, loadPath); | ||
86 | |||
87 | new ArchiveReadRequest(m_scene, loadPath).DearchiveRegion(); | ||
88 | } | ||
89 | |||
90 | public void DearchiveRegion(Stream loadStream) | ||
91 | { | ||
92 | new ArchiveReadRequest(m_scene, loadStream).DearchiveRegion(); | ||
93 | } | ||
94 | } | ||
95 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs new file mode 100644 index 0000000..76d27ce --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsArchiver.cs | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Collections.Generic; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Xml; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using OpenSim.Framework; | ||
35 | |||
36 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// Archives assets | ||
40 | /// </summary> | ||
41 | public class AssetsArchiver | ||
42 | { | ||
43 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
44 | |||
45 | /// <summary> | ||
46 | /// Archive assets | ||
47 | /// </summary> | ||
48 | protected IDictionary<UUID, AssetBase> m_assets; | ||
49 | |||
50 | public AssetsArchiver(IDictionary<UUID, AssetBase> assets) | ||
51 | { | ||
52 | m_assets = assets; | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Archive the assets given to this archiver to the given archive. | ||
57 | /// </summary> | ||
58 | /// <param name="archive"></param> | ||
59 | public void Archive(TarArchiveWriter archive) | ||
60 | { | ||
61 | //WriteMetadata(archive); | ||
62 | WriteData(archive); | ||
63 | } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Write an assets metadata file to the given archive | ||
67 | /// </summary> | ||
68 | /// <param name="archive"></param> | ||
69 | protected void WriteMetadata(TarArchiveWriter archive) | ||
70 | { | ||
71 | StringWriter sw = new StringWriter(); | ||
72 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
73 | |||
74 | xtw.Formatting = Formatting.Indented; | ||
75 | xtw.WriteStartDocument(); | ||
76 | |||
77 | xtw.WriteStartElement("assets"); | ||
78 | |||
79 | foreach (UUID uuid in m_assets.Keys) | ||
80 | { | ||
81 | AssetBase asset = m_assets[uuid]; | ||
82 | |||
83 | if (asset != null) | ||
84 | { | ||
85 | xtw.WriteStartElement("asset"); | ||
86 | |||
87 | string extension = string.Empty; | ||
88 | |||
89 | if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.Metadata.Type)) | ||
90 | { | ||
91 | extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.Metadata.Type]; | ||
92 | } | ||
93 | |||
94 | xtw.WriteElementString("filename", uuid.ToString() + extension); | ||
95 | |||
96 | xtw.WriteElementString("name", asset.Metadata.Name); | ||
97 | xtw.WriteElementString("description", asset.Metadata.Description); | ||
98 | xtw.WriteElementString("asset-type", asset.Metadata.Type.ToString()); | ||
99 | |||
100 | xtw.WriteEndElement(); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | xtw.WriteEndElement(); | ||
105 | |||
106 | xtw.WriteEndDocument(); | ||
107 | |||
108 | archive.AddFile("assets.xml", sw.ToString()); | ||
109 | } | ||
110 | |||
111 | /// <summary> | ||
112 | /// Write asset data files to the given archive | ||
113 | /// </summary> | ||
114 | /// <param name="archive"></param> | ||
115 | protected void WriteData(TarArchiveWriter archive) | ||
116 | { | ||
117 | // It appears that gtar, at least, doesn't need the intermediate directory entries in the tar | ||
118 | //archive.AddDir("assets"); | ||
119 | |||
120 | foreach (UUID uuid in m_assets.Keys) | ||
121 | { | ||
122 | AssetBase asset = m_assets[uuid]; | ||
123 | |||
124 | string extension = string.Empty; | ||
125 | |||
126 | if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(asset.Metadata.Type)) | ||
127 | { | ||
128 | extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[asset.Metadata.Type]; | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | m_log.ErrorFormat( | ||
133 | "[ARCHIVER]: Unrecognized asset type {0} with uuid {1}. This asset will be saved but not reloaded", | ||
134 | asset.Metadata.Type, asset.Metadata.ID); | ||
135 | } | ||
136 | |||
137 | archive.AddFile( | ||
138 | ArchiveConstants.ASSETS_PATH + uuid.ToString() + extension, | ||
139 | asset.Data); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs new file mode 100644 index 0000000..f9909d9 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsDearchiver.cs | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using System.Xml; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Communications.Cache; | ||
37 | |||
38 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
39 | { | ||
40 | /// <summary> | ||
41 | /// Dearchives assets | ||
42 | /// </summary> | ||
43 | public class AssetsDearchiver | ||
44 | { | ||
45 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | |||
47 | protected static System.Text.ASCIIEncoding m_asciiEncoding = new System.Text.ASCIIEncoding(); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Store for asset data we received before we get the metadata | ||
51 | /// </summary> | ||
52 | protected Dictionary<string, byte[]> m_assetDataAwaitingMetadata = new Dictionary<string, byte[]>(); | ||
53 | |||
54 | /// <summary> | ||
55 | /// Asset metadata. Is null if asset metadata isn't yet available. | ||
56 | /// </summary> | ||
57 | protected Dictionary<string, AssetMetadata> m_metadata; | ||
58 | |||
59 | /// <summary> | ||
60 | /// Cache to which dearchived assets will be added | ||
61 | /// </summary> | ||
62 | protected IAssetCache m_cache; | ||
63 | |||
64 | public AssetsDearchiver(IAssetCache cache) | ||
65 | { | ||
66 | m_cache = cache; | ||
67 | } | ||
68 | |||
69 | /// <summary> | ||
70 | /// Add asset data to the dearchiver | ||
71 | /// </summary> | ||
72 | /// <param name="assetFilename"></param> | ||
73 | /// <param name="data"></param> | ||
74 | public void AddAssetData(string assetFilename, byte[] data) | ||
75 | { | ||
76 | if (null == m_metadata) | ||
77 | { | ||
78 | m_assetDataAwaitingMetadata[assetFilename] = data; | ||
79 | } | ||
80 | else | ||
81 | { | ||
82 | ResolveAssetData(assetFilename, data); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /// <summary> | ||
87 | /// Add asset metadata xml | ||
88 | /// </summary> | ||
89 | /// <param name="xml"></param> | ||
90 | public void AddAssetMetadata(string xml) | ||
91 | { | ||
92 | m_metadata = new Dictionary<string, AssetMetadata>(); | ||
93 | |||
94 | StringReader sr = new StringReader(xml); | ||
95 | XmlTextReader reader = new XmlTextReader(sr); | ||
96 | |||
97 | reader.ReadStartElement("assets"); | ||
98 | reader.Read(); | ||
99 | |||
100 | while (reader.Name.Equals("asset")) | ||
101 | { | ||
102 | reader.Read(); | ||
103 | |||
104 | AssetMetadata metadata = new AssetMetadata(); | ||
105 | |||
106 | string filename = reader.ReadElementString("filename"); | ||
107 | m_log.DebugFormat("[DEARCHIVER]: Reading node {0}", filename); | ||
108 | |||
109 | metadata.Name = reader.ReadElementString("name"); | ||
110 | metadata.Description = reader.ReadElementString("description"); | ||
111 | metadata.AssetType = Convert.ToSByte(reader.ReadElementString("asset-type")); | ||
112 | |||
113 | m_metadata[filename] = metadata; | ||
114 | |||
115 | // Read asset end tag | ||
116 | reader.ReadEndElement(); | ||
117 | |||
118 | reader.Read(); | ||
119 | } | ||
120 | |||
121 | m_log.DebugFormat("[DEARCHIVER]: Resolved {0} items of asset metadata", m_metadata.Count); | ||
122 | |||
123 | ResolvePendingAssetData(); | ||
124 | } | ||
125 | |||
126 | /// <summary> | ||
127 | /// Resolve asset data that we collected before receiving the metadata | ||
128 | /// </summary> | ||
129 | protected void ResolvePendingAssetData() | ||
130 | { | ||
131 | foreach (string filename in m_assetDataAwaitingMetadata.Keys) | ||
132 | { | ||
133 | ResolveAssetData(filename, m_assetDataAwaitingMetadata[filename]); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | /// <summary> | ||
138 | /// Resolve a new piece of asset data against stored metadata | ||
139 | /// </summary> | ||
140 | /// <param name="assetFilename"></param> | ||
141 | /// <param name="data"></param> | ||
142 | protected void ResolveAssetData(string assetPath, byte[] data) | ||
143 | { | ||
144 | // Right now we're nastily obtaining the UUID from the filename | ||
145 | string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length); | ||
146 | |||
147 | if (m_metadata.ContainsKey(filename)) | ||
148 | { | ||
149 | AssetMetadata metadata = m_metadata[filename]; | ||
150 | |||
151 | if (ArchiveConstants.ASSET_TYPE_TO_EXTENSION.ContainsKey(metadata.AssetType)) | ||
152 | { | ||
153 | string extension = ArchiveConstants.ASSET_TYPE_TO_EXTENSION[metadata.AssetType]; | ||
154 | filename = filename.Remove(filename.Length - extension.Length); | ||
155 | } | ||
156 | |||
157 | m_log.DebugFormat("[ARCHIVER]: Importing asset {0}", filename); | ||
158 | |||
159 | AssetBase asset = new AssetBase(new UUID(filename), metadata.Name); | ||
160 | asset.Metadata.Description = metadata.Description; | ||
161 | asset.Metadata.Type = metadata.AssetType; | ||
162 | asset.Data = data; | ||
163 | |||
164 | m_cache.AddAsset(asset); | ||
165 | } | ||
166 | else | ||
167 | { | ||
168 | m_log.ErrorFormat( | ||
169 | "[DEARCHIVER]: Tried to dearchive data with filename {0} without any corresponding metadata", | ||
170 | assetPath); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /// <summary> | ||
175 | /// Metadata for an asset | ||
176 | /// </summary> | ||
177 | protected struct AssetMetadata | ||
178 | { | ||
179 | public string Name; | ||
180 | public string Description; | ||
181 | public sbyte AssetType; | ||
182 | } | ||
183 | } | ||
184 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs new file mode 100644 index 0000000..8971b6e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Threading; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Framework.Communications.Cache; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | |||
39 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
40 | { | ||
41 | /// <summary> | ||
42 | /// Encapsulate the asynchronous requests for the assets required for an archive operation | ||
43 | /// </summary> | ||
44 | class AssetsRequest | ||
45 | { | ||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | /// <summary> | ||
49 | /// uuids to request | ||
50 | /// </summary> | ||
51 | protected ICollection<UUID> m_uuids; | ||
52 | |||
53 | /// <summary> | ||
54 | /// Callback used when all the assets requested have been received. | ||
55 | /// </summary> | ||
56 | protected AssetsRequestCallback m_assetsRequestCallback; | ||
57 | |||
58 | /// <summary> | ||
59 | /// Assets retrieved in this request | ||
60 | /// </summary> | ||
61 | protected Dictionary<UUID, AssetBase> m_assets = new Dictionary<UUID, AssetBase>(); | ||
62 | |||
63 | /// <summary> | ||
64 | /// Maintain a list of assets that could not be found. This will be passed back to the requester. | ||
65 | /// </summary> | ||
66 | protected List<UUID> m_notFoundAssetUuids = new List<UUID>(); | ||
67 | |||
68 | /// <summary> | ||
69 | /// Record the number of asset replies required so we know when we've finished | ||
70 | /// </summary> | ||
71 | private int m_repliesRequired; | ||
72 | |||
73 | /// <summary> | ||
74 | /// Asset cache used to request the assets | ||
75 | /// </summary> | ||
76 | protected IAssetCache m_assetCache; | ||
77 | |||
78 | protected internal AssetsRequest(ICollection<UUID> uuids, IAssetCache assetCache, AssetsRequestCallback assetsRequestCallback) | ||
79 | { | ||
80 | m_uuids = uuids; | ||
81 | m_assetsRequestCallback = assetsRequestCallback; | ||
82 | m_assetCache = assetCache; | ||
83 | m_repliesRequired = uuids.Count; | ||
84 | } | ||
85 | |||
86 | protected internal void Execute() | ||
87 | { | ||
88 | // We can stop here if there are no assets to fetch | ||
89 | if (m_repliesRequired == 0) | ||
90 | m_assetsRequestCallback(m_assets, m_notFoundAssetUuids); | ||
91 | |||
92 | foreach (UUID uuid in m_uuids) | ||
93 | { | ||
94 | m_assetCache.GetAsset(uuid, AssetRequestCallback, true); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /// <summary> | ||
99 | /// Called back by the asset cache when it has the asset | ||
100 | /// </summary> | ||
101 | /// <param name="assetID"></param> | ||
102 | /// <param name="asset"></param> | ||
103 | public void AssetRequestCallback(UUID assetID, AssetBase asset) | ||
104 | { | ||
105 | if (asset != null) | ||
106 | m_assets[assetID] = asset; | ||
107 | else | ||
108 | m_notFoundAssetUuids.Add(assetID); | ||
109 | |||
110 | //m_log.DebugFormat( | ||
111 | // "[ARCHIVER]: Received {0} assets and notification of {1} missing assets", m_assets.Count, m_notFoundAssetUuids.Count); | ||
112 | |||
113 | if (m_assets.Count + m_notFoundAssetUuids.Count == m_repliesRequired) | ||
114 | { | ||
115 | // We want to stop using the asset cache thread asap as we now need to do the actual work of producing the archive | ||
116 | Thread newThread = new Thread(PerformAssetsRequestCallback); | ||
117 | newThread.Name = "OpenSimulator archiving thread post assets receipt"; | ||
118 | newThread.Start(); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /// <summary> | ||
123 | /// Perform the callback on the original requester of the assets | ||
124 | /// </summary> | ||
125 | protected void PerformAssetsRequestCallback() | ||
126 | { | ||
127 | try | ||
128 | { | ||
129 | m_assetsRequestCallback(m_assets, m_notFoundAssetUuids); | ||
130 | } | ||
131 | catch (Exception e) | ||
132 | { | ||
133 | m_log.ErrorFormat( | ||
134 | "[ARCHIVER]: Terminating archive creation since asset requster callback failed with {0}", e); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs b/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs new file mode 100644 index 0000000..2580316 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/RegionSettingsSerializer.cs | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Text; | ||
31 | using System.Xml; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
36 | { | ||
37 | /// <summary> | ||
38 | /// Serialize and deserialize region settings for an archive file format. | ||
39 | /// </summary> | ||
40 | /// We didn't simply use automatic .NET serializagion for OpenSim.Framework.RegionSettings since this is really | ||
41 | /// a file format rather than an object serialization. | ||
42 | /// TODO: However, we could still have used separate non-framework classes here to read and write the xml | ||
43 | /// automatically rather than laboriously doing it by hand using XmlTextReader and Writer. Should switch to this | ||
44 | /// in the future. | ||
45 | public class RegionSettingsSerializer | ||
46 | { | ||
47 | protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Deserialize region settings | ||
51 | /// </summary> | ||
52 | /// <param name="serializedSettings"></param> | ||
53 | /// <returns></returns> | ||
54 | /// <exception cref="System.Xml.XmlException"></exception> | ||
55 | public static RegionSettings Deserialize(byte[] serializedSettings) | ||
56 | { | ||
57 | return Deserialize(m_asciiEncoding.GetString(serializedSettings, 0, serializedSettings.Length)); | ||
58 | } | ||
59 | |||
60 | /// <summary> | ||
61 | /// Deserialize region settings | ||
62 | /// </summary> | ||
63 | /// <param name="serializedSettings"></param> | ||
64 | /// <returns></returns> | ||
65 | /// <exception cref="System.Xml.XmlException"></exception> | ||
66 | public static RegionSettings Deserialize(string serializedSettings) | ||
67 | { | ||
68 | RegionSettings settings = new RegionSettings(); | ||
69 | |||
70 | StringReader sr = new StringReader(serializedSettings); | ||
71 | XmlTextReader xtr = new XmlTextReader(sr); | ||
72 | |||
73 | xtr.ReadStartElement("RegionSettings"); | ||
74 | |||
75 | xtr.ReadStartElement("General"); | ||
76 | |||
77 | while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) | ||
78 | { | ||
79 | switch (xtr.Name) | ||
80 | { | ||
81 | case "AllowDamage": | ||
82 | settings.AllowDamage = bool.Parse(xtr.ReadElementContentAsString()); | ||
83 | break; | ||
84 | case "AllowLandResell": | ||
85 | settings.AllowLandResell = bool.Parse(xtr.ReadElementContentAsString()); | ||
86 | break; | ||
87 | case "AllowLandJoinDivide": | ||
88 | settings.AllowLandJoinDivide = bool.Parse(xtr.ReadElementContentAsString()); | ||
89 | break; | ||
90 | case "BlockFly": | ||
91 | settings.BlockFly = bool.Parse(xtr.ReadElementContentAsString()); | ||
92 | break; | ||
93 | case "BlockLandShowInSearch": | ||
94 | settings.BlockShowInSearch = bool.Parse(xtr.ReadElementContentAsString()); | ||
95 | break; | ||
96 | case "BlockTerraform": | ||
97 | settings.BlockTerraform = bool.Parse(xtr.ReadElementContentAsString()); | ||
98 | break; | ||
99 | case "DisableCollisions": | ||
100 | settings.DisableCollisions = bool.Parse(xtr.ReadElementContentAsString()); | ||
101 | break; | ||
102 | case "DisablePhysics": | ||
103 | settings.DisablePhysics = bool.Parse(xtr.ReadElementContentAsString()); | ||
104 | break; | ||
105 | case "DisableScripts": | ||
106 | settings.DisableScripts = bool.Parse(xtr.ReadElementContentAsString()); | ||
107 | break; | ||
108 | case "MaturityRating": | ||
109 | settings.Maturity = int.Parse(xtr.ReadElementContentAsString()); | ||
110 | break; | ||
111 | case "RestrictPushing": | ||
112 | settings.RestrictPushing = bool.Parse(xtr.ReadElementContentAsString()); | ||
113 | break; | ||
114 | case "AgentLimit": | ||
115 | settings.AgentLimit = int.Parse(xtr.ReadElementContentAsString()); | ||
116 | break; | ||
117 | case "ObjectBonus": | ||
118 | settings.ObjectBonus = double.Parse(xtr.ReadElementContentAsString()); | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | xtr.ReadEndElement(); | ||
124 | xtr.ReadStartElement("GroundTextures"); | ||
125 | |||
126 | while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) | ||
127 | { | ||
128 | switch (xtr.Name) | ||
129 | { | ||
130 | case "Texture1": | ||
131 | settings.TerrainTexture1 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
132 | break; | ||
133 | case "Texture2": | ||
134 | settings.TerrainTexture2 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
135 | break; | ||
136 | case "Texture3": | ||
137 | settings.TerrainTexture3 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
138 | break; | ||
139 | case "Texture4": | ||
140 | settings.TerrainTexture4 = UUID.Parse(xtr.ReadElementContentAsString()); | ||
141 | break; | ||
142 | case "ElevationLowSW": | ||
143 | settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString()); | ||
144 | break; | ||
145 | case "ElevationLowNW": | ||
146 | settings.Elevation1NW = double.Parse(xtr.ReadElementContentAsString()); | ||
147 | break; | ||
148 | case "ElevationLowSE": | ||
149 | settings.Elevation1SE = double.Parse(xtr.ReadElementContentAsString()); | ||
150 | break; | ||
151 | case "ElevationLowNE": | ||
152 | settings.Elevation1NE = double.Parse(xtr.ReadElementContentAsString()); | ||
153 | break; | ||
154 | case "ElevationHighSW": | ||
155 | settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString()); | ||
156 | break; | ||
157 | case "ElevationHighNW": | ||
158 | settings.Elevation2NW = double.Parse(xtr.ReadElementContentAsString()); | ||
159 | break; | ||
160 | case "ElevationHighSE": | ||
161 | settings.Elevation2SE = double.Parse(xtr.ReadElementContentAsString()); | ||
162 | break; | ||
163 | case "ElevationHighNE": | ||
164 | settings.Elevation2NE = double.Parse(xtr.ReadElementContentAsString()); | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | xtr.ReadEndElement(); | ||
170 | xtr.ReadStartElement("Terrain"); | ||
171 | |||
172 | while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) | ||
173 | { | ||
174 | switch (xtr.Name) | ||
175 | { | ||
176 | case "WaterHeight": | ||
177 | settings.WaterHeight = double.Parse(xtr.ReadElementContentAsString()); | ||
178 | break; | ||
179 | case "TerrainRaiseLimit": | ||
180 | settings.TerrainRaiseLimit = double.Parse(xtr.ReadElementContentAsString()); | ||
181 | break; | ||
182 | case "TerrainLowerLimit": | ||
183 | settings.TerrainLowerLimit = double.Parse(xtr.ReadElementContentAsString()); | ||
184 | break; | ||
185 | case "UseEstateSun": | ||
186 | settings.UseEstateSun = bool.Parse(xtr.ReadElementContentAsString()); | ||
187 | break; | ||
188 | case "FixedSun": | ||
189 | settings.FixedSun = bool.Parse(xtr.ReadElementContentAsString()); | ||
190 | break; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | xtr.Close(); | ||
195 | sr.Close(); | ||
196 | |||
197 | return settings; | ||
198 | } | ||
199 | |||
200 | public static string Serialize(RegionSettings settings) | ||
201 | { | ||
202 | StringWriter sw = new StringWriter(); | ||
203 | XmlTextWriter xtw = new XmlTextWriter(sw); | ||
204 | xtw.Formatting = Formatting.Indented; | ||
205 | xtw.WriteStartDocument(); | ||
206 | |||
207 | xtw.WriteStartElement("RegionSettings"); | ||
208 | |||
209 | xtw.WriteStartElement("General"); | ||
210 | xtw.WriteElementString("AllowDamage", settings.AllowDamage.ToString()); | ||
211 | xtw.WriteElementString("AllowLandResell", settings.AllowLandResell.ToString()); | ||
212 | xtw.WriteElementString("AllowLandJoinDivide", settings.AllowLandJoinDivide.ToString()); | ||
213 | xtw.WriteElementString("BlockFly", settings.BlockFly.ToString()); | ||
214 | xtw.WriteElementString("BlockLandShowInSearch", settings.BlockShowInSearch.ToString()); | ||
215 | xtw.WriteElementString("BlockTerraform", settings.BlockTerraform.ToString()); | ||
216 | xtw.WriteElementString("DisableCollisions", settings.DisableCollisions.ToString()); | ||
217 | xtw.WriteElementString("DisablePhysics", settings.DisablePhysics.ToString()); | ||
218 | xtw.WriteElementString("DisableScripts", settings.DisableScripts.ToString()); | ||
219 | xtw.WriteElementString("MaturityRating", settings.Maturity.ToString()); | ||
220 | xtw.WriteElementString("RestrictPushing", settings.RestrictPushing.ToString()); | ||
221 | xtw.WriteElementString("AgentLimit", settings.AgentLimit.ToString()); | ||
222 | xtw.WriteElementString("ObjectBonus", settings.ObjectBonus.ToString()); | ||
223 | xtw.WriteEndElement(); | ||
224 | |||
225 | xtw.WriteStartElement("GroundTextures"); | ||
226 | xtw.WriteElementString("Texture1", settings.TerrainTexture1.ToString()); | ||
227 | xtw.WriteElementString("Texture2", settings.TerrainTexture2.ToString()); | ||
228 | xtw.WriteElementString("Texture3", settings.TerrainTexture3.ToString()); | ||
229 | xtw.WriteElementString("Texture4", settings.TerrainTexture4.ToString()); | ||
230 | xtw.WriteElementString("ElevationLowSW", settings.Elevation1SW.ToString()); | ||
231 | xtw.WriteElementString("ElevationLowNW", settings.Elevation1NW.ToString()); | ||
232 | xtw.WriteElementString("ElevationLowSE", settings.Elevation1SE.ToString()); | ||
233 | xtw.WriteElementString("ElevationLowNE", settings.Elevation1NE.ToString()); | ||
234 | xtw.WriteElementString("ElevationHighSW", settings.Elevation2SW.ToString()); | ||
235 | xtw.WriteElementString("ElevationHighNW", settings.Elevation2NW.ToString()); | ||
236 | xtw.WriteElementString("ElevationHighSE", settings.Elevation2SE.ToString()); | ||
237 | xtw.WriteElementString("ElevationHighNE", settings.Elevation2NE.ToString()); | ||
238 | xtw.WriteEndElement(); | ||
239 | |||
240 | xtw.WriteStartElement("Terrain"); | ||
241 | xtw.WriteElementString("WaterHeight", settings.WaterHeight.ToString()); | ||
242 | xtw.WriteElementString("TerrainRaiseLimit", settings.TerrainRaiseLimit.ToString()); | ||
243 | xtw.WriteElementString("TerrainLowerLimit", settings.TerrainLowerLimit.ToString()); | ||
244 | xtw.WriteElementString("UseEstateSun", settings.UseEstateSun.ToString()); | ||
245 | xtw.WriteElementString("FixedSun", settings.FixedSun.ToString()); | ||
246 | // XXX: Need to expose interface to get sun phase information from sun module | ||
247 | // xtw.WriteStartElement("SunPhase", | ||
248 | xtw.WriteEndElement(); | ||
249 | |||
250 | xtw.WriteEndElement(); | ||
251 | |||
252 | xtw.Close(); | ||
253 | sw.Close(); | ||
254 | |||
255 | return sw.ToString(); | ||
256 | } | ||
257 | } | ||
258 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs new file mode 100644 index 0000000..506d770 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveReader.cs | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Text; | ||
32 | using log4net; | ||
33 | |||
34 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
35 | { | ||
36 | /// <summary> | ||
37 | /// Temporary code to do the bare minimum required to read a tar archive for our purposes | ||
38 | /// </summary> | ||
39 | public class TarArchiveReader | ||
40 | { | ||
41 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
42 | |||
43 | public enum TarEntryType | ||
44 | { | ||
45 | TYPE_UNKNOWN = 0, | ||
46 | TYPE_NORMAL_FILE = 1, | ||
47 | TYPE_HARD_LINK = 2, | ||
48 | TYPE_SYMBOLIC_LINK = 3, | ||
49 | TYPE_CHAR_SPECIAL = 4, | ||
50 | TYPE_BLOCK_SPECIAL = 5, | ||
51 | TYPE_DIRECTORY = 6, | ||
52 | TYPE_FIFO = 7, | ||
53 | TYPE_CONTIGUOUS_FILE = 8, | ||
54 | } | ||
55 | |||
56 | protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); | ||
57 | |||
58 | /// <summary> | ||
59 | /// Binary reader for the underlying stream | ||
60 | /// </summary> | ||
61 | protected BinaryReader m_br; | ||
62 | |||
63 | /// <summary> | ||
64 | /// Used to trim off null chars | ||
65 | /// </summary> | ||
66 | protected char[] m_nullCharArray = new char[] { '\0' }; | ||
67 | |||
68 | /// <summary> | ||
69 | /// Generate a tar reader which reads from the given stream. | ||
70 | /// </summary> | ||
71 | /// <param name="s"></param> | ||
72 | public TarArchiveReader(Stream s) | ||
73 | { | ||
74 | m_br = new BinaryReader(s); | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Read the next entry in the tar file. | ||
79 | /// </summary> | ||
80 | /// <param name="filePath"></param> | ||
81 | /// <returns>the data for the entry. Returns null if there are no more entries</returns> | ||
82 | public byte[] ReadEntry(out string filePath, out TarEntryType entryType) | ||
83 | { | ||
84 | filePath = String.Empty; | ||
85 | entryType = TarEntryType.TYPE_UNKNOWN; | ||
86 | TarHeader header = ReadHeader(); | ||
87 | |||
88 | if (null == header) | ||
89 | return null; | ||
90 | |||
91 | entryType = header.EntryType; | ||
92 | filePath = header.FilePath; | ||
93 | byte[] data = m_br.ReadBytes(header.FileSize); | ||
94 | |||
95 | //m_log.DebugFormat("[TAR ARCHIVE READER]: filePath {0}, fileSize {1}", filePath, header.FileSize); | ||
96 | |||
97 | // Read the rest of the empty padding in the 512 byte block | ||
98 | if (header.FileSize % 512 != 0) | ||
99 | { | ||
100 | int paddingLeft = 512 - (header.FileSize % 512); | ||
101 | |||
102 | //m_log.DebugFormat("[TAR ARCHIVE READER]: Reading {0} padding bytes", paddingLeft); | ||
103 | |||
104 | m_br.ReadBytes(paddingLeft); | ||
105 | } | ||
106 | |||
107 | return data; | ||
108 | } | ||
109 | |||
110 | /// <summary> | ||
111 | /// Read the next 512 byte chunk of data as a tar header. | ||
112 | /// </summary> | ||
113 | /// <returns>A tar header struct. null if we have reached the end of the archive.</returns> | ||
114 | protected TarHeader ReadHeader() | ||
115 | { | ||
116 | byte[] header = m_br.ReadBytes(512); | ||
117 | |||
118 | // If we've reached the end of the archive we'll be in null block territory, which means | ||
119 | // the next byte will be 0 | ||
120 | if (header[0] == 0) | ||
121 | return null; | ||
122 | |||
123 | TarHeader tarHeader = new TarHeader(); | ||
124 | |||
125 | tarHeader.FilePath = m_asciiEncoding.GetString(header, 0, 100); | ||
126 | tarHeader.FilePath = tarHeader.FilePath.Trim(m_nullCharArray); | ||
127 | tarHeader.FileSize = ConvertOctalBytesToDecimal(header, 124, 11); | ||
128 | |||
129 | switch (header[156]) | ||
130 | { | ||
131 | case 0: | ||
132 | tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE; | ||
133 | break; | ||
134 | case (byte)'0': | ||
135 | tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE; | ||
136 | break; | ||
137 | case (byte)'1': | ||
138 | tarHeader.EntryType = TarEntryType.TYPE_HARD_LINK; | ||
139 | break; | ||
140 | case (byte)'2': | ||
141 | tarHeader.EntryType = TarEntryType.TYPE_SYMBOLIC_LINK; | ||
142 | break; | ||
143 | case (byte)'3': | ||
144 | tarHeader.EntryType = TarEntryType.TYPE_CHAR_SPECIAL; | ||
145 | break; | ||
146 | case (byte)'4': | ||
147 | tarHeader.EntryType = TarEntryType.TYPE_BLOCK_SPECIAL; | ||
148 | break; | ||
149 | case (byte)'5': | ||
150 | tarHeader.EntryType = TarEntryType.TYPE_DIRECTORY; | ||
151 | break; | ||
152 | case (byte)'6': | ||
153 | tarHeader.EntryType = TarEntryType.TYPE_FIFO; | ||
154 | break; | ||
155 | case (byte)'7': | ||
156 | tarHeader.EntryType = TarEntryType.TYPE_CONTIGUOUS_FILE; | ||
157 | break; | ||
158 | } | ||
159 | |||
160 | return tarHeader; | ||
161 | } | ||
162 | |||
163 | public void Close() | ||
164 | { | ||
165 | m_br.Close(); | ||
166 | } | ||
167 | |||
168 | /// <summary> | ||
169 | /// Convert octal bytes to a decimal representation | ||
170 | /// </summary> | ||
171 | /// <param name="bytes"></param> | ||
172 | /// <returns></returns> | ||
173 | public static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count) | ||
174 | { | ||
175 | string oString = m_asciiEncoding.GetString(bytes, startIndex, count); | ||
176 | |||
177 | int d = 0; | ||
178 | |||
179 | foreach (char c in oString) | ||
180 | { | ||
181 | d <<= 3; | ||
182 | d |= c - '0'; | ||
183 | } | ||
184 | |||
185 | return d; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | public class TarHeader | ||
190 | { | ||
191 | public string FilePath; | ||
192 | public int FileSize; | ||
193 | public TarArchiveReader.TarEntryType EntryType; | ||
194 | } | ||
195 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs new file mode 100644 index 0000000..437939e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/TarArchiveWriter.cs | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Text; | ||
32 | using System.Reflection; | ||
33 | using log4net; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Archiver | ||
36 | { | ||
37 | /// <summary> | ||
38 | /// Temporary code to produce a tar archive in tar v7 format | ||
39 | /// </summary> | ||
40 | public class TarArchiveWriter | ||
41 | { | ||
42 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
43 | |||
44 | protected Dictionary<string, byte[]> m_files = new Dictionary<string, byte[]>(); | ||
45 | |||
46 | protected static ASCIIEncoding m_asciiEncoding = new ASCIIEncoding(); | ||
47 | |||
48 | /// <summary> | ||
49 | /// Add a directory to the tar archive. We can only handle one path level right now! | ||
50 | /// </summary> | ||
51 | /// <param name="dirName"></param> | ||
52 | public void AddDir(string dirName) | ||
53 | { | ||
54 | // Directories are signalled by a final / | ||
55 | if (!dirName.EndsWith("/")) | ||
56 | dirName += "/"; | ||
57 | |||
58 | AddFile(dirName, new byte[0]); | ||
59 | } | ||
60 | |||
61 | /// <summary> | ||
62 | /// Add a file to the tar archive | ||
63 | /// </summary> | ||
64 | /// <param name="filePath"></param> | ||
65 | /// <param name="data"></param> | ||
66 | public void AddFile(string filePath, string data) | ||
67 | { | ||
68 | AddFile(filePath, m_asciiEncoding.GetBytes(data)); | ||
69 | } | ||
70 | |||
71 | /// <summary> | ||
72 | /// Add a file to the tar archive | ||
73 | /// </summary> | ||
74 | /// <param name="filePath"></param> | ||
75 | /// <param name="data"></param> | ||
76 | public void AddFile(string filePath, byte[] data) | ||
77 | { | ||
78 | m_files[filePath] = data; | ||
79 | } | ||
80 | |||
81 | /// <summary> | ||
82 | /// Write the raw tar archive data to a stream. The stream will be closed on completion. | ||
83 | /// </summary> | ||
84 | /// <param name="s">Stream to which to write the data</param> | ||
85 | /// <returns></returns> | ||
86 | public void WriteTar(Stream s) | ||
87 | { | ||
88 | BinaryWriter bw = new BinaryWriter(s); | ||
89 | |||
90 | foreach (string filePath in m_files.Keys) | ||
91 | { | ||
92 | byte[] header = new byte[512]; | ||
93 | byte[] data = m_files[filePath]; | ||
94 | |||
95 | // file path field (100) | ||
96 | byte[] nameBytes = m_asciiEncoding.GetBytes(filePath); | ||
97 | int nameSize = (nameBytes.Length >= 100) ? 100 : nameBytes.Length; | ||
98 | Array.Copy(nameBytes, header, nameSize); | ||
99 | |||
100 | // file mode (8) | ||
101 | byte[] modeBytes = m_asciiEncoding.GetBytes("0000777"); | ||
102 | Array.Copy(modeBytes, 0, header, 100, 7); | ||
103 | |||
104 | // owner user id (8) | ||
105 | byte[] ownerIdBytes = m_asciiEncoding.GetBytes("0000764"); | ||
106 | Array.Copy(ownerIdBytes, 0, header, 108, 7); | ||
107 | |||
108 | // group user id (8) | ||
109 | byte[] groupIdBytes = m_asciiEncoding.GetBytes("0000764"); | ||
110 | Array.Copy(groupIdBytes, 0, header, 116, 7); | ||
111 | |||
112 | // file size in bytes (12) | ||
113 | int fileSize = data.Length; | ||
114 | //m_log.DebugFormat("[TAR ARCHIVE WRITER]: File size of {0} is {1}", filePath, fileSize); | ||
115 | |||
116 | byte[] fileSizeBytes = ConvertDecimalToPaddedOctalBytes(fileSize, 11); | ||
117 | |||
118 | Array.Copy(fileSizeBytes, 0, header, 124, 11); | ||
119 | |||
120 | // last modification time (12) | ||
121 | byte[] lastModTimeBytes = m_asciiEncoding.GetBytes("11017037332"); | ||
122 | Array.Copy(lastModTimeBytes, 0, header, 136, 11); | ||
123 | |||
124 | // link indicator (1) | ||
125 | //header[156] = m_asciiEncoding.GetBytes("0")[0]; | ||
126 | if (filePath.EndsWith("/")) | ||
127 | { | ||
128 | header[156] = m_asciiEncoding.GetBytes("5")[0]; | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | header[156] = 0; | ||
133 | } | ||
134 | |||
135 | Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 329, 7); | ||
136 | Array.Copy(m_asciiEncoding.GetBytes("0000000"), 0, header, 337, 7); | ||
137 | |||
138 | // check sum for header block (8) [calculated last] | ||
139 | Array.Copy(m_asciiEncoding.GetBytes(" "), 0, header, 148, 8); | ||
140 | |||
141 | int checksum = 0; | ||
142 | foreach (byte b in header) | ||
143 | { | ||
144 | checksum += b; | ||
145 | } | ||
146 | |||
147 | //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Decimal header checksum is {0}", checksum); | ||
148 | |||
149 | byte[] checkSumBytes = ConvertDecimalToPaddedOctalBytes(checksum, 6); | ||
150 | |||
151 | Array.Copy(checkSumBytes, 0, header, 148, 6); | ||
152 | |||
153 | header[154] = 0; | ||
154 | |||
155 | // Write out header | ||
156 | bw.Write(header); | ||
157 | |||
158 | // Write out data | ||
159 | bw.Write(data); | ||
160 | |||
161 | if (data.Length % 512 != 0) | ||
162 | { | ||
163 | int paddingRequired = 512 - (data.Length % 512); | ||
164 | |||
165 | //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Padding data with {0} bytes", paddingRequired); | ||
166 | |||
167 | byte[] padding = new byte[paddingRequired]; | ||
168 | bw.Write(padding); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | //m_log.Debug("[TAR ARCHIVE WRITER]: Writing final consecutive 0 blocks"); | ||
173 | |||
174 | // Write two consecutive 0 blocks to end the archive | ||
175 | byte[] finalZeroPadding = new byte[1024]; | ||
176 | bw.Write(finalZeroPadding); | ||
177 | |||
178 | bw.Flush(); | ||
179 | bw.Close(); | ||
180 | } | ||
181 | |||
182 | public static byte[] ConvertDecimalToPaddedOctalBytes(int d, int padding) | ||
183 | { | ||
184 | string oString = ""; | ||
185 | |||
186 | while (d > 0) | ||
187 | { | ||
188 | oString = Convert.ToString((byte)'0' + d & 7) + oString; | ||
189 | d >>= 3; | ||
190 | } | ||
191 | |||
192 | while (oString.Length < padding) | ||
193 | { | ||
194 | oString = "0" + oString; | ||
195 | } | ||
196 | |||
197 | byte[] oBytes = m_asciiEncoding.GetBytes(oString); | ||
198 | |||
199 | return oBytes; | ||
200 | } | ||
201 | } | ||
202 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs new file mode 100644 index 0000000..a14e0f6 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Threading; | ||
31 | using NUnit.Framework; | ||
32 | using NUnit.Framework.SyntaxHelpers; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.CoreModules.World.Archiver; | ||
37 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
38 | using OpenSim.Region.CoreModules.World.Terrain; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenSim.Tests.Common.Setup; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.World.Archiver.Tests | ||
43 | { | ||
44 | [TestFixture] | ||
45 | public class ArchiverTests | ||
46 | { | ||
47 | private EventWaitHandle m_waitHandle = new AutoResetEvent(false); | ||
48 | |||
49 | private void SaveCompleted(string errorMessage) | ||
50 | { | ||
51 | m_waitHandle.Set(); | ||
52 | } | ||
53 | |||
54 | /// <summary> | ||
55 | /// Test saving a V0.2 OpenSim Region Archive. | ||
56 | /// </summary> | ||
57 | [Test] | ||
58 | public void TestSaveOarV0p2() | ||
59 | { | ||
60 | log4net.Config.XmlConfigurator.Configure(); | ||
61 | |||
62 | ArchiverModule archiverModule = new ArchiverModule(); | ||
63 | SerialiserModule serialiserModule = new SerialiserModule(); | ||
64 | TerrainModule terrainModule = new TerrainModule(); | ||
65 | |||
66 | Scene scene = SceneSetupHelpers.SetupScene(); | ||
67 | SceneSetupHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule); | ||
68 | |||
69 | SceneObjectPart part1; | ||
70 | |||
71 | // Create and add prim 1 | ||
72 | { | ||
73 | string partName = "My Little Pony"; | ||
74 | UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000015"); | ||
75 | PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); | ||
76 | Vector3 groupPosition = new Vector3(10, 20, 30); | ||
77 | Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); | ||
78 | Vector3 offsetPosition = new Vector3(5, 10, 15); | ||
79 | |||
80 | part1 | ||
81 | = new SceneObjectPart( | ||
82 | ownerId, shape, groupPosition, rotationOffset, offsetPosition); | ||
83 | part1.Name = partName; | ||
84 | |||
85 | scene.AddNewSceneObject(new SceneObjectGroup(part1), false); | ||
86 | } | ||
87 | |||
88 | SceneObjectPart part2; | ||
89 | |||
90 | // Create and add prim 2 | ||
91 | { | ||
92 | string partName = "Action Man"; | ||
93 | UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000016"); | ||
94 | PrimitiveBaseShape shape = PrimitiveBaseShape.CreateCylinder(); | ||
95 | Vector3 groupPosition = new Vector3(90, 80, 70); | ||
96 | Quaternion rotationOffset = new Quaternion(60, 70, 80, 90); | ||
97 | Vector3 offsetPosition = new Vector3(20, 25, 30); | ||
98 | |||
99 | part2 | ||
100 | = new SceneObjectPart( | ||
101 | ownerId, shape, groupPosition, rotationOffset, offsetPosition); | ||
102 | part2.Name = partName; | ||
103 | |||
104 | scene.AddNewSceneObject(new SceneObjectGroup(part2), false); | ||
105 | } | ||
106 | |||
107 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
108 | |||
109 | scene.EventManager.OnOarFileSaved += SaveCompleted; | ||
110 | archiverModule.ArchiveRegion(archiveWriteStream); | ||
111 | m_waitHandle.WaitOne(60000, true); | ||
112 | |||
113 | byte[] archive = archiveWriteStream.ToArray(); | ||
114 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
115 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
116 | |||
117 | bool gotControlFile = false; | ||
118 | bool gotObject1File = false; | ||
119 | bool gotObject2File = false; | ||
120 | string expectedObject1FileName = string.Format( | ||
121 | "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", | ||
122 | part1.Name, | ||
123 | Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z), | ||
124 | part1.UUID); | ||
125 | string expectedObject2FileName = string.Format( | ||
126 | "{0}_{1:000}-{2:000}-{3:000}__{4}.xml", | ||
127 | part2.Name, | ||
128 | Math.Round(part2.GroupPosition.X), Math.Round(part2.GroupPosition.Y), Math.Round(part2.GroupPosition.Z), | ||
129 | part2.UUID); | ||
130 | |||
131 | string filePath; | ||
132 | TarArchiveReader.TarEntryType tarEntryType; | ||
133 | |||
134 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
135 | { | ||
136 | if (ArchiveConstants.CONTROL_FILE_PATH == filePath) | ||
137 | { | ||
138 | gotControlFile = true; | ||
139 | } | ||
140 | else if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH)) | ||
141 | { | ||
142 | string fileName = filePath.Remove(0, ArchiveConstants.OBJECTS_PATH.Length); | ||
143 | |||
144 | if (fileName.StartsWith(part1.Name)) | ||
145 | { | ||
146 | Assert.That(fileName, Is.EqualTo(expectedObject1FileName)); | ||
147 | gotObject1File = true; | ||
148 | } | ||
149 | else if (fileName.StartsWith(part2.Name)) | ||
150 | { | ||
151 | Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); | ||
152 | gotObject2File = true; | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | Assert.That(gotControlFile, Is.True, "No control file in archive"); | ||
158 | Assert.That(gotObject1File, Is.True, "No object1 file in archive"); | ||
159 | Assert.That(gotObject2File, Is.True, "No object2 file in archive"); | ||
160 | |||
161 | // TODO: Test presence of more files and contents of files. | ||
162 | } | ||
163 | |||
164 | /// <summary> | ||
165 | /// Test loading a V0.2 OpenSim Region Archive. Does not yet do what it says on the tin. | ||
166 | /// </summary> | ||
167 | [Test] | ||
168 | public void TestLoadOarV0p2() | ||
169 | { | ||
170 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
171 | TarArchiveWriter tar = new TarArchiveWriter(); | ||
172 | |||
173 | tar.AddFile(ArchiveConstants.CONTROL_FILE_PATH, ArchiveWriteRequestExecution.Create0p2ControlFile()); | ||
174 | tar.WriteTar(archiveWriteStream); | ||
175 | |||
176 | MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray()); | ||
177 | |||
178 | ArchiverModule archiverModule = new ArchiverModule(); | ||
179 | |||
180 | Scene scene = SceneSetupHelpers.SetupScene(); | ||
181 | SceneSetupHelpers.SetupSceneModules(scene, archiverModule); | ||
182 | |||
183 | archiverModule.DearchiveRegion(archiveReadStream); | ||
184 | |||
185 | // TODO: Okay, so nothing is tested yet apart from the fact that it doesn't blow up | ||
186 | } | ||
187 | } | ||
188 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs new file mode 100644 index 0000000..8b15308 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs | |||
@@ -0,0 +1,1012 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | using System; | ||
28 | using System.Threading; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using OpenMetaverse; | ||
32 | using log4net; | ||
33 | using Nini.Config; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | |||
38 | namespace OpenSim.Region.CoreModules.World.Estate | ||
39 | { | ||
40 | public class EstateManagementModule : IEstateModule | ||
41 | { | ||
42 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
43 | |||
44 | private delegate void LookupUUIDS(List<UUID> uuidLst); | ||
45 | |||
46 | private Scene m_scene; | ||
47 | |||
48 | private EstateTerrainXferHandler TerrainUploader = null; | ||
49 | |||
50 | #region Packet Data Responders | ||
51 | |||
52 | private void sendDetailedEstateData(IClientAPI remote_client, UUID invoice) | ||
53 | { | ||
54 | uint sun = 0; | ||
55 | |||
56 | if (!m_scene.RegionInfo.EstateSettings.UseGlobalTime) | ||
57 | sun=(uint)(m_scene.RegionInfo.EstateSettings.SunPosition*1024.0) + 0x1800; | ||
58 | UUID estateOwner; | ||
59 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
60 | estateOwner = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
61 | else | ||
62 | estateOwner = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
63 | |||
64 | if (m_scene.Permissions.IsGod(remote_client.AgentId)) | ||
65 | estateOwner = remote_client.AgentId; | ||
66 | |||
67 | remote_client.SendDetailedEstateData(invoice, | ||
68 | m_scene.RegionInfo.EstateSettings.EstateName, | ||
69 | m_scene.RegionInfo.EstateSettings.EstateID, | ||
70 | m_scene.RegionInfo.EstateSettings.ParentEstateID, | ||
71 | GetEstateFlags(), | ||
72 | sun, | ||
73 | m_scene.RegionInfo.RegionSettings.Covenant, | ||
74 | m_scene.RegionInfo.EstateSettings.AbuseEmail, | ||
75 | estateOwner); | ||
76 | |||
77 | remote_client.SendEstateManagersList(invoice, | ||
78 | m_scene.RegionInfo.EstateSettings.EstateManagers, | ||
79 | m_scene.RegionInfo.EstateSettings.EstateID); | ||
80 | |||
81 | remote_client.SendBannedUserList(invoice, | ||
82 | m_scene.RegionInfo.EstateSettings.EstateBans, | ||
83 | m_scene.RegionInfo.EstateSettings.EstateID); | ||
84 | } | ||
85 | |||
86 | private void estateSetRegionInfoHandler(bool blockTerraform, bool noFly, bool allowDamage, bool blockLandResell, int maxAgents, float objectBonusFactor, | ||
87 | int matureLevel, bool restrictPushObject, bool allowParcelChanges) | ||
88 | { | ||
89 | if (blockTerraform) | ||
90 | m_scene.RegionInfo.RegionSettings.BlockTerraform = true; | ||
91 | else | ||
92 | m_scene.RegionInfo.RegionSettings.BlockTerraform = false; | ||
93 | |||
94 | if (noFly) | ||
95 | m_scene.RegionInfo.RegionSettings.BlockFly = true; | ||
96 | else | ||
97 | m_scene.RegionInfo.RegionSettings.BlockFly = false; | ||
98 | |||
99 | if (allowDamage) | ||
100 | m_scene.RegionInfo.RegionSettings.AllowDamage = true; | ||
101 | else | ||
102 | m_scene.RegionInfo.RegionSettings.AllowDamage = false; | ||
103 | |||
104 | if (blockLandResell) | ||
105 | m_scene.RegionInfo.RegionSettings.AllowLandResell = false; | ||
106 | else | ||
107 | m_scene.RegionInfo.RegionSettings.AllowLandResell = true; | ||
108 | |||
109 | m_scene.RegionInfo.RegionSettings.AgentLimit = (byte) maxAgents; | ||
110 | |||
111 | m_scene.RegionInfo.RegionSettings.ObjectBonus = objectBonusFactor; | ||
112 | |||
113 | if (matureLevel <= 13) | ||
114 | m_scene.RegionInfo.RegionSettings.Maturity = 0; | ||
115 | else | ||
116 | m_scene.RegionInfo.RegionSettings.Maturity = 1; | ||
117 | |||
118 | if (restrictPushObject) | ||
119 | m_scene.RegionInfo.RegionSettings.RestrictPushing = true; | ||
120 | else | ||
121 | m_scene.RegionInfo.RegionSettings.RestrictPushing = false; | ||
122 | |||
123 | if (allowParcelChanges) | ||
124 | m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide = true; | ||
125 | else | ||
126 | m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide = false; | ||
127 | |||
128 | m_scene.RegionInfo.RegionSettings.Save(); | ||
129 | |||
130 | sendRegionInfoPacketToAll(); | ||
131 | } | ||
132 | |||
133 | public void setEstateTerrainBaseTexture(IClientAPI remoteClient, int corner, UUID texture) | ||
134 | { | ||
135 | if (texture == UUID.Zero) | ||
136 | return; | ||
137 | |||
138 | switch (corner) | ||
139 | { | ||
140 | case 0: | ||
141 | m_scene.RegionInfo.RegionSettings.TerrainTexture1 = texture; | ||
142 | break; | ||
143 | case 1: | ||
144 | m_scene.RegionInfo.RegionSettings.TerrainTexture2 = texture; | ||
145 | break; | ||
146 | case 2: | ||
147 | m_scene.RegionInfo.RegionSettings.TerrainTexture3 = texture; | ||
148 | break; | ||
149 | case 3: | ||
150 | m_scene.RegionInfo.RegionSettings.TerrainTexture4 = texture; | ||
151 | break; | ||
152 | } | ||
153 | m_scene.RegionInfo.RegionSettings.Save(); | ||
154 | } | ||
155 | |||
156 | public void setEstateTerrainTextureHeights(IClientAPI client, int corner, float lowValue, float highValue) | ||
157 | { | ||
158 | switch (corner) | ||
159 | { | ||
160 | case 0: | ||
161 | m_scene.RegionInfo.RegionSettings.Elevation1SW = lowValue; | ||
162 | m_scene.RegionInfo.RegionSettings.Elevation2SW = highValue; | ||
163 | break; | ||
164 | case 1: | ||
165 | m_scene.RegionInfo.RegionSettings.Elevation1NW = lowValue; | ||
166 | m_scene.RegionInfo.RegionSettings.Elevation2NW = highValue; | ||
167 | break; | ||
168 | case 2: | ||
169 | m_scene.RegionInfo.RegionSettings.Elevation1SE = lowValue; | ||
170 | m_scene.RegionInfo.RegionSettings.Elevation2SE = highValue; | ||
171 | break; | ||
172 | case 3: | ||
173 | m_scene.RegionInfo.RegionSettings.Elevation1NE = lowValue; | ||
174 | m_scene.RegionInfo.RegionSettings.Elevation2NE = highValue; | ||
175 | break; | ||
176 | } | ||
177 | m_scene.RegionInfo.RegionSettings.Save(); | ||
178 | } | ||
179 | |||
180 | private void handleCommitEstateTerrainTextureRequest(IClientAPI remoteClient) | ||
181 | { | ||
182 | sendRegionHandshakeToAll(); | ||
183 | } | ||
184 | |||
185 | public void setRegionTerrainSettings(float WaterHeight, | ||
186 | float TerrainRaiseLimit, float TerrainLowerLimit, | ||
187 | bool UseEstateSun, bool UseFixedSun, float SunHour, | ||
188 | bool UseGlobal, bool EstateFixedSun, float EstateSunHour) | ||
189 | { | ||
190 | // Water Height | ||
191 | m_scene.RegionInfo.RegionSettings.WaterHeight = WaterHeight; | ||
192 | |||
193 | // Terraforming limits | ||
194 | m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit = TerrainRaiseLimit; | ||
195 | m_scene.RegionInfo.RegionSettings.TerrainLowerLimit = TerrainLowerLimit; | ||
196 | |||
197 | // Time of day / fixed sun | ||
198 | m_scene.RegionInfo.RegionSettings.UseEstateSun = UseEstateSun; | ||
199 | m_scene.RegionInfo.RegionSettings.FixedSun = UseFixedSun; | ||
200 | m_scene.RegionInfo.RegionSettings.SunPosition = SunHour; | ||
201 | |||
202 | m_scene.EventManager.TriggerEstateToolsTimeUpdate(m_scene.RegionInfo.RegionHandle, UseFixedSun, UseEstateSun, SunHour); | ||
203 | |||
204 | //m_log.Debug("[ESTATE]: UFS: " + UseFixedSun.ToString()); | ||
205 | //m_log.Debug("[ESTATE]: SunHour: " + SunHour.ToString()); | ||
206 | |||
207 | sendRegionInfoPacketToAll(); | ||
208 | m_scene.RegionInfo.RegionSettings.Save(); | ||
209 | } | ||
210 | |||
211 | private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds) | ||
212 | { | ||
213 | m_scene.Restart(timeInSeconds); | ||
214 | } | ||
215 | |||
216 | private void handleChangeEstateCovenantRequest(IClientAPI remoteClient, UUID estateCovenantID) | ||
217 | { | ||
218 | m_scene.RegionInfo.RegionSettings.Covenant = estateCovenantID; | ||
219 | m_scene.RegionInfo.RegionSettings.Save(); | ||
220 | } | ||
221 | |||
222 | private void handleEstateAccessDeltaRequest(IClientAPI remote_client, UUID invoice, int estateAccessType, UUID user) | ||
223 | { | ||
224 | // EstateAccessDelta handles Estate Managers, Sim Access, Sim Banlist, allowed Groups.. etc. | ||
225 | |||
226 | if (user == m_scene.RegionInfo.EstateSettings.EstateOwner) | ||
227 | return; // never process EO | ||
228 | if (user == m_scene.RegionInfo.MasterAvatarAssignedUUID) | ||
229 | return; // never process owner | ||
230 | |||
231 | switch (estateAccessType) | ||
232 | { | ||
233 | case 64: | ||
234 | if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false) || m_scene.Permissions.BypassPermissions()) | ||
235 | { | ||
236 | EstateBan[] banlistcheck = m_scene.RegionInfo.EstateSettings.EstateBans; | ||
237 | |||
238 | bool alreadyInList = false; | ||
239 | |||
240 | for (int i = 0; i < banlistcheck.Length; i++) | ||
241 | { | ||
242 | if (user == banlistcheck[i].bannedUUID) | ||
243 | { | ||
244 | alreadyInList = true; | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | } | ||
249 | if (!alreadyInList) | ||
250 | { | ||
251 | |||
252 | EstateBan item = new EstateBan(); | ||
253 | |||
254 | item.bannedUUID = user; | ||
255 | item.estateID = m_scene.RegionInfo.EstateSettings.EstateID; | ||
256 | item.bannedIP = "0.0.0.0"; | ||
257 | item.bannedIPHostMask = "0.0.0.0"; | ||
258 | |||
259 | m_scene.RegionInfo.EstateSettings.AddBan(item); | ||
260 | m_scene.RegionInfo.EstateSettings.Save(); | ||
261 | |||
262 | ScenePresence s = m_scene.GetScenePresence(user); | ||
263 | if (s != null) | ||
264 | { | ||
265 | if (!s.IsChildAgent) | ||
266 | { | ||
267 | s.ControllingClient.SendTeleportLocationStart(); | ||
268 | m_scene.TeleportClientHome(user, s.ControllingClient); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | } | ||
273 | else | ||
274 | { | ||
275 | remote_client.SendAlertMessage("User is already on the region ban list"); | ||
276 | } | ||
277 | //m_scene.RegionInfo.regionBanlist.Add(Manager(user); | ||
278 | remote_client.SendBannedUserList(invoice, m_scene.RegionInfo.EstateSettings.EstateBans, m_scene.RegionInfo.EstateSettings.EstateID); | ||
279 | } | ||
280 | else | ||
281 | { | ||
282 | remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); | ||
283 | } | ||
284 | break; | ||
285 | case 128: | ||
286 | if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, false) || m_scene.Permissions.BypassPermissions()) | ||
287 | { | ||
288 | EstateBan[] banlistcheck = m_scene.RegionInfo.EstateSettings.EstateBans; | ||
289 | |||
290 | bool alreadyInList = false; | ||
291 | EstateBan listitem = null; | ||
292 | |||
293 | for (int i = 0; i < banlistcheck.Length; i++) | ||
294 | { | ||
295 | if (user == banlistcheck[i].bannedUUID) | ||
296 | { | ||
297 | alreadyInList = true; | ||
298 | listitem = banlistcheck[i]; | ||
299 | break; | ||
300 | } | ||
301 | |||
302 | } | ||
303 | if (alreadyInList && listitem != null) | ||
304 | { | ||
305 | m_scene.RegionInfo.EstateSettings.RemoveBan(listitem.bannedUUID); | ||
306 | m_scene.RegionInfo.EstateSettings.Save(); | ||
307 | } | ||
308 | else | ||
309 | { | ||
310 | remote_client.SendAlertMessage("User is not on the region ban list"); | ||
311 | } | ||
312 | //m_scene.RegionInfo.regionBanlist.Add(Manager(user); | ||
313 | remote_client.SendBannedUserList(invoice, m_scene.RegionInfo.EstateSettings.EstateBans, m_scene.RegionInfo.EstateSettings.EstateID); | ||
314 | } | ||
315 | else | ||
316 | { | ||
317 | remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); | ||
318 | } | ||
319 | break; | ||
320 | case 256: | ||
321 | |||
322 | if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || m_scene.Permissions.BypassPermissions()) | ||
323 | { | ||
324 | m_scene.RegionInfo.EstateSettings.AddEstateManager(user); | ||
325 | m_scene.RegionInfo.EstateSettings.Save(); | ||
326 | remote_client.SendEstateManagersList(invoice, m_scene.RegionInfo.EstateSettings.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateID); | ||
327 | } | ||
328 | else | ||
329 | { | ||
330 | remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); | ||
331 | } | ||
332 | |||
333 | break; | ||
334 | case 512: | ||
335 | if (m_scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true) || m_scene.Permissions.BypassPermissions()) | ||
336 | { | ||
337 | m_scene.RegionInfo.EstateSettings.RemoveEstateManager(user); | ||
338 | m_scene.RegionInfo.EstateSettings.Save(); | ||
339 | |||
340 | remote_client.SendEstateManagersList(invoice, m_scene.RegionInfo.EstateSettings.EstateManagers, m_scene.RegionInfo.EstateSettings.EstateID); | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); | ||
345 | } | ||
346 | break; | ||
347 | |||
348 | default: | ||
349 | |||
350 | m_log.ErrorFormat("EstateOwnerMessage: Unknown EstateAccessType requested in estateAccessDelta: {0}", estateAccessType.ToString()); | ||
351 | break; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | private void SendSimulatorBlueBoxMessage( | ||
356 | IClientAPI remote_client, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message) | ||
357 | { | ||
358 | IDialogModule dm = m_scene.RequestModuleInterface<IDialogModule>(); | ||
359 | |||
360 | if (dm != null) | ||
361 | dm.SendNotificationToUsersInRegion(senderID, senderName, message); | ||
362 | } | ||
363 | |||
364 | private void SendEstateBlueBoxMessage( | ||
365 | IClientAPI remote_client, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message) | ||
366 | { | ||
367 | IDialogModule dm = m_scene.RequestModuleInterface<IDialogModule>(); | ||
368 | |||
369 | if (dm != null) | ||
370 | dm.SendNotificationToUsersInEstate(senderID, senderName, message); | ||
371 | } | ||
372 | |||
373 | private void handleEstateDebugRegionRequest(IClientAPI remote_client, UUID invoice, UUID senderID, bool scripted, bool collisionEvents, bool physics) | ||
374 | { | ||
375 | if (physics) | ||
376 | m_scene.RegionInfo.RegionSettings.DisablePhysics = true; | ||
377 | else | ||
378 | m_scene.RegionInfo.RegionSettings.DisablePhysics = false; | ||
379 | |||
380 | if (scripted) | ||
381 | m_scene.RegionInfo.RegionSettings.DisableScripts = true; | ||
382 | else | ||
383 | m_scene.RegionInfo.RegionSettings.DisableScripts = false; | ||
384 | |||
385 | if (collisionEvents) | ||
386 | m_scene.RegionInfo.RegionSettings.DisableCollisions = true; | ||
387 | else | ||
388 | m_scene.RegionInfo.RegionSettings.DisableCollisions = false; | ||
389 | |||
390 | |||
391 | m_scene.RegionInfo.RegionSettings.Save(); | ||
392 | |||
393 | m_scene.SetSceneCoreDebug(scripted, collisionEvents, physics); | ||
394 | } | ||
395 | |||
396 | private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey) | ||
397 | { | ||
398 | if (prey != UUID.Zero) | ||
399 | { | ||
400 | ScenePresence s = m_scene.GetScenePresence(prey); | ||
401 | if (s != null) | ||
402 | { | ||
403 | s.ControllingClient.SendTeleportLocationStart(); | ||
404 | m_scene.TeleportClientHome(prey, s.ControllingClient); | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | |||
409 | private void handleEstateTeleportAllUsersHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID) | ||
410 | { | ||
411 | // Get a fresh list that will not change as people get teleported away | ||
412 | List<ScenePresence> prescences = m_scene.GetScenePresences(); | ||
413 | foreach (ScenePresence p in prescences) | ||
414 | { | ||
415 | if (p.UUID != senderID) | ||
416 | { | ||
417 | // make sure they are still there, we could be working down a long list | ||
418 | ScenePresence s = m_scene.GetScenePresence(p.UUID); | ||
419 | if (s != null) | ||
420 | { | ||
421 | // Also make sure they are actually in the region | ||
422 | if (!s.IsChildAgent) | ||
423 | { | ||
424 | s.ControllingClient.SendTeleportLocationStart(); | ||
425 | m_scene.TeleportClientHome(s.UUID, s.ControllingClient); | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | private void AbortTerrainXferHandler(IClientAPI remoteClient, ulong XferID) | ||
432 | { | ||
433 | if (TerrainUploader != null) | ||
434 | { | ||
435 | lock (TerrainUploader) | ||
436 | { | ||
437 | if (XferID == TerrainUploader.XferID) | ||
438 | { | ||
439 | remoteClient.OnXferReceive -= TerrainUploader.XferReceive; | ||
440 | remoteClient.OnAbortXfer -= AbortTerrainXferHandler; | ||
441 | TerrainUploader.TerrainUploadDone -= HandleTerrainApplication; | ||
442 | |||
443 | TerrainUploader = null; | ||
444 | remoteClient.SendAlertMessage("Terrain Upload aborted by the client"); | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | |||
449 | } | ||
450 | private void HandleTerrainApplication(string filename, byte[] terrainData, IClientAPI remoteClient) | ||
451 | { | ||
452 | lock (TerrainUploader) | ||
453 | { | ||
454 | remoteClient.OnXferReceive -= TerrainUploader.XferReceive; | ||
455 | remoteClient.OnAbortXfer -= AbortTerrainXferHandler; | ||
456 | TerrainUploader.TerrainUploadDone -= HandleTerrainApplication; | ||
457 | |||
458 | TerrainUploader = null; | ||
459 | } | ||
460 | remoteClient.SendAlertMessage("Terrain Upload Complete. Loading...."); | ||
461 | OpenSim.Region.CoreModules.World.Terrain.ITerrainModule terr = m_scene.RequestModuleInterface<OpenSim.Region.CoreModules.World.Terrain.ITerrainModule>(); | ||
462 | |||
463 | if (terr != null) | ||
464 | { | ||
465 | m_log.Warn("[CLIENT]: Got Request to Send Terrain in region " + m_scene.RegionInfo.RegionName); | ||
466 | if (System.IO.File.Exists(Util.dataDir() + "/terrain.raw")) | ||
467 | { | ||
468 | System.IO.File.Delete(Util.dataDir() + "/terrain.raw"); | ||
469 | } | ||
470 | try | ||
471 | { | ||
472 | System.IO.FileStream input = new System.IO.FileStream(Util.dataDir() + "/terrain.raw", System.IO.FileMode.CreateNew); | ||
473 | input.Write(terrainData, 0, terrainData.Length); | ||
474 | input.Close(); | ||
475 | } | ||
476 | catch (System.IO.IOException e) | ||
477 | { | ||
478 | m_log.ErrorFormat("[TERRAIN]: Error Saving a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString()); | ||
479 | remoteClient.SendAlertMessage("There was an IO Exception loading your terrain. Please check free space"); | ||
480 | |||
481 | return; | ||
482 | } | ||
483 | catch (System.Security.SecurityException e) | ||
484 | { | ||
485 | m_log.ErrorFormat("[TERRAIN]: Error Saving a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString()); | ||
486 | remoteClient.SendAlertMessage("There was a security Exception loading your terrain. Please check the security on the simulator drive"); | ||
487 | |||
488 | return; | ||
489 | } | ||
490 | catch (System.UnauthorizedAccessException e) | ||
491 | { | ||
492 | m_log.ErrorFormat("[TERRAIN]: Error Saving a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString()); | ||
493 | remoteClient.SendAlertMessage("There was a security Exception loading your terrain. Please check the security on the simulator drive"); | ||
494 | |||
495 | return; | ||
496 | } | ||
497 | |||
498 | |||
499 | |||
500 | |||
501 | try | ||
502 | { | ||
503 | terr.LoadFromFile(Util.dataDir() + "/terrain.raw"); | ||
504 | remoteClient.SendAlertMessage("Your terrain was loaded. Give it a minute or two to apply"); | ||
505 | } | ||
506 | catch (Exception e) | ||
507 | { | ||
508 | m_log.ErrorFormat("[TERRAIN]: Error loading a terrain file uploaded via the estate tools. It gave us the following error: {0}", e.ToString()); | ||
509 | remoteClient.SendAlertMessage("There was a general error loading your terrain. Please fix the terrain file and try again"); | ||
510 | } | ||
511 | |||
512 | } | ||
513 | else | ||
514 | { | ||
515 | remoteClient.SendAlertMessage("Unable to apply terrain. Cannot get an instance of the terrain module"); | ||
516 | } | ||
517 | |||
518 | |||
519 | |||
520 | } | ||
521 | |||
522 | private void handleUploadTerrain(IClientAPI remote_client, string clientFileName) | ||
523 | { | ||
524 | |||
525 | if (TerrainUploader == null) | ||
526 | { | ||
527 | |||
528 | TerrainUploader = new EstateTerrainXferHandler(remote_client, clientFileName); | ||
529 | lock (TerrainUploader) | ||
530 | { | ||
531 | remote_client.OnXferReceive += TerrainUploader.XferReceive; | ||
532 | remote_client.OnAbortXfer += AbortTerrainXferHandler; | ||
533 | TerrainUploader.TerrainUploadDone += HandleTerrainApplication; | ||
534 | } | ||
535 | TerrainUploader.RequestStartXfer(remote_client); | ||
536 | |||
537 | } | ||
538 | else | ||
539 | { | ||
540 | remote_client.SendAlertMessage("Another Terrain Upload is in progress. Please wait your turn!"); | ||
541 | } | ||
542 | |||
543 | } | ||
544 | private void handleTerrainRequest(IClientAPI remote_client, string clientFileName) | ||
545 | { | ||
546 | // Save terrain here | ||
547 | OpenSim.Region.CoreModules.World.Terrain.ITerrainModule terr = m_scene.RequestModuleInterface<OpenSim.Region.CoreModules.World.Terrain.ITerrainModule>(); | ||
548 | |||
549 | if (terr != null) | ||
550 | { | ||
551 | m_log.Warn("[CLIENT]: Got Request to Send Terrain in region " + m_scene.RegionInfo.RegionName); | ||
552 | if (System.IO.File.Exists(Util.dataDir() + "/terrain.raw")) | ||
553 | { | ||
554 | System.IO.File.Delete(Util.dataDir() + "/terrain.raw"); | ||
555 | } | ||
556 | terr.SaveToFile(Util.dataDir() + "/terrain.raw"); | ||
557 | |||
558 | System.IO.FileStream input = new System.IO.FileStream(Util.dataDir() + "/terrain.raw", System.IO.FileMode.Open); | ||
559 | byte[] bdata = new byte[input.Length]; | ||
560 | input.Read(bdata, 0, (int)input.Length); | ||
561 | remote_client.SendAlertMessage("Terrain file written, starting download..."); | ||
562 | m_scene.XferManager.AddNewFile("terrain.raw", bdata); | ||
563 | // Tell client about it | ||
564 | m_log.Warn("[CLIENT]: Sending Terrain to " + remote_client.Name); | ||
565 | remote_client.SendInitiateDownload("terrain.raw", clientFileName); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | private void HandleRegionInfoRequest(IClientAPI remote_client) | ||
570 | { | ||
571 | RegionInfoForEstateMenuArgs args = new RegionInfoForEstateMenuArgs(); | ||
572 | args.billableFactor = m_scene.RegionInfo.EstateSettings.BillableFactor; | ||
573 | args.estateID = m_scene.RegionInfo.EstateSettings.EstateID; | ||
574 | args.maxAgents = (byte)m_scene.RegionInfo.RegionSettings.AgentLimit; | ||
575 | args.objectBonusFactor = (float)m_scene.RegionInfo.RegionSettings.ObjectBonus; | ||
576 | args.parentEstateID = m_scene.RegionInfo.EstateSettings.ParentEstateID; | ||
577 | args.pricePerMeter = m_scene.RegionInfo.EstateSettings.PricePerMeter; | ||
578 | args.redirectGridX = m_scene.RegionInfo.EstateSettings.RedirectGridX; | ||
579 | args.redirectGridY = m_scene.RegionInfo.EstateSettings.RedirectGridY; | ||
580 | args.regionFlags = GetRegionFlags(); | ||
581 | byte mature = 13; | ||
582 | if (m_scene.RegionInfo.RegionSettings.Maturity == 1) | ||
583 | mature = 21; | ||
584 | args.simAccess = mature; | ||
585 | |||
586 | args.sunHour = (float)m_scene.RegionInfo.RegionSettings.SunPosition; | ||
587 | args.terrainLowerLimit = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; | ||
588 | args.terrainRaiseLimit = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; | ||
589 | args.useEstateSun = m_scene.RegionInfo.RegionSettings.UseEstateSun; | ||
590 | args.waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; | ||
591 | args.simName = m_scene.RegionInfo.RegionName; | ||
592 | |||
593 | remote_client.SendRegionInfoToEstateMenu(args); | ||
594 | } | ||
595 | |||
596 | private void HandleEstateCovenantRequest(IClientAPI remote_client) | ||
597 | { | ||
598 | remote_client.SendEstateCovenantInformation(m_scene.RegionInfo.RegionSettings.Covenant); | ||
599 | } | ||
600 | |||
601 | private void HandleLandStatRequest(int parcelID, uint reportType, uint requestFlags, string filter, IClientAPI remoteClient) | ||
602 | { | ||
603 | Dictionary<uint, float> SceneData = new Dictionary<uint,float>(); | ||
604 | List<UUID> uuidNameLookupList = new List<UUID>(); | ||
605 | |||
606 | if (reportType == 1) | ||
607 | { | ||
608 | SceneData = m_scene.PhysicsScene.GetTopColliders(); | ||
609 | } | ||
610 | else if (reportType == 0) | ||
611 | { | ||
612 | SceneData = m_scene.m_sceneGraph.GetTopScripts(); | ||
613 | } | ||
614 | |||
615 | List<LandStatReportItem> SceneReport = new List<LandStatReportItem>(); | ||
616 | lock (SceneData) | ||
617 | { | ||
618 | foreach (uint obj in SceneData.Keys) | ||
619 | { | ||
620 | SceneObjectPart prt = m_scene.GetSceneObjectPart(obj); | ||
621 | if (prt != null) | ||
622 | { | ||
623 | if (prt.ParentGroup != null) | ||
624 | { | ||
625 | SceneObjectGroup sog = prt.ParentGroup; | ||
626 | if (sog != null) | ||
627 | { | ||
628 | LandStatReportItem lsri = new LandStatReportItem(); | ||
629 | lsri.LocationX = sog.AbsolutePosition.X; | ||
630 | lsri.LocationY = sog.AbsolutePosition.Y; | ||
631 | lsri.LocationZ = sog.AbsolutePosition.Z; | ||
632 | lsri.Score = SceneData[obj]; | ||
633 | lsri.TaskID = sog.UUID; | ||
634 | lsri.TaskLocalID = sog.LocalId; | ||
635 | lsri.TaskName = sog.GetPartName(obj); | ||
636 | if (m_scene.CommsManager.UUIDNameCachedTest(sog.OwnerID)) | ||
637 | { | ||
638 | lsri.OwnerName = m_scene.CommsManager.UUIDNameRequestString(sog.OwnerID); | ||
639 | } | ||
640 | else | ||
641 | { | ||
642 | lsri.OwnerName = "waiting"; | ||
643 | lock (uuidNameLookupList) | ||
644 | uuidNameLookupList.Add(sog.OwnerID); | ||
645 | } | ||
646 | |||
647 | if (filter.Length != 0) | ||
648 | { | ||
649 | if ((lsri.OwnerName.Contains(filter) || lsri.TaskName.Contains(filter))) | ||
650 | { | ||
651 | } | ||
652 | else | ||
653 | { | ||
654 | continue; | ||
655 | } | ||
656 | } | ||
657 | |||
658 | SceneReport.Add(lsri); | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | |||
663 | } | ||
664 | } | ||
665 | remoteClient.SendLandStatReply(reportType, requestFlags, (uint)SceneReport.Count,SceneReport.ToArray()); | ||
666 | |||
667 | if (uuidNameLookupList.Count > 0) | ||
668 | LookupUUID(uuidNameLookupList); | ||
669 | } | ||
670 | |||
671 | private void LookupUUIDSCompleted(IAsyncResult iar) | ||
672 | { | ||
673 | LookupUUIDS icon = (LookupUUIDS)iar.AsyncState; | ||
674 | icon.EndInvoke(iar); | ||
675 | } | ||
676 | private void LookupUUID(List<UUID> uuidLst) | ||
677 | { | ||
678 | LookupUUIDS d = LookupUUIDsAsync; | ||
679 | |||
680 | d.BeginInvoke(uuidLst, | ||
681 | LookupUUIDSCompleted, | ||
682 | d); | ||
683 | } | ||
684 | private void LookupUUIDsAsync(List<UUID> uuidLst) | ||
685 | { | ||
686 | UUID[] uuidarr = new UUID[0]; | ||
687 | |||
688 | lock (uuidLst) | ||
689 | { | ||
690 | uuidarr = uuidLst.ToArray(); | ||
691 | } | ||
692 | |||
693 | for (int i = 0; i < uuidarr.Length; i++) | ||
694 | { | ||
695 | // string lookupname = m_scene.CommsManager.UUIDNameRequestString(uuidarr[i]); | ||
696 | m_scene.CommsManager.UUIDNameRequestString(uuidarr[i]); | ||
697 | // we drop it. It gets cached though... so we're ready for the next request. | ||
698 | } | ||
699 | } | ||
700 | #endregion | ||
701 | |||
702 | #region Outgoing Packets | ||
703 | |||
704 | public void sendRegionInfoPacketToAll() | ||
705 | { | ||
706 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
707 | |||
708 | for (int i = 0; i < avatars.Count; i++) | ||
709 | { | ||
710 | HandleRegionInfoRequest(avatars[i].ControllingClient); ; | ||
711 | } | ||
712 | } | ||
713 | |||
714 | public void sendRegionHandshake(IClientAPI remoteClient) | ||
715 | { | ||
716 | RegionHandshakeArgs args = new RegionHandshakeArgs(); | ||
717 | |||
718 | args.isEstateManager = m_scene.RegionInfo.EstateSettings.IsEstateManager(remoteClient.AgentId); | ||
719 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero && m_scene.RegionInfo.EstateSettings.EstateOwner == remoteClient.AgentId) | ||
720 | args.isEstateManager = true; | ||
721 | |||
722 | args.billableFactor = m_scene.RegionInfo.EstateSettings.BillableFactor; | ||
723 | args.terrainStartHeight0 = (float)m_scene.RegionInfo.RegionSettings.Elevation1SW; | ||
724 | args.terrainHeightRange0 = (float)m_scene.RegionInfo.RegionSettings.Elevation2SW; | ||
725 | args.terrainStartHeight1 = (float)m_scene.RegionInfo.RegionSettings.Elevation1NW; | ||
726 | args.terrainHeightRange1 = (float)m_scene.RegionInfo.RegionSettings.Elevation2NW; | ||
727 | args.terrainStartHeight2 = (float)m_scene.RegionInfo.RegionSettings.Elevation1SE; | ||
728 | args.terrainHeightRange2 = (float)m_scene.RegionInfo.RegionSettings.Elevation2SE; | ||
729 | args.terrainStartHeight3 = (float)m_scene.RegionInfo.RegionSettings.Elevation1NE; | ||
730 | args.terrainHeightRange3 = (float)m_scene.RegionInfo.RegionSettings.Elevation2NE; | ||
731 | byte mature = 13; | ||
732 | if (m_scene.RegionInfo.RegionSettings.Maturity == 1) | ||
733 | mature = 21; | ||
734 | args.simAccess = mature; | ||
735 | args.waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; | ||
736 | |||
737 | args.regionFlags = GetRegionFlags(); | ||
738 | args.regionName = m_scene.RegionInfo.RegionName; | ||
739 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
740 | args.SimOwner = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
741 | else | ||
742 | args.SimOwner = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
743 | |||
744 | // Fudge estate owner | ||
745 | //if (m_scene.Permissions.IsGod(remoteClient.AgentId)) | ||
746 | // args.SimOwner = remoteClient.AgentId; | ||
747 | |||
748 | args.terrainBase0 = UUID.Zero; | ||
749 | args.terrainBase1 = UUID.Zero; | ||
750 | args.terrainBase2 = UUID.Zero; | ||
751 | args.terrainBase3 = UUID.Zero; | ||
752 | args.terrainDetail0 = m_scene.RegionInfo.RegionSettings.TerrainTexture1; | ||
753 | args.terrainDetail1 = m_scene.RegionInfo.RegionSettings.TerrainTexture2; | ||
754 | args.terrainDetail2 = m_scene.RegionInfo.RegionSettings.TerrainTexture3; | ||
755 | args.terrainDetail3 = m_scene.RegionInfo.RegionSettings.TerrainTexture4; | ||
756 | |||
757 | remoteClient.SendRegionHandshake(m_scene.RegionInfo,args); | ||
758 | } | ||
759 | |||
760 | public void sendRegionHandshakeToAll() | ||
761 | { | ||
762 | m_scene.Broadcast(sendRegionHandshake); | ||
763 | } | ||
764 | |||
765 | public void handleEstateChangeInfo(IClientAPI remoteClient, UUID invoice, UUID senderID, UInt32 parms1, UInt32 parms2) | ||
766 | { | ||
767 | if (parms2 == 0) | ||
768 | { | ||
769 | m_scene.RegionInfo.EstateSettings.UseGlobalTime = true; | ||
770 | m_scene.RegionInfo.EstateSettings.SunPosition = 0.0; | ||
771 | } | ||
772 | else | ||
773 | { | ||
774 | m_scene.RegionInfo.EstateSettings.UseGlobalTime = false; | ||
775 | m_scene.RegionInfo.EstateSettings.SunPosition = (double)(parms2 - 0x1800)/1024.0; | ||
776 | } | ||
777 | |||
778 | if ((parms1 & 0x00000010) != 0) | ||
779 | m_scene.RegionInfo.EstateSettings.FixedSun = true; | ||
780 | else | ||
781 | m_scene.RegionInfo.EstateSettings.FixedSun = false; | ||
782 | |||
783 | if ((parms1 & 0x00008000) != 0) | ||
784 | m_scene.RegionInfo.EstateSettings.PublicAccess = true; | ||
785 | else | ||
786 | m_scene.RegionInfo.EstateSettings.PublicAccess = false; | ||
787 | |||
788 | if ((parms1 & 0x10000000) != 0) | ||
789 | m_scene.RegionInfo.EstateSettings.AllowVoice = true; | ||
790 | else | ||
791 | m_scene.RegionInfo.EstateSettings.AllowVoice = false; | ||
792 | |||
793 | if ((parms1 & 0x00100000) != 0) | ||
794 | m_scene.RegionInfo.EstateSettings.AllowDirectTeleport = true; | ||
795 | else | ||
796 | m_scene.RegionInfo.EstateSettings.AllowDirectTeleport = false; | ||
797 | |||
798 | if ((parms1 & 0x00800000) != 0) | ||
799 | m_scene.RegionInfo.EstateSettings.DenyAnonymous = true; | ||
800 | else | ||
801 | m_scene.RegionInfo.EstateSettings.DenyAnonymous = false; | ||
802 | |||
803 | if ((parms1 & 0x01000000) != 0) | ||
804 | m_scene.RegionInfo.EstateSettings.DenyIdentified = true; | ||
805 | else | ||
806 | m_scene.RegionInfo.EstateSettings.DenyIdentified = false; | ||
807 | |||
808 | if ((parms1 & 0x02000000) != 0) | ||
809 | m_scene.RegionInfo.EstateSettings.DenyTransacted = true; | ||
810 | else | ||
811 | m_scene.RegionInfo.EstateSettings.DenyTransacted = false; | ||
812 | |||
813 | if ((parms1 & 0x40000000) != 0) | ||
814 | m_scene.RegionInfo.EstateSettings.DenyMinors = true; | ||
815 | else | ||
816 | m_scene.RegionInfo.EstateSettings.DenyMinors = false; | ||
817 | |||
818 | m_scene.RegionInfo.EstateSettings.Save(); | ||
819 | |||
820 | float sun = (float)m_scene.RegionInfo.RegionSettings.SunPosition; | ||
821 | if (m_scene.RegionInfo.RegionSettings.UseEstateSun) | ||
822 | { | ||
823 | sun = (float)m_scene.RegionInfo.EstateSettings.SunPosition; | ||
824 | if (m_scene.RegionInfo.EstateSettings.UseGlobalTime) | ||
825 | sun = m_scene.EventManager.GetSunLindenHour(); | ||
826 | } | ||
827 | |||
828 | m_scene.EventManager.TriggerEstateToolsTimeUpdate( | ||
829 | m_scene.RegionInfo.RegionHandle, | ||
830 | m_scene.RegionInfo.EstateSettings.FixedSun || | ||
831 | m_scene.RegionInfo.RegionSettings.FixedSun, | ||
832 | m_scene.RegionInfo.RegionSettings.UseEstateSun, sun); | ||
833 | |||
834 | sendDetailedEstateData(remoteClient, invoice); | ||
835 | } | ||
836 | |||
837 | #endregion | ||
838 | |||
839 | #region IRegionModule Members | ||
840 | |||
841 | public void Initialise(Scene scene, IConfigSource source) | ||
842 | { | ||
843 | m_scene = scene; | ||
844 | m_scene.RegisterModuleInterface<IEstateModule>(this); | ||
845 | m_scene.EventManager.OnNewClient += EventManager_OnNewClient; | ||
846 | m_scene.EventManager.OnRequestChangeWaterHeight += changeWaterHeight; | ||
847 | } | ||
848 | |||
849 | |||
850 | public void PostInitialise() | ||
851 | { | ||
852 | } | ||
853 | |||
854 | public void Close() | ||
855 | { | ||
856 | } | ||
857 | |||
858 | public string Name | ||
859 | { | ||
860 | get { return "EstateManagementModule"; } | ||
861 | } | ||
862 | |||
863 | public bool IsSharedModule | ||
864 | { | ||
865 | get { return false; } | ||
866 | } | ||
867 | |||
868 | #endregion | ||
869 | |||
870 | #region Other Functions | ||
871 | |||
872 | public void changeWaterHeight(float height) | ||
873 | { | ||
874 | setRegionTerrainSettings(height, | ||
875 | (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit, | ||
876 | (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit, | ||
877 | m_scene.RegionInfo.RegionSettings.UseEstateSun, | ||
878 | m_scene.RegionInfo.RegionSettings.FixedSun, | ||
879 | (float)m_scene.RegionInfo.RegionSettings.SunPosition, | ||
880 | m_scene.RegionInfo.EstateSettings.UseGlobalTime, | ||
881 | m_scene.RegionInfo.EstateSettings.FixedSun, | ||
882 | (float)m_scene.RegionInfo.EstateSettings.SunPosition); | ||
883 | |||
884 | sendRegionInfoPacketToAll(); | ||
885 | } | ||
886 | |||
887 | #endregion | ||
888 | |||
889 | private void EventManager_OnNewClient(IClientAPI client) | ||
890 | { | ||
891 | client.OnDetailedEstateDataRequest += sendDetailedEstateData; | ||
892 | client.OnSetEstateFlagsRequest += estateSetRegionInfoHandler; | ||
893 | // client.OnSetEstateTerrainBaseTexture += setEstateTerrainBaseTexture; | ||
894 | client.OnSetEstateTerrainDetailTexture += setEstateTerrainBaseTexture; | ||
895 | client.OnSetEstateTerrainTextureHeights += setEstateTerrainTextureHeights; | ||
896 | client.OnCommitEstateTerrainTextureRequest += handleCommitEstateTerrainTextureRequest; | ||
897 | client.OnSetRegionTerrainSettings += setRegionTerrainSettings; | ||
898 | client.OnEstateRestartSimRequest += handleEstateRestartSimRequest; | ||
899 | client.OnEstateChangeCovenantRequest += handleChangeEstateCovenantRequest; | ||
900 | client.OnEstateChangeInfo += handleEstateChangeInfo; | ||
901 | client.OnUpdateEstateAccessDeltaRequest += handleEstateAccessDeltaRequest; | ||
902 | client.OnSimulatorBlueBoxMessageRequest += SendSimulatorBlueBoxMessage; | ||
903 | client.OnEstateBlueBoxMessageRequest += SendEstateBlueBoxMessage; | ||
904 | client.OnEstateDebugRegionRequest += handleEstateDebugRegionRequest; | ||
905 | client.OnEstateTeleportOneUserHomeRequest += handleEstateTeleportOneUserHomeRequest; | ||
906 | client.OnEstateTeleportAllUsersHomeRequest += handleEstateTeleportAllUsersHomeRequest; | ||
907 | client.OnRequestTerrain += handleTerrainRequest; | ||
908 | client.OnUploadTerrain += handleUploadTerrain; | ||
909 | |||
910 | client.OnRegionInfoRequest += HandleRegionInfoRequest; | ||
911 | client.OnEstateCovenantRequest += HandleEstateCovenantRequest; | ||
912 | client.OnLandStatRequest += HandleLandStatRequest; | ||
913 | sendRegionHandshake(client); | ||
914 | } | ||
915 | |||
916 | public uint GetRegionFlags() | ||
917 | { | ||
918 | RegionFlags flags = RegionFlags.None; | ||
919 | |||
920 | // Fully implemented | ||
921 | // | ||
922 | if (m_scene.RegionInfo.RegionSettings.AllowDamage) | ||
923 | flags |= RegionFlags.AllowDamage; | ||
924 | if (m_scene.RegionInfo.RegionSettings.BlockTerraform) | ||
925 | flags |= RegionFlags.BlockTerraform; | ||
926 | if (!m_scene.RegionInfo.RegionSettings.AllowLandResell) | ||
927 | flags |= RegionFlags.BlockLandResell; | ||
928 | if (m_scene.RegionInfo.RegionSettings.DisableCollisions) | ||
929 | flags |= RegionFlags.SkipCollisions; | ||
930 | if (m_scene.RegionInfo.RegionSettings.DisableScripts) | ||
931 | flags |= RegionFlags.SkipScripts; | ||
932 | if (m_scene.RegionInfo.RegionSettings.DisablePhysics) | ||
933 | flags |= RegionFlags.SkipPhysics; | ||
934 | if (m_scene.RegionInfo.RegionSettings.BlockFly) | ||
935 | flags |= RegionFlags.NoFly; | ||
936 | if (m_scene.RegionInfo.RegionSettings.RestrictPushing) | ||
937 | flags |= RegionFlags.RestrictPushObject; | ||
938 | if (m_scene.RegionInfo.RegionSettings.AllowLandJoinDivide) | ||
939 | flags |= RegionFlags.AllowParcelChanges; | ||
940 | if (m_scene.RegionInfo.RegionSettings.BlockShowInSearch) | ||
941 | flags |= (RegionFlags)(1 << 29); | ||
942 | |||
943 | if (m_scene.RegionInfo.RegionSettings.FixedSun) | ||
944 | flags |= RegionFlags.SunFixed; | ||
945 | if (m_scene.RegionInfo.RegionSettings.Sandbox) | ||
946 | flags |= RegionFlags.Sandbox; | ||
947 | |||
948 | // Fudge these to always on, so the menu options activate | ||
949 | // | ||
950 | flags |= RegionFlags.AllowLandmark; | ||
951 | flags |= RegionFlags.AllowSetHome; | ||
952 | |||
953 | // TODO: SkipUpdateInterestList | ||
954 | |||
955 | // Omitted | ||
956 | // | ||
957 | // Omitted: NullLayer (what is that?) | ||
958 | // Omitted: SkipAgentAction (what does it do?) | ||
959 | |||
960 | return (uint)flags; | ||
961 | } | ||
962 | |||
963 | public uint GetEstateFlags() | ||
964 | { | ||
965 | RegionFlags flags = RegionFlags.None; | ||
966 | |||
967 | if (m_scene.RegionInfo.EstateSettings.FixedSun) | ||
968 | flags |= RegionFlags.SunFixed; | ||
969 | if (m_scene.RegionInfo.EstateSettings.PublicAccess) | ||
970 | flags |= (RegionFlags.PublicAllowed | | ||
971 | RegionFlags.ExternallyVisible); | ||
972 | if (m_scene.RegionInfo.EstateSettings.AllowVoice) | ||
973 | flags |= RegionFlags.AllowVoice; | ||
974 | if (m_scene.RegionInfo.EstateSettings.AllowDirectTeleport) | ||
975 | flags |= RegionFlags.AllowDirectTeleport; | ||
976 | if (m_scene.RegionInfo.EstateSettings.DenyAnonymous) | ||
977 | flags |= RegionFlags.DenyAnonymous; | ||
978 | if (m_scene.RegionInfo.EstateSettings.DenyIdentified) | ||
979 | flags |= RegionFlags.DenyIdentified; | ||
980 | if (m_scene.RegionInfo.EstateSettings.DenyTransacted) | ||
981 | flags |= RegionFlags.DenyTransacted; | ||
982 | if (m_scene.RegionInfo.EstateSettings.AbuseEmailToEstateOwner) | ||
983 | flags |= RegionFlags.AbuseEmailToEstateOwner; | ||
984 | if (m_scene.RegionInfo.EstateSettings.BlockDwell) | ||
985 | flags |= RegionFlags.BlockDwell; | ||
986 | if (m_scene.RegionInfo.EstateSettings.EstateSkipScripts) | ||
987 | flags |= RegionFlags.EstateSkipScripts; | ||
988 | if (m_scene.RegionInfo.EstateSettings.ResetHomeOnTeleport) | ||
989 | flags |= RegionFlags.ResetHomeOnTeleport; | ||
990 | if (m_scene.RegionInfo.EstateSettings.TaxFree) | ||
991 | flags |= RegionFlags.TaxFree; | ||
992 | if (m_scene.RegionInfo.EstateSettings.DenyMinors) | ||
993 | flags |= (RegionFlags)(1 << 30); | ||
994 | |||
995 | return (uint)flags; | ||
996 | } | ||
997 | |||
998 | public bool IsManager(UUID avatarID) | ||
999 | { | ||
1000 | if (avatarID == m_scene.RegionInfo.MasterAvatarAssignedUUID) | ||
1001 | return true; | ||
1002 | if (avatarID == m_scene.RegionInfo.EstateSettings.EstateOwner) | ||
1003 | return true; | ||
1004 | |||
1005 | List<UUID> ems = new List<UUID>(m_scene.RegionInfo.EstateSettings.EstateManagers); | ||
1006 | if (ems.Contains(avatarID)) | ||
1007 | return true; | ||
1008 | |||
1009 | return false; | ||
1010 | } | ||
1011 | } | ||
1012 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs b/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs new file mode 100644 index 0000000..94a4072 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Estate/EstateTerrainXferHandler.cs | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using log4net; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Framework.Communications.Cache; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | |||
37 | |||
38 | namespace OpenSim.Region.CoreModules.World.Estate | ||
39 | { | ||
40 | |||
41 | public class EstateTerrainXferHandler | ||
42 | { | ||
43 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
44 | |||
45 | private AssetBase m_asset; | ||
46 | |||
47 | public delegate void TerrainUploadComplete(string name, byte[] filedata, IClientAPI remoteClient); | ||
48 | |||
49 | public event TerrainUploadComplete TerrainUploadDone; | ||
50 | |||
51 | //private string m_description = String.Empty; | ||
52 | //private string m_name = String.Empty; | ||
53 | //private UUID TransactionID = UUID.Zero; | ||
54 | private sbyte type = 0; | ||
55 | |||
56 | public ulong mXferID; | ||
57 | private TerrainUploadComplete handlerTerrainUploadDone; | ||
58 | |||
59 | public EstateTerrainXferHandler(IClientAPI pRemoteClient, string pClientFilename) | ||
60 | { | ||
61 | |||
62 | m_asset = new AssetBase(); | ||
63 | m_asset.Metadata.FullID = UUID.Zero; | ||
64 | m_asset.Metadata.Type = type; | ||
65 | m_asset.Data = new byte[0]; | ||
66 | m_asset.Metadata.Name = pClientFilename; | ||
67 | m_asset.Metadata.Description = "empty"; | ||
68 | m_asset.Metadata.Local = true; | ||
69 | m_asset.Metadata.Temporary = true; | ||
70 | |||
71 | } | ||
72 | |||
73 | public ulong XferID | ||
74 | { | ||
75 | get { return mXferID; } | ||
76 | } | ||
77 | |||
78 | public void RequestStartXfer(IClientAPI pRemoteClient) | ||
79 | { | ||
80 | mXferID = Util.GetNextXferID(); | ||
81 | pRemoteClient.SendXferRequest(mXferID, m_asset.Metadata.Type, m_asset.Metadata.FullID, 0, Utils.StringToBytes(m_asset.Metadata.Name)); | ||
82 | } | ||
83 | |||
84 | /// <summary> | ||
85 | /// Process transfer data received from the client. | ||
86 | /// </summary> | ||
87 | /// <param name="xferID"></param> | ||
88 | /// <param name="packetID"></param> | ||
89 | /// <param name="data"></param> | ||
90 | public void XferReceive(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data) | ||
91 | { | ||
92 | if (mXferID == xferID) | ||
93 | { | ||
94 | if (m_asset.Data.Length > 1) | ||
95 | { | ||
96 | byte[] destinationArray = new byte[m_asset.Data.Length + data.Length]; | ||
97 | Array.Copy(m_asset.Data, 0, destinationArray, 0, m_asset.Data.Length); | ||
98 | Array.Copy(data, 0, destinationArray, m_asset.Data.Length, data.Length); | ||
99 | m_asset.Data = destinationArray; | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | byte[] buffer2 = new byte[data.Length - 4]; | ||
104 | Array.Copy(data, 4, buffer2, 0, data.Length - 4); | ||
105 | m_asset.Data = buffer2; | ||
106 | } | ||
107 | |||
108 | remoteClient.SendConfirmXfer(xferID, packetID); | ||
109 | |||
110 | if ((packetID & 0x80000000) != 0) | ||
111 | { | ||
112 | SendCompleteMessage(remoteClient); | ||
113 | |||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | public void SendCompleteMessage(IClientAPI remoteClient) | ||
119 | { | ||
120 | handlerTerrainUploadDone = TerrainUploadDone; | ||
121 | if (handlerTerrainUploadDone != null) | ||
122 | { | ||
123 | handlerTerrainUploadDone(m_asset.Metadata.Name, m_asset.Data, remoteClient); | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Land/LandChannel.cs b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs new file mode 100644 index 0000000..41163a0 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Land/LandChannel.cs | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using OpenMetaverse; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenSim.Region.Framework.Interfaces; | ||
33 | using OpenSim.Region.Framework.Scenes; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Land | ||
36 | { | ||
37 | public class LandChannel : ILandChannel | ||
38 | { | ||
39 | #region Constants | ||
40 | |||
41 | //Land types set with flags in ParcelOverlay. | ||
42 | //Only one of these can be used. | ||
43 | public const float BAN_LINE_SAFETY_HIEGHT = 100; | ||
44 | public const byte LAND_FLAG_PROPERTY_BORDER_SOUTH = 128; //Equals 10000000 | ||
45 | public const byte LAND_FLAG_PROPERTY_BORDER_WEST = 64; //Equals 01000000 | ||
46 | |||
47 | //RequestResults (I think these are right, they seem to work): | ||
48 | public const int LAND_RESULT_MULTIPLE = 1; // The request they made contained more than a single peice of land | ||
49 | public const int LAND_RESULT_SINGLE = 0; // The request they made contained only a single piece of land | ||
50 | |||
51 | //ParcelSelectObjects | ||
52 | public const int LAND_SELECT_OBJECTS_GROUP = 4; | ||
53 | public const int LAND_SELECT_OBJECTS_OTHER = 8; | ||
54 | public const int LAND_SELECT_OBJECTS_OWNER = 2; | ||
55 | public const byte LAND_TYPE_IS_BEING_AUCTIONED = 5; //Equals 00000101 | ||
56 | public const byte LAND_TYPE_IS_FOR_SALE = 4; //Equals 00000100 | ||
57 | public const byte LAND_TYPE_OWNED_BY_GROUP = 2; //Equals 00000010 | ||
58 | public const byte LAND_TYPE_OWNED_BY_OTHER = 1; //Equals 00000001 | ||
59 | public const byte LAND_TYPE_OWNED_BY_REQUESTER = 3; //Equals 00000011 | ||
60 | public const byte LAND_TYPE_PUBLIC = 0; //Equals 00000000 | ||
61 | |||
62 | //These are other constants. Yay! | ||
63 | public const int START_LAND_LOCAL_ID = 1; | ||
64 | |||
65 | #endregion | ||
66 | |||
67 | private readonly Scene m_scene; | ||
68 | private readonly LandManagementModule m_landManagementModule; | ||
69 | |||
70 | public LandChannel(Scene scene, LandManagementModule landManagementMod) | ||
71 | { | ||
72 | m_scene = scene; | ||
73 | m_landManagementModule = landManagementMod; | ||
74 | } | ||
75 | |||
76 | #region ILandChannel Members | ||
77 | |||
78 | |||
79 | /// <summary> | ||
80 | /// Get the land object at the specified point | ||
81 | /// </summary> | ||
82 | /// <param name="x_float">Value between 0 - 256 on the x axis of the point</param> | ||
83 | /// <param name="y_float">Value between 0 - 256 on the y axis of the point</param> | ||
84 | /// <returns>Land object at the point supplied</returns> | ||
85 | public ILandObject GetLandObject(float x_float, float y_float) | ||
86 | { | ||
87 | if (m_landManagementModule != null) | ||
88 | { | ||
89 | return m_landManagementModule.GetLandObject(x_float, y_float); | ||
90 | } | ||
91 | ILandObject obj = new LandObject(UUID.Zero, false, m_scene); | ||
92 | obj.landData.Name = "NO LAND"; | ||
93 | return obj; | ||
94 | } | ||
95 | |||
96 | public ILandObject GetLandObject(int x, int y) | ||
97 | { | ||
98 | if (m_landManagementModule != null) | ||
99 | { | ||
100 | return m_landManagementModule.GetLandObject(x, y); | ||
101 | } | ||
102 | ILandObject obj = new LandObject(UUID.Zero, false, m_scene); | ||
103 | obj.landData.Name = "NO LAND"; | ||
104 | return obj; | ||
105 | } | ||
106 | |||
107 | public List<ILandObject> AllParcels() | ||
108 | { | ||
109 | if (m_landManagementModule != null) | ||
110 | { | ||
111 | return m_landManagementModule.AllParcels(); | ||
112 | } | ||
113 | |||
114 | return new List<ILandObject>(); | ||
115 | } | ||
116 | |||
117 | public List<ILandObject> ParcelsNearPoint(Vector3 position) | ||
118 | { | ||
119 | if (m_landManagementModule != null) | ||
120 | { | ||
121 | return m_landManagementModule.ParcelsNearPoint(position); | ||
122 | } | ||
123 | |||
124 | return new List<ILandObject>(); | ||
125 | } | ||
126 | |||
127 | public bool IsLandPrimCountTainted() | ||
128 | { | ||
129 | if (m_landManagementModule != null) | ||
130 | { | ||
131 | return m_landManagementModule.IsLandPrimCountTainted(); | ||
132 | } | ||
133 | |||
134 | return false; | ||
135 | } | ||
136 | |||
137 | public bool IsForcefulBansAllowed() | ||
138 | { | ||
139 | if (m_landManagementModule != null) | ||
140 | { | ||
141 | return m_landManagementModule.AllowedForcefulBans; | ||
142 | } | ||
143 | |||
144 | return false; | ||
145 | } | ||
146 | |||
147 | public void UpdateLandObject(int localID, LandData data) | ||
148 | { | ||
149 | if (m_landManagementModule != null) | ||
150 | { | ||
151 | m_landManagementModule.UpdateLandObject(localID, data); | ||
152 | } | ||
153 | } | ||
154 | public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient) | ||
155 | { | ||
156 | if (m_landManagementModule != null) | ||
157 | { | ||
158 | m_landManagementModule.ReturnObjectsInParcel(localID, returnType, agentIDs, taskIDs, remoteClient); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | public void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel) | ||
163 | { | ||
164 | if (m_landManagementModule != null) | ||
165 | { | ||
166 | m_landManagementModule.setParcelObjectMaxOverride(overrideDel); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | public void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel) | ||
171 | { | ||
172 | if (m_landManagementModule != null) | ||
173 | { | ||
174 | m_landManagementModule.setSimulatorObjectMaxOverride(overrideDel); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | public void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime) | ||
179 | { | ||
180 | if (m_landManagementModule != null) | ||
181 | { | ||
182 | m_landManagementModule.setParcelOtherCleanTime(remoteClient, localID, otherCleanTime); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | #endregion | ||
187 | } | ||
188 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs new file mode 100644 index 0000000..6ae6576 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs | |||
@@ -0,0 +1,1347 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Region.Framework.Interfaces; | ||
36 | using OpenSim.Region.Framework.Scenes; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Servers; | ||
39 | using OpenSim.Framework.Communications.Capabilities; | ||
40 | using OpenSim.Region.Physics.Manager; | ||
41 | using Caps = OpenSim.Framework.Communications.Capabilities.Caps; | ||
42 | |||
43 | namespace OpenSim.Region.CoreModules.World.Land | ||
44 | { | ||
45 | // used for caching | ||
46 | internal class ExtendedLandData { | ||
47 | public LandData landData; | ||
48 | public ulong regionHandle; | ||
49 | public uint x, y; | ||
50 | } | ||
51 | |||
52 | public class LandManagementModule : IRegionModule | ||
53 | { | ||
54 | private static readonly ILog m_log = | ||
55 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
56 | |||
57 | private static readonly string remoteParcelRequestPath = "0009/"; | ||
58 | |||
59 | private LandChannel landChannel; | ||
60 | private Scene m_scene; | ||
61 | |||
62 | private readonly int[,] m_landIDList = new int[64, 64]; | ||
63 | private readonly Dictionary<int, ILandObject> m_landList = new Dictionary<int, ILandObject>(); | ||
64 | |||
65 | private bool m_landPrimCountTainted; | ||
66 | private int m_lastLandLocalID = LandChannel.START_LAND_LOCAL_ID - 1; | ||
67 | |||
68 | private bool m_allowedForcefulBans = true; | ||
69 | |||
70 | // caches ExtendedLandData | ||
71 | private Cache parcelInfoCache; | ||
72 | |||
73 | #region IRegionModule Members | ||
74 | |||
75 | public void Initialise(Scene scene, IConfigSource source) | ||
76 | { | ||
77 | m_scene = scene; | ||
78 | m_landIDList.Initialize(); | ||
79 | landChannel = new LandChannel(scene, this); | ||
80 | |||
81 | parcelInfoCache = new Cache(); | ||
82 | parcelInfoCache.Size = 30; // the number of different parcel requests in this region to cache | ||
83 | parcelInfoCache.DefaultTTL = new TimeSpan(0, 5, 0); | ||
84 | |||
85 | m_scene.EventManager.OnParcelPrimCountAdd += AddPrimToLandPrimCounts; | ||
86 | m_scene.EventManager.OnParcelPrimCountUpdate += UpdateLandPrimCounts; | ||
87 | m_scene.EventManager.OnAvatarEnteringNewParcel += new EventManager.AvatarEnteringNewParcel(handleAvatarChangingParcel); | ||
88 | m_scene.EventManager.OnClientMovement += new EventManager.ClientMovement(handleAnyClientMovement); | ||
89 | m_scene.EventManager.OnValidateLandBuy += handleLandValidationRequest; | ||
90 | m_scene.EventManager.OnLandBuy += handleLandBuyRequest; | ||
91 | m_scene.EventManager.OnNewClient += new EventManager.OnNewClientDelegate(EventManager_OnNewClient); | ||
92 | m_scene.EventManager.OnSignificantClientMovement += handleSignificantClientMovement; | ||
93 | m_scene.EventManager.OnObjectBeingRemovedFromScene += RemovePrimFromLandPrimCounts; | ||
94 | |||
95 | m_scene.EventManager.OnNoticeNoLandDataFromStorage += this.NoLandDataFromStorage; | ||
96 | m_scene.EventManager.OnIncomingLandDataFromStorage += this.IncomingLandObjectsFromStorage; | ||
97 | m_scene.EventManager.OnSetAllowForcefulBan += this.SetAllowedForcefulBans; | ||
98 | m_scene.EventManager.OnRequestParcelPrimCountUpdate += this.PerformParcelPrimCountUpdate; | ||
99 | m_scene.EventManager.OnParcelPrimCountTainted += this.SetPrimsTainted; | ||
100 | m_scene.EventManager.OnRegisterCaps += this.OnRegisterCaps; | ||
101 | |||
102 | lock (m_scene) | ||
103 | { | ||
104 | m_scene.LandChannel = (ILandChannel) landChannel; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | void EventManager_OnNewClient(IClientAPI client) | ||
109 | { | ||
110 | //Register some client events | ||
111 | client.OnParcelPropertiesRequest += new ParcelPropertiesRequest(handleParcelPropertiesRequest); | ||
112 | client.OnParcelDivideRequest += new ParcelDivideRequest(handleParcelDivideRequest); | ||
113 | client.OnParcelJoinRequest += new ParcelJoinRequest(handleParcelJoinRequest); | ||
114 | client.OnParcelPropertiesUpdateRequest += new ParcelPropertiesUpdateRequest(handleParcelPropertiesUpdateRequest); | ||
115 | client.OnParcelSelectObjects += new ParcelSelectObjects(handleParcelSelectObjectsRequest); | ||
116 | client.OnParcelObjectOwnerRequest += new ParcelObjectOwnerRequest(handleParcelObjectOwnersRequest); | ||
117 | client.OnParcelAccessListRequest += new ParcelAccessListRequest(handleParcelAccessRequest); | ||
118 | client.OnParcelAccessListUpdateRequest += new ParcelAccessListUpdateRequest(handleParcelAccessUpdateRequest); | ||
119 | client.OnParcelAbandonRequest += new ParcelAbandonRequest(handleParcelAbandonRequest); | ||
120 | client.OnParcelGodForceOwner += new ParcelGodForceOwner(handleParcelGodForceOwner); | ||
121 | client.OnParcelReclaim += new ParcelReclaim(handleParcelReclaim); | ||
122 | client.OnParcelInfoRequest += new ParcelInfoRequest(handleParcelInfo); | ||
123 | client.OnParcelDwellRequest += new ParcelDwellRequest(handleParcelDwell); | ||
124 | if (m_scene.Entities.ContainsKey(client.AgentId)) | ||
125 | { | ||
126 | SendLandUpdate((ScenePresence)m_scene.Entities[client.AgentId], true); | ||
127 | SendParcelOverlay(client); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | public void PostInitialise() | ||
132 | { | ||
133 | } | ||
134 | |||
135 | public void Close() | ||
136 | { | ||
137 | } | ||
138 | |||
139 | public string Name | ||
140 | { | ||
141 | get { return "LandManagementModule"; } | ||
142 | } | ||
143 | |||
144 | public bool IsSharedModule | ||
145 | { | ||
146 | get { return false; } | ||
147 | } | ||
148 | |||
149 | #endregion | ||
150 | |||
151 | #region Parcel Add/Remove/Get/Create | ||
152 | |||
153 | public void SetAllowedForcefulBans(bool forceful) | ||
154 | { | ||
155 | AllowedForcefulBans = forceful; | ||
156 | } | ||
157 | |||
158 | public void UpdateLandObject(int local_id, LandData data) | ||
159 | { | ||
160 | LandData newData = data.Copy(); | ||
161 | newData.LocalID = local_id; | ||
162 | |||
163 | lock (m_landList) | ||
164 | { | ||
165 | if (m_landList.ContainsKey(local_id)) | ||
166 | { | ||
167 | m_landList[local_id].landData = newData; | ||
168 | m_scene.EventManager.TriggerLandObjectUpdated((uint)local_id, m_landList[local_id]); | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | public bool AllowedForcefulBans | ||
174 | { | ||
175 | get { return m_allowedForcefulBans; } | ||
176 | set { m_allowedForcefulBans = value; } | ||
177 | } | ||
178 | |||
179 | /// <summary> | ||
180 | /// Resets the sim to the default land object (full sim piece of land owned by the default user) | ||
181 | /// </summary> | ||
182 | public void ResetSimLandObjects() | ||
183 | { | ||
184 | //Remove all the land objects in the sim and add a blank, full sim land object set to public | ||
185 | lock (m_landList) | ||
186 | { | ||
187 | m_landList.Clear(); | ||
188 | m_lastLandLocalID = LandChannel.START_LAND_LOCAL_ID - 1; | ||
189 | m_landIDList.Initialize(); | ||
190 | } | ||
191 | |||
192 | ILandObject fullSimParcel = new LandObject(UUID.Zero, false, m_scene); | ||
193 | |||
194 | fullSimParcel.setLandBitmap(fullSimParcel.getSquareLandBitmap(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize)); | ||
195 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
196 | fullSimParcel.landData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
197 | else | ||
198 | fullSimParcel.landData.OwnerID = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
199 | fullSimParcel.landData.ClaimDate = Util.UnixTimeSinceEpoch(); | ||
200 | AddLandObject(fullSimParcel); | ||
201 | } | ||
202 | |||
203 | public List<ILandObject> AllParcels() | ||
204 | { | ||
205 | lock (m_landList) | ||
206 | { | ||
207 | return new List<ILandObject>(m_landList.Values); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | public List<ILandObject> ParcelsNearPoint(Vector3 position) | ||
212 | { | ||
213 | List<ILandObject> parcelsNear = new List<ILandObject>(); | ||
214 | for (int x = -4; x <= 4; x += 4) | ||
215 | { | ||
216 | for (int y = -4; y <= 4; y += 4) | ||
217 | { | ||
218 | ILandObject check = GetLandObject(position.X + x, position.Y + y); | ||
219 | if (check != null) | ||
220 | { | ||
221 | if (!parcelsNear.Contains(check)) | ||
222 | { | ||
223 | parcelsNear.Add(check); | ||
224 | } | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return parcelsNear; | ||
230 | } | ||
231 | |||
232 | public void SendYouAreBannedNotice(ScenePresence avatar) | ||
233 | { | ||
234 | if (AllowedForcefulBans) | ||
235 | { | ||
236 | avatar.ControllingClient.SendAlertMessage( | ||
237 | "You are not allowed on this parcel because you are banned. Please go away."); | ||
238 | |||
239 | avatar.PhysicsActor.Position = | ||
240 | new PhysicsVector(avatar.lastKnownAllowedPosition.X, avatar.lastKnownAllowedPosition.Y, | ||
241 | avatar.lastKnownAllowedPosition.Z); | ||
242 | avatar.PhysicsActor.Velocity = new PhysicsVector(0, 0, 0); | ||
243 | } | ||
244 | else | ||
245 | { | ||
246 | avatar.ControllingClient.SendAlertMessage( | ||
247 | "You are not allowed on this parcel because you are banned; however, the grid administrator has disabled ban lines globally. Please obey the land owner's requests or you can be banned from the entire sim!"); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | public void handleAvatarChangingParcel(ScenePresence avatar, int localLandID, UUID regionID) | ||
252 | { | ||
253 | if (m_scene.RegionInfo.RegionID == regionID) | ||
254 | { | ||
255 | ILandObject parcelAvatarIsEntering; | ||
256 | lock (m_landList) | ||
257 | { | ||
258 | parcelAvatarIsEntering = m_landList[localLandID]; | ||
259 | } | ||
260 | |||
261 | if (parcelAvatarIsEntering != null) | ||
262 | { | ||
263 | if (avatar.AbsolutePosition.Z < LandChannel.BAN_LINE_SAFETY_HIEGHT) | ||
264 | { | ||
265 | if (parcelAvatarIsEntering.isBannedFromLand(avatar.UUID)) | ||
266 | { | ||
267 | SendYouAreBannedNotice(avatar); | ||
268 | } | ||
269 | else if (parcelAvatarIsEntering.isRestrictedFromLand(avatar.UUID)) | ||
270 | { | ||
271 | avatar.ControllingClient.SendAlertMessage( | ||
272 | "You are not allowed on this parcel because the land owner has restricted access. For now, you can enter, but please respect the land owner's decisions (or he can ban you!)."); | ||
273 | } | ||
274 | else | ||
275 | { | ||
276 | avatar.sentMessageAboutRestrictedParcelFlyingDown = true; | ||
277 | } | ||
278 | } | ||
279 | else | ||
280 | { | ||
281 | avatar.sentMessageAboutRestrictedParcelFlyingDown = true; | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | } | ||
286 | |||
287 | public void SendOutNearestBanLine(IClientAPI avatar) | ||
288 | { | ||
289 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
290 | foreach (ScenePresence presence in avatars) | ||
291 | { | ||
292 | if (presence.UUID == avatar.AgentId) | ||
293 | { | ||
294 | List<ILandObject> checkLandParcels = ParcelsNearPoint(presence.AbsolutePosition); | ||
295 | foreach (ILandObject checkBan in checkLandParcels) | ||
296 | { | ||
297 | if (checkBan.isBannedFromLand(avatar.AgentId)) | ||
298 | { | ||
299 | checkBan.sendLandProperties((int)ParcelStatus.CollisionBanned, false, (int)ParcelResult.Single, avatar); | ||
300 | return; //Only send one | ||
301 | } | ||
302 | if (checkBan.isRestrictedFromLand(avatar.AgentId)) | ||
303 | { | ||
304 | checkBan.sendLandProperties((int)ParcelStatus.CollisionNotOnAccessList, false, (int)ParcelResult.Single, avatar); | ||
305 | return; //Only send one | ||
306 | } | ||
307 | } | ||
308 | return; | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | |||
313 | public void SendLandUpdate(ScenePresence avatar, bool force) | ||
314 | { | ||
315 | ILandObject over = GetLandObject((int)Math.Min(255, Math.Max(0, Math.Round(avatar.AbsolutePosition.X))), | ||
316 | (int)Math.Min(255, Math.Max(0, Math.Round(avatar.AbsolutePosition.Y)))); | ||
317 | |||
318 | if (over != null) | ||
319 | { | ||
320 | if (force) | ||
321 | { | ||
322 | if (!avatar.IsChildAgent) | ||
323 | { | ||
324 | over.sendLandUpdateToClient(avatar.ControllingClient); | ||
325 | m_scene.EventManager.TriggerAvatarEnteringNewParcel(avatar, over.landData.LocalID, | ||
326 | m_scene.RegionInfo.RegionID); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | if (avatar.currentParcelUUID != over.landData.GlobalID) | ||
331 | { | ||
332 | if (!avatar.IsChildAgent) | ||
333 | { | ||
334 | over.sendLandUpdateToClient(avatar.ControllingClient); | ||
335 | avatar.currentParcelUUID = over.landData.GlobalID; | ||
336 | m_scene.EventManager.TriggerAvatarEnteringNewParcel(avatar, over.landData.LocalID, | ||
337 | m_scene.RegionInfo.RegionID); | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | public void SendLandUpdate(ScenePresence avatar) | ||
344 | { | ||
345 | SendLandUpdate(avatar, false); | ||
346 | } | ||
347 | |||
348 | public void handleSignificantClientMovement(IClientAPI remote_client) | ||
349 | { | ||
350 | ScenePresence clientAvatar = m_scene.GetScenePresence(remote_client.AgentId); | ||
351 | |||
352 | if (clientAvatar != null) | ||
353 | { | ||
354 | SendLandUpdate(clientAvatar); | ||
355 | SendOutNearestBanLine(remote_client); | ||
356 | ILandObject parcel = GetLandObject(clientAvatar.AbsolutePosition.X, clientAvatar.AbsolutePosition.Y); | ||
357 | if (parcel != null) | ||
358 | { | ||
359 | if (clientAvatar.AbsolutePosition.Z < LandChannel.BAN_LINE_SAFETY_HIEGHT && | ||
360 | clientAvatar.sentMessageAboutRestrictedParcelFlyingDown) | ||
361 | { | ||
362 | handleAvatarChangingParcel(clientAvatar, parcel.landData.LocalID, m_scene.RegionInfo.RegionID); | ||
363 | //They are going below the safety line! | ||
364 | if (!parcel.isBannedFromLand(clientAvatar.UUID)) | ||
365 | { | ||
366 | clientAvatar.sentMessageAboutRestrictedParcelFlyingDown = false; | ||
367 | } | ||
368 | } | ||
369 | else if (clientAvatar.AbsolutePosition.Z < LandChannel.BAN_LINE_SAFETY_HIEGHT && | ||
370 | parcel.isBannedFromLand(clientAvatar.UUID)) | ||
371 | { | ||
372 | SendYouAreBannedNotice(clientAvatar); | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | public void handleAnyClientMovement(ScenePresence avatar) | ||
379 | //Like handleSignificantClientMovement, but called with an AgentUpdate regardless of distance. | ||
380 | { | ||
381 | ILandObject over = GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); | ||
382 | if (over != null) | ||
383 | { | ||
384 | if (!over.isBannedFromLand(avatar.UUID) || avatar.AbsolutePosition.Z >= LandChannel.BAN_LINE_SAFETY_HIEGHT) | ||
385 | { | ||
386 | avatar.lastKnownAllowedPosition = | ||
387 | new Vector3(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y, avatar.AbsolutePosition.Z); | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | |||
393 | public void handleParcelAccessRequest(UUID agentID, UUID sessionID, uint flags, int sequenceID, | ||
394 | int landLocalID, IClientAPI remote_client) | ||
395 | { | ||
396 | ILandObject land; | ||
397 | lock (m_landList) | ||
398 | { | ||
399 | m_landList.TryGetValue(landLocalID, out land); | ||
400 | } | ||
401 | |||
402 | if (land != null) | ||
403 | { | ||
404 | m_landList[landLocalID].sendAccessList(agentID, sessionID, flags, sequenceID, remote_client); | ||
405 | } | ||
406 | } | ||
407 | |||
408 | public void handleParcelAccessUpdateRequest(UUID agentID, UUID sessionID, uint flags, int landLocalID, | ||
409 | List<ParcelManager.ParcelAccessEntry> entries, | ||
410 | IClientAPI remote_client) | ||
411 | { | ||
412 | ILandObject land; | ||
413 | lock (m_landList) | ||
414 | { | ||
415 | m_landList.TryGetValue(landLocalID, out land); | ||
416 | } | ||
417 | |||
418 | if (land != null) | ||
419 | { | ||
420 | if (agentID == land.landData.OwnerID) | ||
421 | { | ||
422 | land.updateAccessList(flags, entries, remote_client); | ||
423 | } | ||
424 | } | ||
425 | else | ||
426 | { | ||
427 | m_log.WarnFormat("[LAND]: Invalid local land ID {0}", landLocalID); | ||
428 | } | ||
429 | } | ||
430 | |||
431 | /// <summary> | ||
432 | /// Creates a basic Parcel object without an owner (a zeroed key) | ||
433 | /// </summary> | ||
434 | /// <returns></returns> | ||
435 | public ILandObject CreateBaseLand() | ||
436 | { | ||
437 | return new LandObject(UUID.Zero, false, m_scene); | ||
438 | } | ||
439 | |||
440 | /// <summary> | ||
441 | /// Adds a land object to the stored list and adds them to the landIDList to what they own | ||
442 | /// </summary> | ||
443 | /// <param name="new_land">The land object being added</param> | ||
444 | public ILandObject AddLandObject(ILandObject land) | ||
445 | { | ||
446 | ILandObject new_land = land.Copy(); | ||
447 | |||
448 | lock (m_landList) | ||
449 | { | ||
450 | int newLandLocalID = ++m_lastLandLocalID; | ||
451 | new_land.landData.LocalID = newLandLocalID; | ||
452 | |||
453 | bool[,] landBitmap = new_land.getLandBitmap(); | ||
454 | for (int x = 0; x < 64; x++) | ||
455 | { | ||
456 | for (int y = 0; y < 64; y++) | ||
457 | { | ||
458 | if (landBitmap[x, y]) | ||
459 | { | ||
460 | m_landIDList[x, y] = newLandLocalID; | ||
461 | } | ||
462 | } | ||
463 | } | ||
464 | |||
465 | m_landList.Add(newLandLocalID, new_land); | ||
466 | } | ||
467 | |||
468 | new_land.forceUpdateLandInfo(); | ||
469 | m_scene.EventManager.TriggerLandObjectAdded(new_land); | ||
470 | return new_land; | ||
471 | } | ||
472 | |||
473 | /// <summary> | ||
474 | /// Removes a land object from the list. Will not remove if local_id is still owning an area in landIDList | ||
475 | /// </summary> | ||
476 | /// <param name="local_id">Land.localID of the peice of land to remove.</param> | ||
477 | public void removeLandObject(int local_id) | ||
478 | { | ||
479 | lock (m_landList) | ||
480 | { | ||
481 | for (int x = 0; x < 64; x++) | ||
482 | { | ||
483 | for (int y = 0; y < 64; y++) | ||
484 | { | ||
485 | if (m_landIDList[x, y] == local_id) | ||
486 | { | ||
487 | m_log.WarnFormat("[LAND]: Not removing land object {0}; still being used at {1}, {2}", | ||
488 | local_id, x, y); | ||
489 | return; | ||
490 | //throw new Exception("Could not remove land object. Still being used at " + x + ", " + y); | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | m_scene.EventManager.TriggerLandObjectRemoved(m_landList[local_id].landData.GlobalID); | ||
496 | m_landList.Remove(local_id); | ||
497 | } | ||
498 | } | ||
499 | |||
500 | private void performFinalLandJoin(ILandObject master, ILandObject slave) | ||
501 | { | ||
502 | bool[,] landBitmapSlave = slave.getLandBitmap(); | ||
503 | lock (m_landList) | ||
504 | { | ||
505 | for (int x = 0; x < 64; x++) | ||
506 | { | ||
507 | for (int y = 0; y < 64; y++) | ||
508 | { | ||
509 | if (landBitmapSlave[x, y]) | ||
510 | { | ||
511 | m_landIDList[x, y] = master.landData.LocalID; | ||
512 | } | ||
513 | } | ||
514 | } | ||
515 | } | ||
516 | |||
517 | removeLandObject(slave.landData.LocalID); | ||
518 | UpdateLandObject(master.landData.LocalID, master.landData); | ||
519 | } | ||
520 | |||
521 | public ILandObject GetLandObject(int parcelLocalID) | ||
522 | { | ||
523 | lock (m_landList) | ||
524 | { | ||
525 | if (m_landList.ContainsKey(parcelLocalID)) | ||
526 | { | ||
527 | return m_landList[parcelLocalID]; | ||
528 | } | ||
529 | } | ||
530 | return null; | ||
531 | } | ||
532 | |||
533 | /// <summary> | ||
534 | /// Get the land object at the specified point | ||
535 | /// </summary> | ||
536 | /// <param name="x_float">Value between 0 - 256 on the x axis of the point</param> | ||
537 | /// <param name="y_float">Value between 0 - 256 on the y axis of the point</param> | ||
538 | /// <returns>Land object at the point supplied</returns> | ||
539 | public ILandObject GetLandObject(float x_float, float y_float) | ||
540 | { | ||
541 | int x; | ||
542 | int y; | ||
543 | |||
544 | try | ||
545 | { | ||
546 | x = Convert.ToInt32(Math.Floor(Convert.ToDouble(x_float) / 4.0)); | ||
547 | y = Convert.ToInt32(Math.Floor(Convert.ToDouble(y_float) / 4.0)); | ||
548 | } | ||
549 | catch (OverflowException) | ||
550 | { | ||
551 | return null; | ||
552 | } | ||
553 | |||
554 | if (x >= 64 || y >= 64 || x < 0 || y < 0) | ||
555 | { | ||
556 | return null; | ||
557 | } | ||
558 | lock (m_landList) | ||
559 | { | ||
560 | // Corner case. If an autoreturn happens during sim startup | ||
561 | // we will come here with the list uninitialized | ||
562 | // | ||
563 | if (m_landList.ContainsKey(m_landIDList[x, y])) | ||
564 | return m_landList[m_landIDList[x, y]]; | ||
565 | return null; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | public ILandObject GetLandObject(int x, int y) | ||
570 | { | ||
571 | if (x >= Convert.ToInt32(Constants.RegionSize) || y >= Convert.ToInt32(Constants.RegionSize) || x < 0 || y < 0) | ||
572 | { | ||
573 | // These exceptions here will cause a lot of complaints from the users specifically because | ||
574 | // they happen every time at border crossings | ||
575 | throw new Exception("Error: Parcel not found at point " + x + ", " + y); | ||
576 | } | ||
577 | lock (m_landIDList) | ||
578 | { | ||
579 | return m_landList[m_landIDList[x / 4, y / 4]]; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | #endregion | ||
584 | |||
585 | #region Parcel Modification | ||
586 | |||
587 | public void ResetAllLandPrimCounts() | ||
588 | { | ||
589 | lock (m_landList) | ||
590 | { | ||
591 | foreach (LandObject p in m_landList.Values) | ||
592 | { | ||
593 | p.resetLandPrimCounts(); | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | |||
598 | public void SetPrimsTainted() | ||
599 | { | ||
600 | m_landPrimCountTainted = true; | ||
601 | } | ||
602 | |||
603 | public bool IsLandPrimCountTainted() | ||
604 | { | ||
605 | return m_landPrimCountTainted; | ||
606 | } | ||
607 | |||
608 | public void AddPrimToLandPrimCounts(SceneObjectGroup obj) | ||
609 | { | ||
610 | Vector3 position = obj.AbsolutePosition; | ||
611 | ILandObject landUnderPrim = GetLandObject(position.X, position.Y); | ||
612 | if (landUnderPrim != null) | ||
613 | { | ||
614 | landUnderPrim.addPrimToCount(obj); | ||
615 | } | ||
616 | } | ||
617 | |||
618 | public void RemovePrimFromLandPrimCounts(SceneObjectGroup obj) | ||
619 | { | ||
620 | |||
621 | lock (m_landList) | ||
622 | { | ||
623 | foreach (LandObject p in m_landList.Values) | ||
624 | { | ||
625 | p.removePrimFromCount(obj); | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | |||
630 | public void FinalizeLandPrimCountUpdate() | ||
631 | { | ||
632 | //Get Simwide prim count for owner | ||
633 | Dictionary<UUID, List<LandObject>> landOwnersAndParcels = new Dictionary<UUID, List<LandObject>>(); | ||
634 | lock (m_landList) | ||
635 | { | ||
636 | foreach (LandObject p in m_landList.Values) | ||
637 | { | ||
638 | if (!landOwnersAndParcels.ContainsKey(p.landData.OwnerID)) | ||
639 | { | ||
640 | List<LandObject> tempList = new List<LandObject>(); | ||
641 | tempList.Add(p); | ||
642 | landOwnersAndParcels.Add(p.landData.OwnerID, tempList); | ||
643 | } | ||
644 | else | ||
645 | { | ||
646 | landOwnersAndParcels[p.landData.OwnerID].Add(p); | ||
647 | } | ||
648 | } | ||
649 | } | ||
650 | |||
651 | foreach (UUID owner in landOwnersAndParcels.Keys) | ||
652 | { | ||
653 | int simArea = 0; | ||
654 | int simPrims = 0; | ||
655 | foreach (LandObject p in landOwnersAndParcels[owner]) | ||
656 | { | ||
657 | simArea += p.landData.Area; | ||
658 | simPrims += p.landData.OwnerPrims + p.landData.OtherPrims + p.landData.GroupPrims + | ||
659 | p.landData.SelectedPrims; | ||
660 | } | ||
661 | |||
662 | foreach (LandObject p in landOwnersAndParcels[owner]) | ||
663 | { | ||
664 | p.landData.SimwideArea = simArea; | ||
665 | p.landData.SimwidePrims = simPrims; | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | |||
670 | public void UpdateLandPrimCounts() | ||
671 | { | ||
672 | ResetAllLandPrimCounts(); | ||
673 | foreach (EntityBase obj in m_scene.Entities) | ||
674 | { | ||
675 | if (obj != null) | ||
676 | { | ||
677 | if ((obj is SceneObjectGroup) && !obj.IsDeleted && !((SceneObjectGroup) obj).IsAttachment) | ||
678 | { | ||
679 | m_scene.EventManager.TriggerParcelPrimCountAdd((SceneObjectGroup) obj); | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | FinalizeLandPrimCountUpdate(); | ||
684 | m_landPrimCountTainted = false; | ||
685 | } | ||
686 | |||
687 | public void PerformParcelPrimCountUpdate() | ||
688 | { | ||
689 | ResetAllLandPrimCounts(); | ||
690 | m_scene.EventManager.TriggerParcelPrimCountUpdate(); | ||
691 | FinalizeLandPrimCountUpdate(); | ||
692 | m_landPrimCountTainted = false; | ||
693 | } | ||
694 | |||
695 | /// <summary> | ||
696 | /// Subdivides a piece of land | ||
697 | /// </summary> | ||
698 | /// <param name="start_x">West Point</param> | ||
699 | /// <param name="start_y">South Point</param> | ||
700 | /// <param name="end_x">East Point</param> | ||
701 | /// <param name="end_y">North Point</param> | ||
702 | /// <param name="attempting_user_id">UUID of user who is trying to subdivide</param> | ||
703 | /// <returns>Returns true if successful</returns> | ||
704 | private void subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) | ||
705 | { | ||
706 | //First, lets loop through the points and make sure they are all in the same peice of land | ||
707 | //Get the land object at start | ||
708 | |||
709 | ILandObject startLandObject = GetLandObject(start_x, start_y); | ||
710 | |||
711 | if (startLandObject == null) return; | ||
712 | |||
713 | //Loop through the points | ||
714 | try | ||
715 | { | ||
716 | int totalX = end_x - start_x; | ||
717 | int totalY = end_y - start_y; | ||
718 | for (int y = 0; y < totalY; y++) | ||
719 | { | ||
720 | for (int x = 0; x < totalX; x++) | ||
721 | { | ||
722 | ILandObject tempLandObject = GetLandObject(start_x + x, start_y + y); | ||
723 | if (tempLandObject == null) return; | ||
724 | if (tempLandObject != startLandObject) return; | ||
725 | } | ||
726 | } | ||
727 | } | ||
728 | catch (Exception) | ||
729 | { | ||
730 | return; | ||
731 | } | ||
732 | |||
733 | //If we are still here, then they are subdividing within one piece of land | ||
734 | //Check owner | ||
735 | if (!m_scene.Permissions.CanEditParcel(attempting_user_id, startLandObject)) | ||
736 | { | ||
737 | return; | ||
738 | } | ||
739 | |||
740 | //Lets create a new land object with bitmap activated at that point (keeping the old land objects info) | ||
741 | ILandObject newLand = startLandObject.Copy(); | ||
742 | newLand.landData.Name = "Subdivision of " + newLand.landData.Name; | ||
743 | newLand.landData.GlobalID = UUID.Random(); | ||
744 | |||
745 | newLand.setLandBitmap(newLand.getSquareLandBitmap(start_x, start_y, end_x, end_y)); | ||
746 | |||
747 | //Now, lets set the subdivision area of the original to false | ||
748 | int startLandObjectIndex = startLandObject.landData.LocalID; | ||
749 | lock (m_landList) | ||
750 | { | ||
751 | m_landList[startLandObjectIndex].setLandBitmap( | ||
752 | newLand.modifyLandBitmapSquare(startLandObject.getLandBitmap(), start_x, start_y, end_x, end_y, false)); | ||
753 | m_landList[startLandObjectIndex].forceUpdateLandInfo(); | ||
754 | } | ||
755 | |||
756 | SetPrimsTainted(); | ||
757 | |||
758 | //Now add the new land object | ||
759 | ILandObject result = AddLandObject(newLand); | ||
760 | UpdateLandObject(startLandObject.landData.LocalID, startLandObject.landData); | ||
761 | result.sendLandUpdateToAvatarsOverMe(); | ||
762 | } | ||
763 | |||
764 | /// <summary> | ||
765 | /// Join 2 land objects together | ||
766 | /// </summary> | ||
767 | /// <param name="start_x">x value in first piece of land</param> | ||
768 | /// <param name="start_y">y value in first piece of land</param> | ||
769 | /// <param name="end_x">x value in second peice of land</param> | ||
770 | /// <param name="end_y">y value in second peice of land</param> | ||
771 | /// <param name="attempting_user_id">UUID of the avatar trying to join the land objects</param> | ||
772 | /// <returns>Returns true if successful</returns> | ||
773 | private void join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) | ||
774 | { | ||
775 | end_x -= 4; | ||
776 | end_y -= 4; | ||
777 | |||
778 | List<ILandObject> selectedLandObjects = new List<ILandObject>(); | ||
779 | int stepYSelected; | ||
780 | for (stepYSelected = start_y; stepYSelected <= end_y; stepYSelected += 4) | ||
781 | { | ||
782 | int stepXSelected; | ||
783 | for (stepXSelected = start_x; stepXSelected <= end_x; stepXSelected += 4) | ||
784 | { | ||
785 | ILandObject p = GetLandObject(stepXSelected, stepYSelected); | ||
786 | |||
787 | if (p != null) | ||
788 | { | ||
789 | if (!selectedLandObjects.Contains(p)) | ||
790 | { | ||
791 | selectedLandObjects.Add(p); | ||
792 | } | ||
793 | } | ||
794 | } | ||
795 | } | ||
796 | ILandObject masterLandObject = selectedLandObjects[0]; | ||
797 | selectedLandObjects.RemoveAt(0); | ||
798 | |||
799 | if (selectedLandObjects.Count < 1) | ||
800 | { | ||
801 | return; | ||
802 | } | ||
803 | if (!m_scene.Permissions.CanEditParcel(attempting_user_id, masterLandObject)) | ||
804 | { | ||
805 | return; | ||
806 | } | ||
807 | foreach (ILandObject p in selectedLandObjects) | ||
808 | { | ||
809 | if (p.landData.OwnerID != masterLandObject.landData.OwnerID) | ||
810 | { | ||
811 | return; | ||
812 | } | ||
813 | } | ||
814 | |||
815 | lock (m_landList) | ||
816 | { | ||
817 | foreach (ILandObject slaveLandObject in selectedLandObjects) | ||
818 | { | ||
819 | m_landList[masterLandObject.landData.LocalID].setLandBitmap( | ||
820 | slaveLandObject.mergeLandBitmaps(masterLandObject.getLandBitmap(), slaveLandObject.getLandBitmap())); | ||
821 | performFinalLandJoin(masterLandObject, slaveLandObject); | ||
822 | } | ||
823 | } | ||
824 | SetPrimsTainted(); | ||
825 | |||
826 | masterLandObject.sendLandUpdateToAvatarsOverMe(); | ||
827 | } | ||
828 | |||
829 | #endregion | ||
830 | |||
831 | #region Parcel Updating | ||
832 | |||
833 | /// <summary> | ||
834 | /// Where we send the ParcelOverlay packet to the client | ||
835 | /// </summary> | ||
836 | /// <param name="remote_client">The object representing the client</param> | ||
837 | public void SendParcelOverlay(IClientAPI remote_client) | ||
838 | { | ||
839 | const int LAND_BLOCKS_PER_PACKET = 1024; | ||
840 | |||
841 | byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET]; | ||
842 | int byteArrayCount = 0; | ||
843 | int sequenceID = 0; | ||
844 | |||
845 | for (int y = 0; y < 64; y++) | ||
846 | { | ||
847 | for (int x = 0; x < 64; x++) | ||
848 | { | ||
849 | byte tempByte = 0; //This represents the byte for the current 4x4 | ||
850 | |||
851 | ILandObject currentParcelBlock = GetLandObject(x * 4, y * 4); | ||
852 | |||
853 | if (currentParcelBlock != null) | ||
854 | { | ||
855 | if (currentParcelBlock.landData.OwnerID == remote_client.AgentId) | ||
856 | { | ||
857 | //Owner Flag | ||
858 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_OWNED_BY_REQUESTER); | ||
859 | } | ||
860 | else if (currentParcelBlock.landData.SalePrice > 0 && | ||
861 | (currentParcelBlock.landData.AuthBuyerID == UUID.Zero || | ||
862 | currentParcelBlock.landData.AuthBuyerID == remote_client.AgentId)) | ||
863 | { | ||
864 | //Sale Flag | ||
865 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_IS_FOR_SALE); | ||
866 | } | ||
867 | else if (currentParcelBlock.landData.OwnerID == UUID.Zero) | ||
868 | { | ||
869 | //Public Flag | ||
870 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_PUBLIC); | ||
871 | } | ||
872 | else | ||
873 | { | ||
874 | //Other Flag | ||
875 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_TYPE_OWNED_BY_OTHER); | ||
876 | } | ||
877 | |||
878 | //Now for border control | ||
879 | |||
880 | ILandObject westParcel = null; | ||
881 | ILandObject southParcel = null; | ||
882 | if (x > 0) | ||
883 | { | ||
884 | westParcel = GetLandObject((x - 1) * 4, y * 4); | ||
885 | } | ||
886 | if (y > 0) | ||
887 | { | ||
888 | southParcel = GetLandObject(x * 4, (y - 1) * 4); | ||
889 | } | ||
890 | |||
891 | if (x == 0) | ||
892 | { | ||
893 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_WEST); | ||
894 | } | ||
895 | else if (westParcel != null && westParcel != currentParcelBlock) | ||
896 | { | ||
897 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_WEST); | ||
898 | } | ||
899 | |||
900 | if (y == 0) | ||
901 | { | ||
902 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_SOUTH); | ||
903 | } | ||
904 | else if (southParcel != null && southParcel != currentParcelBlock) | ||
905 | { | ||
906 | tempByte = Convert.ToByte(tempByte | LandChannel.LAND_FLAG_PROPERTY_BORDER_SOUTH); | ||
907 | } | ||
908 | |||
909 | byteArray[byteArrayCount] = tempByte; | ||
910 | byteArrayCount++; | ||
911 | if (byteArrayCount >= LAND_BLOCKS_PER_PACKET) | ||
912 | { | ||
913 | remote_client.SendLandParcelOverlay(byteArray, sequenceID); | ||
914 | byteArrayCount = 0; | ||
915 | sequenceID++; | ||
916 | byteArray = new byte[LAND_BLOCKS_PER_PACKET]; | ||
917 | } | ||
918 | } | ||
919 | } | ||
920 | } | ||
921 | } | ||
922 | |||
923 | public void handleParcelPropertiesRequest(int start_x, int start_y, int end_x, int end_y, int sequence_id, | ||
924 | bool snap_selection, IClientAPI remote_client) | ||
925 | { | ||
926 | //Get the land objects within the bounds | ||
927 | List<ILandObject> temp = new List<ILandObject>(); | ||
928 | int inc_x = end_x - start_x; | ||
929 | int inc_y = end_y - start_y; | ||
930 | for (int x = 0; x < inc_x; x++) | ||
931 | { | ||
932 | for (int y = 0; y < inc_y; y++) | ||
933 | { | ||
934 | ILandObject currentParcel = GetLandObject(start_x + x, start_y + y); | ||
935 | |||
936 | if (currentParcel != null) | ||
937 | { | ||
938 | if (!temp.Contains(currentParcel)) | ||
939 | { | ||
940 | currentParcel.forceUpdateLandInfo(); | ||
941 | temp.Add(currentParcel); | ||
942 | } | ||
943 | } | ||
944 | } | ||
945 | } | ||
946 | |||
947 | int requestResult = LandChannel.LAND_RESULT_SINGLE; | ||
948 | if (temp.Count > 1) | ||
949 | { | ||
950 | requestResult = LandChannel.LAND_RESULT_MULTIPLE; | ||
951 | } | ||
952 | |||
953 | for (int i = 0; i < temp.Count; i++) | ||
954 | { | ||
955 | temp[i].sendLandProperties(sequence_id, snap_selection, requestResult, remote_client); | ||
956 | } | ||
957 | |||
958 | SendParcelOverlay(remote_client); | ||
959 | } | ||
960 | |||
961 | public void handleParcelPropertiesUpdateRequest(LandUpdateArgs args, int localID, IClientAPI remote_client) | ||
962 | { | ||
963 | ILandObject land; | ||
964 | lock (m_landList) | ||
965 | { | ||
966 | m_landList.TryGetValue(localID, out land); | ||
967 | } | ||
968 | |||
969 | if (land != null) land.updateLandProperties(args, remote_client); | ||
970 | } | ||
971 | |||
972 | public void handleParcelDivideRequest(int west, int south, int east, int north, IClientAPI remote_client) | ||
973 | { | ||
974 | subdivide(west, south, east, north, remote_client.AgentId); | ||
975 | } | ||
976 | |||
977 | public void handleParcelJoinRequest(int west, int south, int east, int north, IClientAPI remote_client) | ||
978 | { | ||
979 | join(west, south, east, north, remote_client.AgentId); | ||
980 | } | ||
981 | |||
982 | public void handleParcelSelectObjectsRequest(int local_id, int request_type, List<UUID> returnIDs, IClientAPI remote_client) | ||
983 | { | ||
984 | m_landList[local_id].sendForceObjectSelect(local_id, request_type, returnIDs, remote_client); | ||
985 | } | ||
986 | |||
987 | public void handleParcelObjectOwnersRequest(int local_id, IClientAPI remote_client) | ||
988 | { | ||
989 | ILandObject land; | ||
990 | lock (m_landList) | ||
991 | { | ||
992 | m_landList.TryGetValue(local_id, out land); | ||
993 | } | ||
994 | |||
995 | if (land != null) | ||
996 | { | ||
997 | m_landList[local_id].sendLandObjectOwners(remote_client); | ||
998 | } | ||
999 | else | ||
1000 | { | ||
1001 | m_log.WarnFormat("[PARCEL]: Invalid land object {0} passed for parcel object owner request", local_id); | ||
1002 | } | ||
1003 | } | ||
1004 | |||
1005 | public void handleParcelGodForceOwner(int local_id, UUID ownerID, IClientAPI remote_client) | ||
1006 | { | ||
1007 | ILandObject land; | ||
1008 | lock (m_landList) | ||
1009 | { | ||
1010 | m_landList.TryGetValue(local_id, out land); | ||
1011 | } | ||
1012 | |||
1013 | if (land != null) | ||
1014 | { | ||
1015 | if (m_scene.Permissions.IsGod(remote_client.AgentId)) | ||
1016 | { | ||
1017 | land.landData.OwnerID = ownerID; | ||
1018 | |||
1019 | m_scene.Broadcast(SendParcelOverlay); | ||
1020 | land.sendLandUpdateToClient(remote_client); | ||
1021 | } | ||
1022 | } | ||
1023 | } | ||
1024 | |||
1025 | public void handleParcelAbandonRequest(int local_id, IClientAPI remote_client) | ||
1026 | { | ||
1027 | ILandObject land; | ||
1028 | lock (m_landList) | ||
1029 | { | ||
1030 | m_landList.TryGetValue(local_id, out land); | ||
1031 | } | ||
1032 | |||
1033 | if (land != null) | ||
1034 | { | ||
1035 | if (m_scene.Permissions.CanAbandonParcel(remote_client.AgentId, land)) | ||
1036 | { | ||
1037 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
1038 | land.landData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
1039 | else | ||
1040 | land.landData.OwnerID = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
1041 | m_scene.Broadcast(SendParcelOverlay); | ||
1042 | land.sendLandUpdateToClient(remote_client); | ||
1043 | } | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | public void handleParcelReclaim(int local_id, IClientAPI remote_client) | ||
1048 | { | ||
1049 | ILandObject land; | ||
1050 | lock (m_landList) | ||
1051 | { | ||
1052 | m_landList.TryGetValue(local_id, out land); | ||
1053 | } | ||
1054 | |||
1055 | if (land != null) | ||
1056 | { | ||
1057 | if (m_scene.Permissions.CanReclaimParcel(remote_client.AgentId, land)) | ||
1058 | { | ||
1059 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
1060 | land.landData.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner; | ||
1061 | else | ||
1062 | land.landData.OwnerID = m_scene.RegionInfo.MasterAvatarAssignedUUID; | ||
1063 | land.landData.ClaimDate = Util.UnixTimeSinceEpoch(); | ||
1064 | m_scene.Broadcast(SendParcelOverlay); | ||
1065 | land.sendLandUpdateToClient(remote_client); | ||
1066 | } | ||
1067 | } | ||
1068 | } | ||
1069 | #endregion | ||
1070 | |||
1071 | // If the economy has been validated by the economy module, | ||
1072 | // and land has been validated as well, this method transfers | ||
1073 | // the land ownership | ||
1074 | |||
1075 | public void handleLandBuyRequest(Object o, EventManager.LandBuyArgs e) | ||
1076 | { | ||
1077 | if (e.economyValidated && e.landValidated) | ||
1078 | { | ||
1079 | ILandObject land; | ||
1080 | lock (m_landList) | ||
1081 | { | ||
1082 | m_landList.TryGetValue(e.parcelLocalID, out land); | ||
1083 | } | ||
1084 | |||
1085 | if (land != null) | ||
1086 | { | ||
1087 | land.updateLandSold(e.agentId, e.groupId, e.groupOwned, (uint)e.transactionID, e.parcelPrice, e.parcelArea); | ||
1088 | } | ||
1089 | } | ||
1090 | } | ||
1091 | |||
1092 | // After receiving a land buy packet, first the data needs to | ||
1093 | // be validated. This method validates the right to buy the | ||
1094 | // parcel | ||
1095 | |||
1096 | public void handleLandValidationRequest(Object o, EventManager.LandBuyArgs e) | ||
1097 | { | ||
1098 | if (e.landValidated == false) | ||
1099 | { | ||
1100 | ILandObject lob = null; | ||
1101 | lock (m_landList) | ||
1102 | { | ||
1103 | m_landList.TryGetValue(e.parcelLocalID, out lob); | ||
1104 | } | ||
1105 | |||
1106 | if (lob != null) | ||
1107 | { | ||
1108 | UUID AuthorizedID = lob.landData.AuthBuyerID; | ||
1109 | int saleprice = lob.landData.SalePrice; | ||
1110 | UUID pOwnerID = lob.landData.OwnerID; | ||
1111 | |||
1112 | bool landforsale = ((lob.landData.Flags & | ||
1113 | (uint)(Parcel.ParcelFlags.ForSale | Parcel.ParcelFlags.ForSaleObjects | Parcel.ParcelFlags.SellParcelObjects)) != 0); | ||
1114 | if ((AuthorizedID == UUID.Zero || AuthorizedID == e.agentId) && e.parcelPrice >= saleprice && landforsale) | ||
1115 | { | ||
1116 | // TODO I don't think we have to lock it here, no? | ||
1117 | //lock (e) | ||
1118 | //{ | ||
1119 | e.parcelOwnerID = pOwnerID; | ||
1120 | e.landValidated = true; | ||
1121 | //} | ||
1122 | } | ||
1123 | } | ||
1124 | } | ||
1125 | } | ||
1126 | |||
1127 | #region Land Object From Storage Functions | ||
1128 | |||
1129 | public void IncomingLandObjectsFromStorage(List<LandData> data) | ||
1130 | { | ||
1131 | for (int i = 0; i < data.Count; i++) | ||
1132 | { | ||
1133 | IncomingLandObjectFromStorage(data[i]); | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | public void IncomingLandObjectFromStorage(LandData data) | ||
1138 | { | ||
1139 | ILandObject new_land = new LandObject(data.OwnerID, data.IsGroupOwned, m_scene); | ||
1140 | new_land.landData = data.Copy(); | ||
1141 | new_land.setLandBitmapFromByteArray(); | ||
1142 | AddLandObject(new_land); | ||
1143 | } | ||
1144 | |||
1145 | public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient) | ||
1146 | { | ||
1147 | ILandObject selectedParcel = null; | ||
1148 | lock (m_landList) | ||
1149 | { | ||
1150 | m_landList.TryGetValue(localID, out selectedParcel); | ||
1151 | } | ||
1152 | |||
1153 | if (selectedParcel == null) return; | ||
1154 | |||
1155 | selectedParcel.returnLandObjects(returnType, agentIDs, taskIDs, remoteClient); | ||
1156 | } | ||
1157 | |||
1158 | public void NoLandDataFromStorage() | ||
1159 | { | ||
1160 | ResetSimLandObjects(); | ||
1161 | } | ||
1162 | |||
1163 | #endregion | ||
1164 | |||
1165 | public void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel) | ||
1166 | { | ||
1167 | lock (m_landList) | ||
1168 | { | ||
1169 | foreach (LandObject obj in m_landList.Values) | ||
1170 | { | ||
1171 | obj.setParcelObjectMaxOverride(overrideDel); | ||
1172 | } | ||
1173 | } | ||
1174 | } | ||
1175 | |||
1176 | public void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel) | ||
1177 | { | ||
1178 | } | ||
1179 | |||
1180 | #region CAPS handler | ||
1181 | |||
1182 | private void OnRegisterCaps(UUID agentID, Caps caps) | ||
1183 | { | ||
1184 | string capsBase = "/CAPS/" + caps.CapsObjectPath; | ||
1185 | caps.RegisterHandler("RemoteParcelRequest", | ||
1186 | new RestStreamHandler("POST", capsBase + remoteParcelRequestPath, | ||
1187 | delegate(string request, string path, string param, | ||
1188 | OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
1189 | { | ||
1190 | return RemoteParcelRequest(request, path, param, agentID, caps); | ||
1191 | })); | ||
1192 | } | ||
1193 | |||
1194 | // we cheat here: As we don't have (and want) a grid-global parcel-store, we can't return the | ||
1195 | // "real" parcelID, because we wouldn't be able to map that to the region the parcel belongs to. | ||
1196 | // So, we create a "fake" parcelID by using the regionHandle (64 bit), and the local (integer) x | ||
1197 | // and y coordinate (each 8 bit), encoded in a UUID (128 bit). | ||
1198 | // | ||
1199 | // Request format: | ||
1200 | // <llsd> | ||
1201 | // <map> | ||
1202 | // <key>location</key> | ||
1203 | // <array> | ||
1204 | // <real>1.23</real> | ||
1205 | // <real>45..6</real> | ||
1206 | // <real>78.9</real> | ||
1207 | // </array> | ||
1208 | // <key>region_id</key> | ||
1209 | // <uuid>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</uuid> | ||
1210 | // </map> | ||
1211 | // </llsd> | ||
1212 | private string RemoteParcelRequest(string request, string path, string param, UUID agentID, Caps caps) | ||
1213 | { | ||
1214 | UUID parcelID = UUID.Zero; | ||
1215 | try | ||
1216 | { | ||
1217 | Hashtable hash = new Hashtable(); | ||
1218 | hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); | ||
1219 | if (hash.ContainsKey("region_id") && hash.ContainsKey("location")) | ||
1220 | { | ||
1221 | UUID regionID = (UUID)hash["region_id"]; | ||
1222 | ArrayList list = (ArrayList)hash["location"]; | ||
1223 | uint x = (uint)(double)list[0]; | ||
1224 | uint y = (uint)(double)list[1]; | ||
1225 | if (hash.ContainsKey("region_handle")) | ||
1226 | { | ||
1227 | // if you do a "About Landmark" on a landmark a second time, the viewer sends the | ||
1228 | // region_handle it got earlier via RegionHandleRequest | ||
1229 | ulong regionHandle = Util.BytesToUInt64Big((byte[])hash["region_handle"]); | ||
1230 | parcelID = Util.BuildFakeParcelID(regionHandle, x, y); | ||
1231 | } | ||
1232 | else if (regionID == m_scene.RegionInfo.RegionID) | ||
1233 | { | ||
1234 | // a parcel request for a local parcel => no need to query the grid | ||
1235 | parcelID = Util.BuildFakeParcelID(m_scene.RegionInfo.RegionHandle, x, y); | ||
1236 | } | ||
1237 | else | ||
1238 | { | ||
1239 | // a parcel request for a parcel in another region. Ask the grid about the region | ||
1240 | RegionInfo info = m_scene.CommsManager.GridService.RequestNeighbourInfo(regionID); | ||
1241 | if (info != null) | ||
1242 | parcelID = Util.BuildFakeParcelID(info.RegionHandle, x, y); | ||
1243 | } | ||
1244 | } | ||
1245 | } | ||
1246 | catch (LLSD.LLSDParseException e) | ||
1247 | { | ||
1248 | m_log.ErrorFormat("[LAND] Fetch error: {0}", e.Message); | ||
1249 | m_log.ErrorFormat("[LAND] ... in request {0}", request); | ||
1250 | } | ||
1251 | catch(InvalidCastException) | ||
1252 | { | ||
1253 | m_log.ErrorFormat("[LAND] Wrong type in request {0}", request); | ||
1254 | } | ||
1255 | |||
1256 | LLSDRemoteParcelResponse response = new LLSDRemoteParcelResponse(); | ||
1257 | response.parcel_id = parcelID; | ||
1258 | m_log.DebugFormat("[LAND] got parcelID {0}", parcelID); | ||
1259 | |||
1260 | return LLSDHelpers.SerialiseLLSDReply(response); | ||
1261 | } | ||
1262 | |||
1263 | #endregion | ||
1264 | |||
1265 | private void handleParcelDwell(int localID, IClientAPI remoteClient) | ||
1266 | { | ||
1267 | ILandObject selectedParcel = null; | ||
1268 | lock (m_landList) | ||
1269 | { | ||
1270 | if (!m_landList.TryGetValue(localID, out selectedParcel)) | ||
1271 | return; | ||
1272 | } | ||
1273 | |||
1274 | remoteClient.SendParcelDwellReply(localID, selectedParcel.landData.GlobalID, selectedParcel.landData.Dwell); | ||
1275 | } | ||
1276 | |||
1277 | private void handleParcelInfo(IClientAPI remoteClient, UUID parcelID) | ||
1278 | { | ||
1279 | if (parcelID == UUID.Zero) | ||
1280 | return; | ||
1281 | |||
1282 | ExtendedLandData data = (ExtendedLandData)parcelInfoCache.Get(parcelID, delegate(UUID parcel) { | ||
1283 | // assume we've got the parcelID we just computed in RemoteParcelRequest | ||
1284 | ExtendedLandData extLandData = new ExtendedLandData(); | ||
1285 | Util.ParseFakeParcelID(parcel, out extLandData.regionHandle, out extLandData.x, out extLandData.y); | ||
1286 | m_log.DebugFormat("[LAND] got parcelinfo request for regionHandle {0}, x/y {1}/{2}", | ||
1287 | extLandData.regionHandle, extLandData.x, extLandData.y); | ||
1288 | |||
1289 | // for this region or for somewhere else? | ||
1290 | if (extLandData.regionHandle == m_scene.RegionInfo.RegionHandle) | ||
1291 | { | ||
1292 | extLandData.landData = this.GetLandObject(extLandData.x, extLandData.y).landData; | ||
1293 | } | ||
1294 | else | ||
1295 | { | ||
1296 | extLandData.landData = m_scene.CommsManager.GridService.RequestLandData(extLandData.regionHandle, | ||
1297 | extLandData.x, | ||
1298 | extLandData.y); | ||
1299 | if (extLandData.landData == null) | ||
1300 | { | ||
1301 | // we didn't find the region/land => don't cache | ||
1302 | return null; | ||
1303 | } | ||
1304 | } | ||
1305 | return extLandData; | ||
1306 | }); | ||
1307 | |||
1308 | if (data != null) // if we found some data, send it | ||
1309 | { | ||
1310 | RegionInfo info; | ||
1311 | if (data.regionHandle == m_scene.RegionInfo.RegionHandle) | ||
1312 | { | ||
1313 | info = m_scene.RegionInfo; | ||
1314 | } | ||
1315 | else | ||
1316 | { | ||
1317 | // most likely still cached from building the extLandData entry | ||
1318 | info = m_scene.CommsManager.GridService.RequestNeighbourInfo(data.regionHandle); | ||
1319 | } | ||
1320 | // we need to transfer the fake parcelID, not the one in landData, so the viewer can match it to the landmark. | ||
1321 | m_log.DebugFormat("[LAND] got parcelinfo for parcel {0} in region {1}; sending...", | ||
1322 | data.landData.Name, data.regionHandle); | ||
1323 | remoteClient.SendParcelInfo(info, data.landData, parcelID, data.x, data.y); | ||
1324 | } | ||
1325 | else | ||
1326 | m_log.Debug("[LAND] got no parcelinfo; not sending"); | ||
1327 | } | ||
1328 | |||
1329 | public void setParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime) | ||
1330 | { | ||
1331 | ILandObject land; | ||
1332 | lock (m_landList) | ||
1333 | { | ||
1334 | m_landList.TryGetValue(localID, out land); | ||
1335 | } | ||
1336 | |||
1337 | if (land == null) return; | ||
1338 | |||
1339 | if (!m_scene.Permissions.CanEditParcel(remoteClient.AgentId, land)) | ||
1340 | return; | ||
1341 | |||
1342 | land.landData.OtherCleanTime = otherCleanTime; | ||
1343 | |||
1344 | UpdateLandObject(localID, land.landData); | ||
1345 | } | ||
1346 | } | ||
1347 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs new file mode 100644 index 0000000..fc5bef1 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs | |||
@@ -0,0 +1,930 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using OpenMetaverse; | ||
32 | using log4net; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Framework.Interfaces; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | |||
37 | namespace OpenSim.Region.CoreModules.World.Land | ||
38 | { | ||
39 | /// <summary> | ||
40 | /// Keeps track of a specific piece of land's information | ||
41 | /// </summary> | ||
42 | public class LandObject : ILandObject | ||
43 | { | ||
44 | #region Member Variables | ||
45 | |||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | private bool[,] m_landBitmap = new bool[64,64]; | ||
48 | |||
49 | protected LandData m_landData = new LandData(); | ||
50 | protected Scene m_scene; | ||
51 | protected List<SceneObjectGroup> primsOverMe = new List<SceneObjectGroup>(); | ||
52 | |||
53 | public bool[,] landBitmap | ||
54 | { | ||
55 | get { return m_landBitmap; } | ||
56 | set { m_landBitmap = value; } | ||
57 | } | ||
58 | |||
59 | #endregion | ||
60 | |||
61 | #region ILandObject Members | ||
62 | |||
63 | public LandData landData | ||
64 | { | ||
65 | get { return m_landData; } | ||
66 | |||
67 | set { m_landData = value; } | ||
68 | } | ||
69 | |||
70 | public UUID regionUUID | ||
71 | { | ||
72 | get { return m_scene.RegionInfo.RegionID; } | ||
73 | } | ||
74 | |||
75 | #region Constructors | ||
76 | |||
77 | public LandObject(UUID owner_id, bool is_group_owned, Scene scene) | ||
78 | { | ||
79 | m_scene = scene; | ||
80 | landData.OwnerID = owner_id; | ||
81 | landData.IsGroupOwned = is_group_owned; | ||
82 | } | ||
83 | |||
84 | #endregion | ||
85 | |||
86 | #region Member Functions | ||
87 | |||
88 | #region General Functions | ||
89 | |||
90 | /// <summary> | ||
91 | /// Checks to see if this land object contains a point | ||
92 | /// </summary> | ||
93 | /// <param name="x"></param> | ||
94 | /// <param name="y"></param> | ||
95 | /// <returns>Returns true if the piece of land contains the specified point</returns> | ||
96 | public bool containsPoint(int x, int y) | ||
97 | { | ||
98 | if (x >= 0 && y >= 0 && x <= Constants.RegionSize && x <= Constants.RegionSize) | ||
99 | { | ||
100 | return (landBitmap[x / 4, y / 4] == true); | ||
101 | } | ||
102 | else | ||
103 | { | ||
104 | return false; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | public ILandObject Copy() | ||
109 | { | ||
110 | ILandObject newLand = new LandObject(landData.OwnerID, landData.IsGroupOwned, m_scene); | ||
111 | |||
112 | //Place all new variables here! | ||
113 | newLand.landBitmap = (bool[,]) (landBitmap.Clone()); | ||
114 | newLand.landData = landData.Copy(); | ||
115 | |||
116 | return newLand; | ||
117 | } | ||
118 | |||
119 | |||
120 | static overrideParcelMaxPrimCountDelegate overrideParcelMaxPrimCount; | ||
121 | static overrideSimulatorMaxPrimCountDelegate overrideSimulatorMaxPrimCount; | ||
122 | |||
123 | public void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel) | ||
124 | { | ||
125 | overrideParcelMaxPrimCount = overrideDel; | ||
126 | } | ||
127 | public void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel) | ||
128 | { | ||
129 | overrideSimulatorMaxPrimCount = overrideDel; | ||
130 | } | ||
131 | |||
132 | public int getParcelMaxPrimCount(ILandObject thisObject) | ||
133 | { | ||
134 | if (overrideParcelMaxPrimCount != null) | ||
135 | { | ||
136 | return overrideParcelMaxPrimCount(thisObject); | ||
137 | } | ||
138 | else | ||
139 | { | ||
140 | //Normal Calculations | ||
141 | return Convert.ToInt32( | ||
142 | Math.Round((Convert.ToDecimal(landData.Area) / Convert.ToDecimal(65536)) * m_scene.objectCapacity * | ||
143 | Convert.ToDecimal(m_scene.RegionInfo.RegionSettings.ObjectBonus))); ; | ||
144 | } | ||
145 | } | ||
146 | public int getSimulatorMaxPrimCount(ILandObject thisObject) | ||
147 | { | ||
148 | if (overrideSimulatorMaxPrimCount != null) | ||
149 | { | ||
150 | return overrideSimulatorMaxPrimCount(thisObject); | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | //Normal Calculations | ||
155 | return m_scene.objectCapacity; | ||
156 | } | ||
157 | } | ||
158 | #endregion | ||
159 | |||
160 | #region Packet Request Handling | ||
161 | |||
162 | public void sendLandProperties(int sequence_id, bool snap_selection, int request_result, IClientAPI remote_client) | ||
163 | { | ||
164 | IEstateModule estateModule = m_scene.RequestModuleInterface<IEstateModule>(); | ||
165 | uint regionFlags = 336723974 & ~((uint)(RegionFlags.AllowLandmark | RegionFlags.AllowSetHome)); | ||
166 | if (estateModule != null) | ||
167 | regionFlags = estateModule.GetRegionFlags(); | ||
168 | |||
169 | // In a perfect world, this would have worked. | ||
170 | // | ||
171 | // if ((landData.Flags & (uint)Parcel.ParcelFlags.AllowLandmark) != 0) | ||
172 | // regionFlags |= (uint)RegionFlags.AllowLandmark; | ||
173 | // if (landData.OwnerID == remote_client.AgentId) | ||
174 | // regionFlags |= (uint)RegionFlags.AllowSetHome; | ||
175 | remote_client.SendLandProperties(sequence_id, | ||
176 | snap_selection, request_result, landData, | ||
177 | (float)m_scene.RegionInfo.RegionSettings.ObjectBonus, | ||
178 | getParcelMaxPrimCount(this), | ||
179 | getSimulatorMaxPrimCount(this), regionFlags); | ||
180 | } | ||
181 | |||
182 | public void updateLandProperties(LandUpdateArgs args, IClientAPI remote_client) | ||
183 | { | ||
184 | if (m_scene.Permissions.CanEditParcel(remote_client.AgentId,this)) | ||
185 | { | ||
186 | //Needs later group support | ||
187 | LandData newData = landData.Copy(); | ||
188 | |||
189 | if (args.AuthBuyerID != newData.AuthBuyerID || args.SalePrice != newData.SalePrice) | ||
190 | { | ||
191 | if (m_scene.Permissions.CanSellParcel(remote_client.AgentId, this)) | ||
192 | { | ||
193 | newData.AuthBuyerID = args.AuthBuyerID; | ||
194 | newData.SalePrice = args.SalePrice; | ||
195 | } | ||
196 | } | ||
197 | newData.Category = args.Category; | ||
198 | newData.Description = args.Desc; | ||
199 | newData.GroupID = args.GroupID; | ||
200 | newData.LandingType = args.LandingType; | ||
201 | newData.MediaAutoScale = args.MediaAutoScale; | ||
202 | newData.MediaID = args.MediaID; | ||
203 | newData.MediaURL = args.MediaURL; | ||
204 | newData.MusicURL = args.MusicURL; | ||
205 | newData.Name = args.Name; | ||
206 | newData.Flags = args.ParcelFlags; | ||
207 | newData.PassHours = args.PassHours; | ||
208 | newData.PassPrice = args.PassPrice; | ||
209 | newData.SnapshotID = args.SnapshotID; | ||
210 | newData.UserLocation = args.UserLocation; | ||
211 | newData.UserLookAt = args.UserLookAt; | ||
212 | |||
213 | m_scene.LandChannel.UpdateLandObject(landData.LocalID, newData); | ||
214 | |||
215 | sendLandUpdateToAvatarsOverMe(); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | public void updateLandSold(UUID avatarID, UUID groupID, bool groupOwned, uint AuctionID, int claimprice, int area) | ||
220 | { | ||
221 | LandData newData = landData.Copy(); | ||
222 | newData.OwnerID = avatarID; | ||
223 | newData.GroupID = groupID; | ||
224 | newData.IsGroupOwned = groupOwned; | ||
225 | //newData.auctionID = AuctionID; | ||
226 | newData.ClaimDate = Util.UnixTimeSinceEpoch(); | ||
227 | newData.ClaimPrice = claimprice; | ||
228 | newData.SalePrice = 0; | ||
229 | newData.AuthBuyerID = UUID.Zero; | ||
230 | newData.Flags &= ~(uint) (Parcel.ParcelFlags.ForSale | Parcel.ParcelFlags.ForSaleObjects | Parcel.ParcelFlags.SellParcelObjects); | ||
231 | m_scene.LandChannel.UpdateLandObject(landData.LocalID, newData); | ||
232 | |||
233 | sendLandUpdateToAvatarsOverMe(); | ||
234 | } | ||
235 | |||
236 | public bool isEitherBannedOrRestricted(UUID avatar) | ||
237 | { | ||
238 | if (isBannedFromLand(avatar)) | ||
239 | { | ||
240 | return true; | ||
241 | } | ||
242 | else if (isRestrictedFromLand(avatar)) | ||
243 | { | ||
244 | return true; | ||
245 | } | ||
246 | return false; | ||
247 | } | ||
248 | |||
249 | public bool isBannedFromLand(UUID avatar) | ||
250 | { | ||
251 | if ((landData.Flags & (uint) Parcel.ParcelFlags.UseBanList) > 0) | ||
252 | { | ||
253 | ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry(); | ||
254 | entry.AgentID = avatar; | ||
255 | entry.Flags = AccessList.Ban; | ||
256 | entry.Time = new DateTime(); | ||
257 | if (landData.ParcelAccessList.Contains(entry)) | ||
258 | { | ||
259 | //They are banned, so lets send them a notice about this parcel | ||
260 | return true; | ||
261 | } | ||
262 | } | ||
263 | return false; | ||
264 | } | ||
265 | |||
266 | public bool isRestrictedFromLand(UUID avatar) | ||
267 | { | ||
268 | if ((landData.Flags & (uint) Parcel.ParcelFlags.UseAccessList) > 0) | ||
269 | { | ||
270 | ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry(); | ||
271 | entry.AgentID = avatar; | ||
272 | entry.Flags = AccessList.Access; | ||
273 | entry.Time = new DateTime(); | ||
274 | if (!landData.ParcelAccessList.Contains(entry)) | ||
275 | { | ||
276 | //They are not allowed in this parcel, but not banned, so lets send them a notice about this parcel | ||
277 | return true; | ||
278 | } | ||
279 | } | ||
280 | return false; | ||
281 | } | ||
282 | |||
283 | public void sendLandUpdateToClient(IClientAPI remote_client) | ||
284 | { | ||
285 | sendLandProperties(0, false, 0, remote_client); | ||
286 | } | ||
287 | |||
288 | public void sendLandUpdateToAvatarsOverMe() | ||
289 | { | ||
290 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
291 | ILandObject over = null; | ||
292 | for (int i = 0; i < avatars.Count; i++) | ||
293 | { | ||
294 | try | ||
295 | { | ||
296 | over = | ||
297 | m_scene.LandChannel.GetLandObject(Util.Clamp<int>((int)Math.Round(avatars[i].AbsolutePosition.X), 0, 255), | ||
298 | Util.Clamp<int>((int)Math.Round(avatars[i].AbsolutePosition.Y), 0, 255)); | ||
299 | } | ||
300 | catch (Exception) | ||
301 | { | ||
302 | m_log.Warn("[LAND]: " + "unable to get land at x: " + Math.Round(avatars[i].AbsolutePosition.X) + " y: " + | ||
303 | Math.Round(avatars[i].AbsolutePosition.Y)); | ||
304 | } | ||
305 | |||
306 | if (over != null) | ||
307 | { | ||
308 | if (over.landData.LocalID == landData.LocalID) | ||
309 | { | ||
310 | if (((over.landData.Flags & (uint)Parcel.ParcelFlags.AllowDamage) != 0) && m_scene.RegionInfo.RegionSettings.AllowDamage) | ||
311 | avatars[i].Invulnerable = false; | ||
312 | else | ||
313 | avatars[i].Invulnerable = true; | ||
314 | |||
315 | sendLandUpdateToClient(avatars[i].ControllingClient); | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | |||
321 | #endregion | ||
322 | |||
323 | #region AccessList Functions | ||
324 | |||
325 | public List<UUID> createAccessListArrayByFlag(AccessList flag) | ||
326 | { | ||
327 | List<UUID> list = new List<UUID>(); | ||
328 | foreach (ParcelManager.ParcelAccessEntry entry in landData.ParcelAccessList) | ||
329 | { | ||
330 | if (entry.Flags == flag) | ||
331 | { | ||
332 | list.Add(entry.AgentID); | ||
333 | } | ||
334 | } | ||
335 | if (list.Count == 0) | ||
336 | { | ||
337 | list.Add(UUID.Zero); | ||
338 | } | ||
339 | |||
340 | return list; | ||
341 | } | ||
342 | |||
343 | public void sendAccessList(UUID agentID, UUID sessionID, uint flags, int sequenceID, | ||
344 | IClientAPI remote_client) | ||
345 | { | ||
346 | |||
347 | if (flags == (uint) AccessList.Access || flags == (uint) AccessList.Both) | ||
348 | { | ||
349 | List<UUID> avatars = createAccessListArrayByFlag(AccessList.Access); | ||
350 | remote_client.SendLandAccessListData(avatars,(uint) AccessList.Access,landData.LocalID); | ||
351 | } | ||
352 | |||
353 | if (flags == (uint) AccessList.Ban || flags == (uint) AccessList.Both) | ||
354 | { | ||
355 | List<UUID> avatars = createAccessListArrayByFlag(AccessList.Ban); | ||
356 | remote_client.SendLandAccessListData(avatars, (uint)AccessList.Ban, landData.LocalID); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | public void updateAccessList(uint flags, List<ParcelManager.ParcelAccessEntry> entries, IClientAPI remote_client) | ||
361 | { | ||
362 | LandData newData = landData.Copy(); | ||
363 | |||
364 | if (entries.Count == 1 && entries[0].AgentID == UUID.Zero) | ||
365 | { | ||
366 | entries.Clear(); | ||
367 | } | ||
368 | |||
369 | List<ParcelManager.ParcelAccessEntry> toRemove = new List<ParcelManager.ParcelAccessEntry>(); | ||
370 | foreach (ParcelManager.ParcelAccessEntry entry in newData.ParcelAccessList) | ||
371 | { | ||
372 | if (entry.Flags == (AccessList)flags) | ||
373 | { | ||
374 | toRemove.Add(entry); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | foreach (ParcelManager.ParcelAccessEntry entry in toRemove) | ||
379 | { | ||
380 | newData.ParcelAccessList.Remove(entry); | ||
381 | } | ||
382 | foreach (ParcelManager.ParcelAccessEntry entry in entries) | ||
383 | { | ||
384 | ParcelManager.ParcelAccessEntry temp = new ParcelManager.ParcelAccessEntry(); | ||
385 | temp.AgentID = entry.AgentID; | ||
386 | temp.Time = new DateTime(); //Pointless? Yes. | ||
387 | temp.Flags = (AccessList)flags; | ||
388 | |||
389 | if (!newData.ParcelAccessList.Contains(temp)) | ||
390 | { | ||
391 | newData.ParcelAccessList.Add(temp); | ||
392 | } | ||
393 | } | ||
394 | |||
395 | m_scene.LandChannel.UpdateLandObject(landData.LocalID, newData); | ||
396 | } | ||
397 | |||
398 | #endregion | ||
399 | |||
400 | #region Update Functions | ||
401 | |||
402 | public void updateLandBitmapByteArray() | ||
403 | { | ||
404 | landData.Bitmap = convertLandBitmapToBytes(); | ||
405 | } | ||
406 | |||
407 | /// <summary> | ||
408 | /// Update all settings in land such as area, bitmap byte array, etc | ||
409 | /// </summary> | ||
410 | public void forceUpdateLandInfo() | ||
411 | { | ||
412 | updateAABBAndAreaValues(); | ||
413 | updateLandBitmapByteArray(); | ||
414 | } | ||
415 | |||
416 | public void setLandBitmapFromByteArray() | ||
417 | { | ||
418 | landBitmap = convertBytesToLandBitmap(); | ||
419 | } | ||
420 | |||
421 | /// <summary> | ||
422 | /// Updates the AABBMin and AABBMax values after area/shape modification of the land object | ||
423 | /// </summary> | ||
424 | private void updateAABBAndAreaValues() | ||
425 | { | ||
426 | int min_x = 64; | ||
427 | int min_y = 64; | ||
428 | int max_x = 0; | ||
429 | int max_y = 0; | ||
430 | int tempArea = 0; | ||
431 | int x, y; | ||
432 | for (x = 0; x < 64; x++) | ||
433 | { | ||
434 | for (y = 0; y < 64; y++) | ||
435 | { | ||
436 | if (landBitmap[x, y] == true) | ||
437 | { | ||
438 | if (min_x > x) min_x = x; | ||
439 | if (min_y > y) min_y = y; | ||
440 | if (max_x < x) max_x = x; | ||
441 | if (max_y < y) max_y = y; | ||
442 | tempArea += 16; //16sqm peice of land | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | int tx = min_x * 4; | ||
447 | if (tx > 255) | ||
448 | tx = 255; | ||
449 | int ty = min_y * 4; | ||
450 | if (ty > 255) | ||
451 | ty = 255; | ||
452 | landData.AABBMin = | ||
453 | new Vector3((float) (min_x * 4), (float) (min_y * 4), | ||
454 | (float) m_scene.Heightmap[tx, ty]); | ||
455 | |||
456 | tx = max_x * 4; | ||
457 | if (tx > 255) | ||
458 | tx = 255; | ||
459 | ty = max_y * 4; | ||
460 | if (ty > 255) | ||
461 | ty = 255; | ||
462 | landData.AABBMax = | ||
463 | new Vector3((float) (max_x * 4), (float) (max_y * 4), | ||
464 | (float) m_scene.Heightmap[tx, ty]); | ||
465 | landData.Area = tempArea; | ||
466 | } | ||
467 | |||
468 | #endregion | ||
469 | |||
470 | #region Land Bitmap Functions | ||
471 | |||
472 | /// <summary> | ||
473 | /// Sets the land's bitmap manually | ||
474 | /// </summary> | ||
475 | /// <param name="bitmap">64x64 block representing where this land is on a map</param> | ||
476 | public void setLandBitmap(bool[,] bitmap) | ||
477 | { | ||
478 | if (bitmap.GetLength(0) != 64 || bitmap.GetLength(1) != 64 || bitmap.Rank != 2) | ||
479 | { | ||
480 | //Throw an exception - The bitmap is not 64x64 | ||
481 | //throw new Exception("Error: Invalid Parcel Bitmap"); | ||
482 | } | ||
483 | else | ||
484 | { | ||
485 | //Valid: Lets set it | ||
486 | landBitmap = bitmap; | ||
487 | forceUpdateLandInfo(); | ||
488 | } | ||
489 | } | ||
490 | |||
491 | /// <summary> | ||
492 | /// Gets the land's bitmap manually | ||
493 | /// </summary> | ||
494 | /// <returns></returns> | ||
495 | public bool[,] getLandBitmap() | ||
496 | { | ||
497 | return landBitmap; | ||
498 | } | ||
499 | |||
500 | /// <summary> | ||
501 | /// Full sim land object creation | ||
502 | /// </summary> | ||
503 | /// <returns></returns> | ||
504 | public bool[,] basicFullRegionLandBitmap() | ||
505 | { | ||
506 | return getSquareLandBitmap(0, 0, (int) Constants.RegionSize, (int) Constants.RegionSize); | ||
507 | } | ||
508 | |||
509 | /// <summary> | ||
510 | /// Used to modify the bitmap between the x and y points. Points use 64 scale | ||
511 | /// </summary> | ||
512 | /// <param name="start_x"></param> | ||
513 | /// <param name="start_y"></param> | ||
514 | /// <param name="end_x"></param> | ||
515 | /// <param name="end_y"></param> | ||
516 | /// <returns></returns> | ||
517 | public bool[,] getSquareLandBitmap(int start_x, int start_y, int end_x, int end_y) | ||
518 | { | ||
519 | bool[,] tempBitmap = new bool[64,64]; | ||
520 | tempBitmap.Initialize(); | ||
521 | |||
522 | tempBitmap = modifyLandBitmapSquare(tempBitmap, start_x, start_y, end_x, end_y, true); | ||
523 | return tempBitmap; | ||
524 | } | ||
525 | |||
526 | /// <summary> | ||
527 | /// Change a land bitmap at within a square and set those points to a specific value | ||
528 | /// </summary> | ||
529 | /// <param name="land_bitmap"></param> | ||
530 | /// <param name="start_x"></param> | ||
531 | /// <param name="start_y"></param> | ||
532 | /// <param name="end_x"></param> | ||
533 | /// <param name="end_y"></param> | ||
534 | /// <param name="set_value"></param> | ||
535 | /// <returns></returns> | ||
536 | public bool[,] modifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, | ||
537 | bool set_value) | ||
538 | { | ||
539 | if (land_bitmap.GetLength(0) != 64 || land_bitmap.GetLength(1) != 64 || land_bitmap.Rank != 2) | ||
540 | { | ||
541 | //Throw an exception - The bitmap is not 64x64 | ||
542 | //throw new Exception("Error: Invalid Parcel Bitmap in modifyLandBitmapSquare()"); | ||
543 | } | ||
544 | |||
545 | int x, y; | ||
546 | for (y = 0; y < 64; y++) | ||
547 | { | ||
548 | for (x = 0; x < 64; x++) | ||
549 | { | ||
550 | if (x >= start_x / 4 && x < end_x / 4 | ||
551 | && y >= start_y / 4 && y < end_y / 4) | ||
552 | { | ||
553 | land_bitmap[x, y] = set_value; | ||
554 | } | ||
555 | } | ||
556 | } | ||
557 | return land_bitmap; | ||
558 | } | ||
559 | |||
560 | /// <summary> | ||
561 | /// Join the true values of 2 bitmaps together | ||
562 | /// </summary> | ||
563 | /// <param name="bitmap_base"></param> | ||
564 | /// <param name="bitmap_add"></param> | ||
565 | /// <returns></returns> | ||
566 | public bool[,] mergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add) | ||
567 | { | ||
568 | if (bitmap_base.GetLength(0) != 64 || bitmap_base.GetLength(1) != 64 || bitmap_base.Rank != 2) | ||
569 | { | ||
570 | //Throw an exception - The bitmap is not 64x64 | ||
571 | throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_base in mergeLandBitmaps"); | ||
572 | } | ||
573 | if (bitmap_add.GetLength(0) != 64 || bitmap_add.GetLength(1) != 64 || bitmap_add.Rank != 2) | ||
574 | { | ||
575 | //Throw an exception - The bitmap is not 64x64 | ||
576 | throw new Exception("Error: Invalid Parcel Bitmap - Bitmap_add in mergeLandBitmaps"); | ||
577 | } | ||
578 | |||
579 | int x, y; | ||
580 | for (y = 0; y < 64; y++) | ||
581 | { | ||
582 | for (x = 0; x < 64; x++) | ||
583 | { | ||
584 | if (bitmap_add[x, y]) | ||
585 | { | ||
586 | bitmap_base[x, y] = true; | ||
587 | } | ||
588 | } | ||
589 | } | ||
590 | return bitmap_base; | ||
591 | } | ||
592 | |||
593 | /// <summary> | ||
594 | /// Converts the land bitmap to a packet friendly byte array | ||
595 | /// </summary> | ||
596 | /// <returns></returns> | ||
597 | private byte[] convertLandBitmapToBytes() | ||
598 | { | ||
599 | byte[] tempConvertArr = new byte[512]; | ||
600 | byte tempByte = 0; | ||
601 | int x, y, i, byteNum = 0; | ||
602 | i = 0; | ||
603 | for (y = 0; y < 64; y++) | ||
604 | { | ||
605 | for (x = 0; x < 64; x++) | ||
606 | { | ||
607 | tempByte = Convert.ToByte(tempByte | Convert.ToByte(landBitmap[x, y]) << (i++ % 8)); | ||
608 | if (i % 8 == 0) | ||
609 | { | ||
610 | tempConvertArr[byteNum] = tempByte; | ||
611 | tempByte = (byte) 0; | ||
612 | i = 0; | ||
613 | byteNum++; | ||
614 | } | ||
615 | } | ||
616 | } | ||
617 | return tempConvertArr; | ||
618 | } | ||
619 | |||
620 | private bool[,] convertBytesToLandBitmap() | ||
621 | { | ||
622 | bool[,] tempConvertMap = new bool[64,64]; | ||
623 | tempConvertMap.Initialize(); | ||
624 | byte tempByte = 0; | ||
625 | int x = 0, y = 0, i = 0, bitNum = 0; | ||
626 | for (i = 0; i < 512; i++) | ||
627 | { | ||
628 | tempByte = landData.Bitmap[i]; | ||
629 | for (bitNum = 0; bitNum < 8; bitNum++) | ||
630 | { | ||
631 | bool bit = Convert.ToBoolean(Convert.ToByte(tempByte >> bitNum) & (byte) 1); | ||
632 | tempConvertMap[x, y] = bit; | ||
633 | x++; | ||
634 | if (x > 63) | ||
635 | { | ||
636 | x = 0; | ||
637 | y++; | ||
638 | } | ||
639 | } | ||
640 | } | ||
641 | return tempConvertMap; | ||
642 | } | ||
643 | |||
644 | #endregion | ||
645 | |||
646 | #region Object Select and Object Owner Listing | ||
647 | |||
648 | public void sendForceObjectSelect(int local_id, int request_type, List<UUID> returnIDs, IClientAPI remote_client) | ||
649 | { | ||
650 | if (m_scene.Permissions.CanEditParcel(remote_client.AgentId, this)) | ||
651 | { | ||
652 | List<uint> resultLocalIDs = new List<uint>(); | ||
653 | try | ||
654 | { | ||
655 | lock (primsOverMe) | ||
656 | { | ||
657 | foreach (SceneObjectGroup obj in primsOverMe) | ||
658 | { | ||
659 | if (obj.LocalId > 0) | ||
660 | { | ||
661 | if (request_type == LandChannel.LAND_SELECT_OBJECTS_OWNER && obj.OwnerID == landData.OwnerID) | ||
662 | { | ||
663 | resultLocalIDs.Add(obj.LocalId); | ||
664 | } | ||
665 | else if (request_type == LandChannel.LAND_SELECT_OBJECTS_GROUP && obj.GroupID == landData.GroupID && landData.GroupID != UUID.Zero) | ||
666 | { | ||
667 | resultLocalIDs.Add(obj.LocalId); | ||
668 | } | ||
669 | else if (request_type == LandChannel.LAND_SELECT_OBJECTS_OTHER && | ||
670 | obj.OwnerID != remote_client.AgentId) | ||
671 | { | ||
672 | resultLocalIDs.Add(obj.LocalId); | ||
673 | } | ||
674 | else if (request_type == (int)ObjectReturnType.List && returnIDs.Contains(obj.OwnerID)) | ||
675 | { | ||
676 | resultLocalIDs.Add(obj.LocalId); | ||
677 | } | ||
678 | } | ||
679 | } | ||
680 | } | ||
681 | } catch (InvalidOperationException) | ||
682 | { | ||
683 | m_log.Error("[LAND]: Unable to force select the parcel objects. Arr."); | ||
684 | } | ||
685 | |||
686 | remote_client.SendForceClientSelectObjects(resultLocalIDs); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | /// <summary> | ||
691 | /// Notify the parcel owner each avatar that owns prims situated on their land. This notification includes | ||
692 | /// aggreagete details such as the number of prims. | ||
693 | /// | ||
694 | /// </summary> | ||
695 | /// <param name="remote_client"> | ||
696 | /// A <see cref="IClientAPI"/> | ||
697 | /// </param> | ||
698 | public void sendLandObjectOwners(IClientAPI remote_client) | ||
699 | { | ||
700 | if (m_scene.Permissions.CanEditParcel(remote_client.AgentId, this)) | ||
701 | { | ||
702 | Dictionary<UUID, int> primCount = new Dictionary<UUID, int>(); | ||
703 | |||
704 | lock (primsOverMe) | ||
705 | { | ||
706 | try | ||
707 | { | ||
708 | |||
709 | foreach (SceneObjectGroup obj in primsOverMe) | ||
710 | { | ||
711 | try | ||
712 | { | ||
713 | if (!primCount.ContainsKey(obj.OwnerID)) | ||
714 | { | ||
715 | primCount.Add(obj.OwnerID, 0); | ||
716 | } | ||
717 | } | ||
718 | catch (NullReferenceException) | ||
719 | { | ||
720 | m_log.Info("[LAND]: " + "Got Null Reference when searching land owners from the parcel panel"); | ||
721 | } | ||
722 | try | ||
723 | { | ||
724 | primCount[obj.OwnerID] += obj.PrimCount; | ||
725 | } | ||
726 | catch (KeyNotFoundException) | ||
727 | { | ||
728 | m_log.Error("[LAND]: Unable to match a prim with it's owner."); | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | catch (InvalidOperationException) | ||
733 | { | ||
734 | m_log.Error("[LAND]: Unable to Enumerate Land object arr."); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | remote_client.SendLandObjectOwners(primCount); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | public Dictionary<UUID, int> getLandObjectOwners() | ||
743 | { | ||
744 | Dictionary<UUID, int> ownersAndCount = new Dictionary<UUID, int>(); | ||
745 | lock (primsOverMe) | ||
746 | { | ||
747 | try | ||
748 | { | ||
749 | |||
750 | foreach (SceneObjectGroup obj in primsOverMe) | ||
751 | { | ||
752 | if (!ownersAndCount.ContainsKey(obj.OwnerID)) | ||
753 | { | ||
754 | ownersAndCount.Add(obj.OwnerID, 0); | ||
755 | } | ||
756 | ownersAndCount[obj.OwnerID] += obj.PrimCount; | ||
757 | } | ||
758 | } | ||
759 | catch (InvalidOperationException) | ||
760 | { | ||
761 | m_log.Error("[LAND]: Unable to enumerate land owners. arr."); | ||
762 | } | ||
763 | |||
764 | } | ||
765 | return ownersAndCount; | ||
766 | } | ||
767 | |||
768 | #endregion | ||
769 | |||
770 | #region Object Returning | ||
771 | |||
772 | public void returnObject(SceneObjectGroup obj) | ||
773 | { | ||
774 | SceneObjectGroup[] objs = new SceneObjectGroup[1]; | ||
775 | objs[0] = obj; | ||
776 | m_scene.returnObjects(objs, obj.OwnerID); | ||
777 | } | ||
778 | |||
779 | public void returnLandObjects(uint type, UUID[] owners, UUID[] tasks, IClientAPI remote_client) | ||
780 | { | ||
781 | Dictionary<UUID,List<SceneObjectGroup>> returns = | ||
782 | new Dictionary<UUID,List<SceneObjectGroup>>(); | ||
783 | |||
784 | lock (primsOverMe) | ||
785 | { | ||
786 | if (type == (uint)ObjectReturnType.Owner) | ||
787 | { | ||
788 | foreach (SceneObjectGroup obj in primsOverMe) | ||
789 | { | ||
790 | if (obj.OwnerID == m_landData.OwnerID) | ||
791 | { | ||
792 | if (!returns.ContainsKey(obj.OwnerID)) | ||
793 | returns[obj.OwnerID] = | ||
794 | new List<SceneObjectGroup>(); | ||
795 | returns[obj.OwnerID].Add(obj); | ||
796 | } | ||
797 | } | ||
798 | } | ||
799 | else if (type == (uint)ObjectReturnType.Group && m_landData.GroupID != UUID.Zero) | ||
800 | { | ||
801 | foreach (SceneObjectGroup obj in primsOverMe) | ||
802 | { | ||
803 | if (obj.GroupID == m_landData.GroupID) | ||
804 | { | ||
805 | if (!returns.ContainsKey(obj.OwnerID)) | ||
806 | returns[obj.OwnerID] = | ||
807 | new List<SceneObjectGroup>(); | ||
808 | returns[obj.OwnerID].Add(obj); | ||
809 | } | ||
810 | } | ||
811 | } | ||
812 | else if (type == (uint)ObjectReturnType.Other) | ||
813 | { | ||
814 | foreach (SceneObjectGroup obj in primsOverMe) | ||
815 | { | ||
816 | if (obj.OwnerID != m_landData.OwnerID && | ||
817 | (obj.GroupID != m_landData.GroupID || | ||
818 | m_landData.GroupID == UUID.Zero)) | ||
819 | { | ||
820 | if (!returns.ContainsKey(obj.OwnerID)) | ||
821 | returns[obj.OwnerID] = | ||
822 | new List<SceneObjectGroup>(); | ||
823 | returns[obj.OwnerID].Add(obj); | ||
824 | } | ||
825 | } | ||
826 | } | ||
827 | else if (type == (uint)ObjectReturnType.List) | ||
828 | { | ||
829 | List<UUID> ownerlist = new List<UUID>(owners); | ||
830 | |||
831 | foreach (SceneObjectGroup obj in primsOverMe) | ||
832 | { | ||
833 | if (ownerlist.Contains(obj.OwnerID)) | ||
834 | { | ||
835 | if (!returns.ContainsKey(obj.OwnerID)) | ||
836 | returns[obj.OwnerID] = | ||
837 | new List<SceneObjectGroup>(); | ||
838 | returns[obj.OwnerID].Add(obj); | ||
839 | } | ||
840 | } | ||
841 | } | ||
842 | } | ||
843 | |||
844 | foreach (List<SceneObjectGroup> ol in returns.Values) | ||
845 | m_scene.returnObjects(ol.ToArray(), remote_client.AgentId); | ||
846 | } | ||
847 | |||
848 | #endregion | ||
849 | |||
850 | #region Object Adding/Removing from Parcel | ||
851 | |||
852 | public void resetLandPrimCounts() | ||
853 | { | ||
854 | landData.GroupPrims = 0; | ||
855 | landData.OwnerPrims = 0; | ||
856 | landData.OtherPrims = 0; | ||
857 | landData.SelectedPrims = 0; | ||
858 | |||
859 | |||
860 | lock (primsOverMe) | ||
861 | primsOverMe.Clear(); | ||
862 | } | ||
863 | |||
864 | public void addPrimToCount(SceneObjectGroup obj) | ||
865 | { | ||
866 | |||
867 | UUID prim_owner = obj.OwnerID; | ||
868 | int prim_count = obj.PrimCount; | ||
869 | |||
870 | if (obj.IsSelected) | ||
871 | { | ||
872 | landData.SelectedPrims += prim_count; | ||
873 | } | ||
874 | else | ||
875 | { | ||
876 | if (prim_owner == landData.OwnerID) | ||
877 | { | ||
878 | landData.OwnerPrims += prim_count; | ||
879 | } | ||
880 | else if ((obj.GroupID == landData.GroupID || | ||
881 | prim_owner == landData.GroupID) && | ||
882 | landData.GroupID != UUID.Zero) | ||
883 | { | ||
884 | landData.GroupPrims += prim_count; | ||
885 | } | ||
886 | else | ||
887 | { | ||
888 | landData.OtherPrims += prim_count; | ||
889 | } | ||
890 | } | ||
891 | |||
892 | lock (primsOverMe) | ||
893 | primsOverMe.Add(obj); | ||
894 | } | ||
895 | |||
896 | public void removePrimFromCount(SceneObjectGroup obj) | ||
897 | { | ||
898 | lock (primsOverMe) | ||
899 | { | ||
900 | if (primsOverMe.Contains(obj)) | ||
901 | { | ||
902 | UUID prim_owner = obj.OwnerID; | ||
903 | int prim_count = obj.PrimCount; | ||
904 | |||
905 | if (prim_owner == landData.OwnerID) | ||
906 | { | ||
907 | landData.OwnerPrims -= prim_count; | ||
908 | } | ||
909 | else if (obj.GroupID == landData.GroupID || | ||
910 | prim_owner == landData.GroupID) | ||
911 | { | ||
912 | landData.GroupPrims -= prim_count; | ||
913 | } | ||
914 | else | ||
915 | { | ||
916 | landData.OtherPrims -= prim_count; | ||
917 | } | ||
918 | |||
919 | primsOverMe.Remove(obj); | ||
920 | } | ||
921 | } | ||
922 | } | ||
923 | |||
924 | #endregion | ||
925 | |||
926 | #endregion | ||
927 | |||
928 | #endregion | ||
929 | } | ||
930 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs new file mode 100644 index 0000000..1469f5d --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs | |||
@@ -0,0 +1,1498 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenMetaverse; | ||
29 | using Nini.Config; | ||
30 | using System; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Reflection; | ||
34 | using log4net; | ||
35 | using OpenSim; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | using OpenSim.Region.Framework.Scenes; | ||
39 | using OpenSim.Region.CoreModules.Framework; | ||
40 | using OpenSim.Framework.Communications.Cache; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.World.Permissions | ||
43 | { | ||
44 | public class PermissionsModule : IRegionModule | ||
45 | { | ||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | protected Scene m_scene; | ||
49 | |||
50 | #region Constants | ||
51 | // These are here for testing. They will be taken out | ||
52 | |||
53 | //private uint PERM_ALL = (uint)2147483647; | ||
54 | private uint PERM_COPY = (uint)32768; | ||
55 | //private uint PERM_MODIFY = (uint)16384; | ||
56 | private uint PERM_MOVE = (uint)524288; | ||
57 | //private uint PERM_TRANS = (uint)8192; | ||
58 | private uint PERM_LOCKED = (uint)540672; | ||
59 | |||
60 | /// <value> | ||
61 | /// Different user set names that come in from the configuration file. | ||
62 | /// </value> | ||
63 | enum UserSet | ||
64 | { | ||
65 | All, | ||
66 | Administrators | ||
67 | }; | ||
68 | |||
69 | #endregion | ||
70 | |||
71 | #region Bypass Permissions / Debug Permissions Stuff | ||
72 | |||
73 | // Bypasses the permissions engine | ||
74 | private bool m_bypassPermissions = true; | ||
75 | private bool m_bypassPermissionsValue = true; | ||
76 | private bool m_propagatePermissions = false; | ||
77 | private bool m_debugPermissions = false; | ||
78 | private bool m_allowGridGods = false; | ||
79 | private bool m_RegionOwnerIsGod = false; | ||
80 | private bool m_ParcelOwnerIsGod = false; | ||
81 | |||
82 | /// <value> | ||
83 | /// The set of users that are allowed to create scripts. This is only active if permissions are not being | ||
84 | /// bypassed. This overrides normal permissions. | ||
85 | /// </value> | ||
86 | private UserSet m_allowedScriptCreators = UserSet.All; | ||
87 | |||
88 | /// <value> | ||
89 | /// The set of users that are allowed to edit (save) scripts. This is only active if | ||
90 | /// permissions are not being bypassed. This overrides normal permissions.- | ||
91 | /// </value> | ||
92 | private UserSet m_allowedScriptEditors = UserSet.All; | ||
93 | |||
94 | #endregion | ||
95 | |||
96 | #region IRegionModule Members | ||
97 | |||
98 | public void Initialise(Scene scene, IConfigSource config) | ||
99 | { | ||
100 | m_scene = scene; | ||
101 | |||
102 | IConfig myConfig = config.Configs["Startup"]; | ||
103 | |||
104 | string permissionModules = myConfig.GetString("permissionmodules", "DefaultPermissionsModule"); | ||
105 | |||
106 | List<string> modules=new List<string>(permissionModules.Split(',')); | ||
107 | |||
108 | if (!modules.Contains("DefaultPermissionsModule")) | ||
109 | return; | ||
110 | |||
111 | m_allowGridGods = myConfig.GetBoolean("allow_grid_gods", false); | ||
112 | m_bypassPermissions = !myConfig.GetBoolean("serverside_object_permissions", true); | ||
113 | m_propagatePermissions = myConfig.GetBoolean("propagate_permissions", true); | ||
114 | m_RegionOwnerIsGod = myConfig.GetBoolean("region_owner_is_god", true); | ||
115 | m_ParcelOwnerIsGod = myConfig.GetBoolean("parcel_owner_is_god", true); | ||
116 | |||
117 | m_allowedScriptCreators | ||
118 | = ParseUserSetConfigSetting(myConfig, "allowed_script_creators", m_allowedScriptCreators); | ||
119 | m_allowedScriptEditors | ||
120 | = ParseUserSetConfigSetting(myConfig, "allowed_script_editors", m_allowedScriptEditors); | ||
121 | |||
122 | if (m_bypassPermissions) | ||
123 | m_log.Info("[PERMISSIONS]: serviceside_object_permissions = false in ini file so disabling all region service permission checks"); | ||
124 | else | ||
125 | m_log.Debug("[PERMISSIONS]: Enabling all region service permission checks"); | ||
126 | |||
127 | //Register functions with Scene External Checks! | ||
128 | m_scene.Permissions.AddBypassPermissionsHandler(BypassPermissions); //FULLY IMPLEMENTED | ||
129 | m_scene.Permissions.AddSetBypassPermissionsHandler(SetBypassPermissions); //FULLY IMPLEMENTED | ||
130 | m_scene.Permissions.AddPropagatePermissionsHandler(PropagatePermissions); //FULLY IMPLEMENTED | ||
131 | m_scene.Permissions.AddGenerateClientFlagsHandler(GenerateClientFlags); //NOT YET FULLY IMPLEMENTED | ||
132 | m_scene.Permissions.AddAbandonParcelHandler(CanAbandonParcel); //FULLY IMPLEMENTED | ||
133 | m_scene.Permissions.AddReclaimParcelHandler(CanReclaimParcel); //FULLY IMPLEMENTED | ||
134 | m_scene.Permissions.AddIsGodHandler(IsGod); //FULLY IMPLEMENTED | ||
135 | m_scene.Permissions.AddDuplicateObjectHandler(CanDuplicateObject); //FULLY IMPLEMENTED | ||
136 | m_scene.Permissions.AddDeleteObjectHandler(CanDeleteObject); //MAYBE FULLY IMPLEMENTED | ||
137 | m_scene.Permissions.AddEditObjectHandler(CanEditObject);//MAYBE FULLY IMPLEMENTED | ||
138 | m_scene.Permissions.AddEditParcelHandler(CanEditParcel); //FULLY IMPLEMENTED | ||
139 | m_scene.Permissions.AddInstantMessageHandler(CanInstantMessage); //FULLY IMPLEMENTED | ||
140 | m_scene.Permissions.AddInventoryTransferHandler(CanInventoryTransfer); //NOT YET IMPLEMENTED | ||
141 | m_scene.Permissions.AddIssueEstateCommandHandler(CanIssueEstateCommand); //FULLY IMPLEMENTED | ||
142 | m_scene.Permissions.AddMoveObjectHandler(CanMoveObject); //HOPEFULLY FULLY IMPLEMENTED | ||
143 | m_scene.Permissions.AddObjectEntryHandler(CanObjectEntry); //FULLY IMPLEMENTED | ||
144 | m_scene.Permissions.AddReturnObjectHandler(CanReturnObject); //NOT YET IMPLEMENTED | ||
145 | m_scene.Permissions.AddRezObjectHandler(CanRezObject); //HOPEFULLY FULLY IMPLEMENTED | ||
146 | m_scene.Permissions.AddRunConsoleCommandHandler(CanRunConsoleCommand); //FULLY IMPLEMENTED | ||
147 | m_scene.Permissions.AddRunScriptHandler(CanRunScript); //NOT YET IMPLEMENTED | ||
148 | m_scene.Permissions.AddSellParcelHandler(CanSellParcel); //FULLY IMPLEMENTED | ||
149 | m_scene.Permissions.AddTakeObjectHandler(CanTakeObject); //FULLY IMPLEMENTED | ||
150 | m_scene.Permissions.AddTakeCopyObjectHandler(CanTakeCopyObject); //FULLY IMPLEMENTED | ||
151 | m_scene.Permissions.AddTerraformLandHandler(CanTerraformLand); //FULL IMPLEMENTED (POINT ONLY!!! NOT AREA!!!) | ||
152 | m_scene.Permissions.AddCanLinkObjectHandler(CanLinkObject); //NOT YET IMPLEMENTED | ||
153 | m_scene.Permissions.AddCanDelinkObjectHandler(CanDelinkObject); //NOT YET IMPLEMENTED | ||
154 | m_scene.Permissions.AddCanBuyLandHandler(CanBuyLand); //NOT YET IMPLEMENTED | ||
155 | |||
156 | m_scene.Permissions.AddViewNotecardHandler(CanViewNotecard); //NOT YET IMPLEMENTED | ||
157 | m_scene.Permissions.AddViewScriptHandler(CanViewScript); //NOT YET IMPLEMENTED | ||
158 | m_scene.Permissions.AddEditNotecardHandler(CanEditNotecard); //NOT YET IMPLEMENTED | ||
159 | m_scene.Permissions.AddEditScriptHandler(CanEditScript); //NOT YET IMPLEMENTED | ||
160 | |||
161 | m_scene.Permissions.AddCanCreateObjectInventoryHandler(CanCreateObjectInventory); //NOT IMPLEMENTED HERE | ||
162 | m_scene.Permissions.AddEditObjectInventoryHandler(CanEditObjectInventory);//MAYBE FULLY IMPLEMENTED | ||
163 | m_scene.Permissions.AddCanCopyObjectInventoryHandler(CanCopyObjectInventory); //NOT YET IMPLEMENTED | ||
164 | m_scene.Permissions.AddCanDeleteObjectInventoryHandler(CanDeleteObjectInventory); //NOT YET IMPLEMENTED | ||
165 | m_scene.Permissions.AddResetScriptHandler(CanResetScript); | ||
166 | |||
167 | m_scene.Permissions.AddCanCreateUserInventoryHandler(CanCreateUserInventory); //NOT YET IMPLEMENTED | ||
168 | m_scene.Permissions.AddCanCopyUserInventoryHandler(CanCopyUserInventory); //NOT YET IMPLEMENTED | ||
169 | m_scene.Permissions.AddCanEditUserInventoryHandler(CanEditUserInventory); //NOT YET IMPLEMENTED | ||
170 | m_scene.Permissions.AddCanDeleteUserInventoryHandler(CanDeleteUserInventory); //NOT YET IMPLEMENTED | ||
171 | |||
172 | m_scene.Permissions.AddCanTeleportHandler(CanTeleport); //NOT YET IMPLEMENTED | ||
173 | |||
174 | m_scene.AddCommand("permissions", "bypass permissions", | ||
175 | "bypass permissions <true / false>", | ||
176 | "Bypass permission checks", | ||
177 | HandleBypassPermissions); | ||
178 | |||
179 | m_scene.AddCommand("permissions", "force permissions", | ||
180 | "force permissions <true / false>", | ||
181 | "Force permissions on or off", | ||
182 | HandleForcePermissions); | ||
183 | |||
184 | m_scene.AddCommand("permissions", "debug permissions", | ||
185 | "debug permissions <true / false>", | ||
186 | "Enable permissions debugging", | ||
187 | HandleDebugPermissions); | ||
188 | } | ||
189 | |||
190 | public void HandleBypassPermissions(string module, string[] args) | ||
191 | { | ||
192 | if (m_scene.ConsoleScene() != null && | ||
193 | m_scene.ConsoleScene() != m_scene) | ||
194 | { | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | if (args.Length > 2) | ||
199 | { | ||
200 | bool val; | ||
201 | |||
202 | if (!bool.TryParse(args[2], out val)) | ||
203 | return; | ||
204 | |||
205 | m_bypassPermissions = val; | ||
206 | |||
207 | m_log.InfoFormat( | ||
208 | "[PERMISSIONS]: Set permissions bypass to {0} for {1}", | ||
209 | m_bypassPermissions, m_scene.RegionInfo.RegionName); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | public void HandleForcePermissions(string module, string[] args) | ||
214 | { | ||
215 | if (m_scene.ConsoleScene() != null && | ||
216 | m_scene.ConsoleScene() != m_scene) | ||
217 | { | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | if (!m_bypassPermissions) | ||
222 | { | ||
223 | m_log.Error("[PERMISSIONS] Permissions can't be forced unless they are bypassed first"); | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | if (args.Length > 2) | ||
228 | { | ||
229 | bool val; | ||
230 | |||
231 | if (!bool.TryParse(args[2], out val)) | ||
232 | return; | ||
233 | |||
234 | m_bypassPermissionsValue = val; | ||
235 | |||
236 | m_log.InfoFormat("[PERMISSIONS] Forced permissions to {0} in {1}", m_bypassPermissionsValue, m_scene.RegionInfo.RegionName); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | public void HandleDebugPermissions(string module, string[] args) | ||
241 | { | ||
242 | if (m_scene.ConsoleScene() != null && | ||
243 | m_scene.ConsoleScene() != m_scene) | ||
244 | { | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | if (args.Length > 2) | ||
249 | { | ||
250 | bool val; | ||
251 | |||
252 | if (!bool.TryParse(args[2], out val)) | ||
253 | return; | ||
254 | |||
255 | m_debugPermissions = val; | ||
256 | |||
257 | m_log.InfoFormat("[PERMISSIONS] Set permissions debugging to {0} in {1}", m_debugPermissions, m_scene.RegionInfo.RegionName); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | public void PostInitialise() | ||
262 | { | ||
263 | } | ||
264 | |||
265 | public void Close() | ||
266 | { | ||
267 | } | ||
268 | |||
269 | public string Name | ||
270 | { | ||
271 | get { return "PermissionsModule"; } | ||
272 | } | ||
273 | |||
274 | public bool IsSharedModule | ||
275 | { | ||
276 | get { return false; } | ||
277 | } | ||
278 | |||
279 | #endregion | ||
280 | |||
281 | #region Helper Functions | ||
282 | protected void SendPermissionError(UUID user, string reason) | ||
283 | { | ||
284 | m_scene.EventManager.TriggerPermissionError(user, reason); | ||
285 | } | ||
286 | |||
287 | protected void DebugPermissionInformation(string permissionCalled) | ||
288 | { | ||
289 | if (m_debugPermissions) | ||
290 | m_log.Debug("[PERMISSIONS]: " + permissionCalled + " was called from " + m_scene.RegionInfo.RegionName); | ||
291 | } | ||
292 | |||
293 | /// <summary> | ||
294 | /// Parse a user set configuration setting | ||
295 | /// </summary> | ||
296 | /// <param name="config"></param> | ||
297 | /// <param name="settingName"></param> | ||
298 | /// <param name="defaultValue">The default value for this attribute</param> | ||
299 | /// <returns>The parsed value</returns> | ||
300 | private static UserSet ParseUserSetConfigSetting(IConfig config, string settingName, UserSet defaultValue) | ||
301 | { | ||
302 | UserSet userSet = defaultValue; | ||
303 | |||
304 | string rawSetting = config.GetString(settingName, defaultValue.ToString()); | ||
305 | |||
306 | // Temporary measure to allow 'gods' to be specified in config for consistency's sake. In the long term | ||
307 | // this should disappear. | ||
308 | if ("gods" == rawSetting.ToLower()) | ||
309 | rawSetting = UserSet.Administrators.ToString(); | ||
310 | |||
311 | // Doing it this was so that we can do a case insensitive conversion | ||
312 | try | ||
313 | { | ||
314 | userSet = (UserSet)Enum.Parse(typeof(UserSet), rawSetting, true); | ||
315 | } | ||
316 | catch | ||
317 | { | ||
318 | m_log.ErrorFormat( | ||
319 | "[PERMISSIONS]: {0} is not a valid {1} value, setting to {2}", | ||
320 | rawSetting, settingName, userSet); | ||
321 | } | ||
322 | |||
323 | m_log.DebugFormat("[PERMISSIONS]: {0} {1}", settingName, userSet); | ||
324 | |||
325 | return userSet; | ||
326 | } | ||
327 | |||
328 | /// <summary> | ||
329 | /// Is the given user an administrator (in other words, a god)? | ||
330 | /// </summary> | ||
331 | /// <param name="user"></param> | ||
332 | /// <returns></returns> | ||
333 | protected bool IsAdministrator(UUID user) | ||
334 | { | ||
335 | if (m_scene.RegionInfo.MasterAvatarAssignedUUID != UUID.Zero) | ||
336 | { | ||
337 | if (m_RegionOwnerIsGod && (m_scene.RegionInfo.MasterAvatarAssignedUUID == user)) | ||
338 | return true; | ||
339 | } | ||
340 | |||
341 | if (m_scene.RegionInfo.EstateSettings.EstateOwner != UUID.Zero) | ||
342 | { | ||
343 | if (m_scene.RegionInfo.EstateSettings.EstateOwner == user) | ||
344 | return true; | ||
345 | } | ||
346 | |||
347 | if (m_allowGridGods) | ||
348 | { | ||
349 | CachedUserInfo profile = m_scene.CommsManager.UserProfileCacheService.GetUserDetails(user); | ||
350 | if (profile != null && profile.UserProfile != null) | ||
351 | { | ||
352 | if (profile.UserProfile.GodLevel >= 200) | ||
353 | return true; | ||
354 | } | ||
355 | else | ||
356 | { | ||
357 | m_log.ErrorFormat("[PERMISSIONS]: Could not find user {0} for administrator check", user); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | return false; | ||
362 | } | ||
363 | |||
364 | protected bool IsEstateManager(UUID user) | ||
365 | { | ||
366 | return m_scene.RegionInfo.EstateSettings.IsEstateManager(user); | ||
367 | } | ||
368 | #endregion | ||
369 | |||
370 | public bool PropagatePermissions() | ||
371 | { | ||
372 | if (m_bypassPermissions) | ||
373 | return false; | ||
374 | |||
375 | return m_propagatePermissions; | ||
376 | } | ||
377 | |||
378 | public bool BypassPermissions() | ||
379 | { | ||
380 | return m_bypassPermissions; | ||
381 | } | ||
382 | |||
383 | public void SetBypassPermissions(bool value) | ||
384 | { | ||
385 | m_bypassPermissions=value; | ||
386 | } | ||
387 | |||
388 | #region Object Permissions | ||
389 | |||
390 | public uint GenerateClientFlags(UUID user, UUID objID) | ||
391 | { | ||
392 | // Here's the way this works, | ||
393 | // ObjectFlags and Permission flags are two different enumerations | ||
394 | // ObjectFlags, however, tells the client to change what it will allow the user to do. | ||
395 | // So, that means that all of the permissions type ObjectFlags are /temporary/ and only | ||
396 | // supposed to be set when customizing the objectflags for the client. | ||
397 | |||
398 | // These temporary objectflags get computed and added in this function based on the | ||
399 | // Permission mask that's appropriate! | ||
400 | // Outside of this method, they should never be added to objectflags! | ||
401 | // -teravus | ||
402 | |||
403 | SceneObjectPart task = m_scene.GetSceneObjectPart(objID); | ||
404 | |||
405 | // this shouldn't ever happen.. return no permissions/objectflags. | ||
406 | if (task == null) | ||
407 | return (uint)0; | ||
408 | |||
409 | uint objflags = task.GetEffectiveObjectFlags(); | ||
410 | UUID objectOwner = task.OwnerID; | ||
411 | |||
412 | |||
413 | // Remove any of the objectFlags that are temporary. These will get added back if appropriate | ||
414 | // in the next bit of code | ||
415 | |||
416 | // libomv will moan about PrimFlags.ObjectYouOfficer being | ||
417 | // deprecated | ||
418 | #pragma warning disable 0612 | ||
419 | objflags &= (uint) | ||
420 | ~(PrimFlags.ObjectCopy | // Tells client you can copy the object | ||
421 | PrimFlags.ObjectModify | // tells client you can modify the object | ||
422 | PrimFlags.ObjectMove | // tells client that you can move the object (only, no mod) | ||
423 | PrimFlags.ObjectTransfer | // tells the client that you can /take/ the object if you don't own it | ||
424 | PrimFlags.ObjectYouOwner | // Tells client that you're the owner of the object | ||
425 | PrimFlags.ObjectAnyOwner | // Tells client that someone owns the object | ||
426 | PrimFlags.ObjectOwnerModify | // Tells client that you're the owner of the object | ||
427 | PrimFlags.ObjectYouOfficer // Tells client that you've got group object editing permission. Used when ObjectGroupOwned is set | ||
428 | ); | ||
429 | #pragma warning restore 0612 | ||
430 | |||
431 | // Creating the three ObjectFlags options for this method to choose from. | ||
432 | // Customize the OwnerMask | ||
433 | uint objectOwnerMask = ApplyObjectModifyMasks(task.OwnerMask, objflags); | ||
434 | objectOwnerMask |= (uint)PrimFlags.ObjectYouOwner | (uint)PrimFlags.ObjectAnyOwner | (uint)PrimFlags.ObjectOwnerModify; | ||
435 | |||
436 | // Customize the GroupMask | ||
437 | // uint objectGroupMask = ApplyObjectModifyMasks(task.GroupMask, objflags); | ||
438 | |||
439 | // Customize the EveryoneMask | ||
440 | uint objectEveryoneMask = ApplyObjectModifyMasks(task.EveryoneMask, objflags); | ||
441 | |||
442 | |||
443 | // Hack to allow collaboration until Groups and Group Permissions are implemented | ||
444 | if ((objectEveryoneMask & (uint)PrimFlags.ObjectMove) != 0) | ||
445 | objectEveryoneMask |= (uint)PrimFlags.ObjectModify; | ||
446 | |||
447 | if (m_bypassPermissions) | ||
448 | return objectOwnerMask; | ||
449 | |||
450 | // Object owners should be able to edit their own content | ||
451 | if (user == objectOwner) | ||
452 | { | ||
453 | return objectOwnerMask; | ||
454 | } | ||
455 | |||
456 | // Users should be able to edit what is over their land. | ||
457 | ILandObject parcel = m_scene.LandChannel.GetLandObject(task.AbsolutePosition.X, task.AbsolutePosition.Y); | ||
458 | if (parcel != null && parcel.landData.OwnerID == user && m_ParcelOwnerIsGod) | ||
459 | return objectOwnerMask; | ||
460 | |||
461 | // Admin objects should not be editable by the above | ||
462 | if (IsAdministrator(objectOwner)) | ||
463 | return objectEveryoneMask; | ||
464 | |||
465 | // Estate users should be able to edit anything in the sim | ||
466 | if (IsEstateManager(user) && m_RegionOwnerIsGod) | ||
467 | return objectOwnerMask; | ||
468 | |||
469 | // Admin should be able to edit anything in the sim (including admin objects) | ||
470 | if (IsAdministrator(user)) | ||
471 | return objectOwnerMask; | ||
472 | |||
473 | |||
474 | return objectEveryoneMask; | ||
475 | } | ||
476 | |||
477 | private uint ApplyObjectModifyMasks(uint setPermissionMask, uint objectFlagsMask) | ||
478 | { | ||
479 | // We are adding the temporary objectflags to the object's objectflags based on the | ||
480 | // permission flag given. These change the F flags on the client. | ||
481 | |||
482 | if ((setPermissionMask & (uint)PermissionMask.Copy) != 0) | ||
483 | { | ||
484 | objectFlagsMask |= (uint)PrimFlags.ObjectCopy; | ||
485 | } | ||
486 | |||
487 | if ((setPermissionMask & (uint)PermissionMask.Move) != 0) | ||
488 | { | ||
489 | objectFlagsMask |= (uint)PrimFlags.ObjectMove; | ||
490 | } | ||
491 | |||
492 | if ((setPermissionMask & (uint)PermissionMask.Modify) != 0) | ||
493 | { | ||
494 | objectFlagsMask |= (uint)PrimFlags.ObjectModify; | ||
495 | } | ||
496 | |||
497 | if ((setPermissionMask & (uint)PermissionMask.Transfer) != 0) | ||
498 | { | ||
499 | objectFlagsMask |= (uint)PrimFlags.ObjectTransfer; | ||
500 | } | ||
501 | |||
502 | return objectFlagsMask; | ||
503 | } | ||
504 | |||
505 | /// <summary> | ||
506 | /// General permissions checks for any operation involving an object. These supplement more specific checks | ||
507 | /// implemented by callers. | ||
508 | /// </summary> | ||
509 | /// <param name="currentUser"></param> | ||
510 | /// <param name="objId"></param> | ||
511 | /// <param name="denyOnLocked"></param> | ||
512 | /// <returns></returns> | ||
513 | protected bool GenericObjectPermission(UUID currentUser, UUID objId, bool denyOnLocked) | ||
514 | { | ||
515 | // Default: deny | ||
516 | bool permission = false; | ||
517 | bool locked = false; | ||
518 | |||
519 | if (!m_scene.Entities.ContainsKey(objId)) | ||
520 | { | ||
521 | return false; | ||
522 | } | ||
523 | |||
524 | // If it's not an object, we cant edit it. | ||
525 | if ((!(m_scene.Entities[objId] is SceneObjectGroup))) | ||
526 | { | ||
527 | return false; | ||
528 | } | ||
529 | |||
530 | SceneObjectGroup group = (SceneObjectGroup)m_scene.Entities[objId]; | ||
531 | |||
532 | UUID objectOwner = group.OwnerID; | ||
533 | locked = ((group.RootPart.OwnerMask & PERM_LOCKED) == 0); | ||
534 | |||
535 | // People shouldn't be able to do anything with locked objects, except the Administrator | ||
536 | // The 'set permissions' runs through a different permission check, so when an object owner | ||
537 | // sets an object locked, the only thing that they can do is unlock it. | ||
538 | // | ||
539 | // Nobody but the object owner can set permissions on an object | ||
540 | // | ||
541 | |||
542 | if (locked && (!IsAdministrator(currentUser)) && denyOnLocked) | ||
543 | { | ||
544 | return false; | ||
545 | } | ||
546 | |||
547 | // Object owners should be able to edit their own content | ||
548 | if (currentUser == objectOwner) | ||
549 | { | ||
550 | permission = true; | ||
551 | } | ||
552 | else if (group.IsAttachment) | ||
553 | { | ||
554 | permission = false; | ||
555 | } | ||
556 | |||
557 | // Users should be able to edit what is over their land. | ||
558 | ILandObject parcel = m_scene.LandChannel.GetLandObject(group.AbsolutePosition.X, group.AbsolutePosition.Y); | ||
559 | if ((parcel != null) && (parcel.landData.OwnerID == currentUser)) | ||
560 | { | ||
561 | permission = true; | ||
562 | } | ||
563 | |||
564 | // Estate users should be able to edit anything in the sim | ||
565 | if (IsEstateManager(currentUser)) | ||
566 | { | ||
567 | permission = true; | ||
568 | } | ||
569 | |||
570 | // Admin objects should not be editable by the above | ||
571 | if (IsAdministrator(objectOwner)) | ||
572 | { | ||
573 | permission = false; | ||
574 | } | ||
575 | |||
576 | // Admin should be able to edit anything in the sim (including admin objects) | ||
577 | if (IsAdministrator(currentUser)) | ||
578 | { | ||
579 | permission = true; | ||
580 | } | ||
581 | |||
582 | return permission; | ||
583 | } | ||
584 | |||
585 | #endregion | ||
586 | |||
587 | #region Generic Permissions | ||
588 | protected bool GenericCommunicationPermission(UUID user, UUID target) | ||
589 | { | ||
590 | // Setting this to true so that cool stuff can happen until we define what determines Generic Communication Permission | ||
591 | bool permission = true; | ||
592 | string reason = "Only registered users may communicate with another account."; | ||
593 | |||
594 | // Uhh, we need to finish this before we enable it.. because it's blocking all sorts of goodies and features | ||
595 | if (IsAdministrator(user)) | ||
596 | permission = true; | ||
597 | |||
598 | if (IsEstateManager(user)) | ||
599 | permission = true; | ||
600 | |||
601 | if (!permission) | ||
602 | SendPermissionError(user, reason); | ||
603 | |||
604 | return permission; | ||
605 | } | ||
606 | |||
607 | public bool GenericEstatePermission(UUID user) | ||
608 | { | ||
609 | // Default: deny | ||
610 | bool permission = false; | ||
611 | |||
612 | // Estate admins should be able to use estate tools | ||
613 | if (IsEstateManager(user)) | ||
614 | permission = true; | ||
615 | |||
616 | // Administrators always have permission | ||
617 | if (IsAdministrator(user)) | ||
618 | permission = true; | ||
619 | |||
620 | return permission; | ||
621 | } | ||
622 | |||
623 | protected bool GenericParcelPermission(UUID user, ILandObject parcel) | ||
624 | { | ||
625 | bool permission = false; | ||
626 | |||
627 | if (parcel.landData.OwnerID == user) | ||
628 | { | ||
629 | permission = true; | ||
630 | } | ||
631 | |||
632 | if (parcel.landData.IsGroupOwned) | ||
633 | { | ||
634 | // TODO: Need to do some extra checks here. Requires group code. | ||
635 | } | ||
636 | |||
637 | if (IsEstateManager(user)) | ||
638 | { | ||
639 | permission = true; | ||
640 | } | ||
641 | |||
642 | if (IsAdministrator(user)) | ||
643 | { | ||
644 | permission = true; | ||
645 | } | ||
646 | |||
647 | return permission; | ||
648 | } | ||
649 | |||
650 | protected bool GenericParcelPermission(UUID user, Vector3 pos) | ||
651 | { | ||
652 | ILandObject parcel = m_scene.LandChannel.GetLandObject(pos.X, pos.Y); | ||
653 | if (parcel == null) return false; | ||
654 | return GenericParcelPermission(user, parcel); | ||
655 | } | ||
656 | #endregion | ||
657 | |||
658 | #region Permission Checks | ||
659 | private bool CanAbandonParcel(UUID user, ILandObject parcel, Scene scene) | ||
660 | { | ||
661 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
662 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
663 | |||
664 | return GenericParcelPermission(user, parcel); | ||
665 | } | ||
666 | |||
667 | private bool CanReclaimParcel(UUID user, ILandObject parcel, Scene scene) | ||
668 | { | ||
669 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
670 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
671 | |||
672 | return GenericParcelPermission(user, parcel); | ||
673 | } | ||
674 | |||
675 | private bool IsGod(UUID user, Scene scene) | ||
676 | { | ||
677 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
678 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
679 | |||
680 | return IsAdministrator(user); | ||
681 | } | ||
682 | |||
683 | private bool CanDuplicateObject(int objectCount, UUID objectID, UUID owner, Scene scene, Vector3 objectPosition) | ||
684 | { | ||
685 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
686 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
687 | |||
688 | if (!GenericObjectPermission(owner, objectID, true)) | ||
689 | { | ||
690 | //They can't even edit the object | ||
691 | return false; | ||
692 | } | ||
693 | |||
694 | SceneObjectPart part = scene.GetSceneObjectPart(objectID); | ||
695 | if (part == null) | ||
696 | return false; | ||
697 | |||
698 | if ((part.OwnerMask & PERM_COPY) == 0) | ||
699 | return false; | ||
700 | |||
701 | if ((part.ParentGroup.GetEffectivePermissions() & PERM_COPY) == 0) | ||
702 | return false; | ||
703 | |||
704 | //If they can rez, they can duplicate | ||
705 | return CanRezObject(objectCount, owner, objectPosition, scene); | ||
706 | } | ||
707 | |||
708 | private bool CanDeleteObject(UUID objectID, UUID deleter, Scene scene) | ||
709 | { | ||
710 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
711 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
712 | |||
713 | return GenericObjectPermission(deleter, objectID, false); | ||
714 | } | ||
715 | |||
716 | private bool CanEditObject(UUID objectID, UUID editorID, Scene scene) | ||
717 | { | ||
718 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
719 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
720 | |||
721 | |||
722 | return GenericObjectPermission(editorID, objectID, false); | ||
723 | } | ||
724 | |||
725 | private bool CanEditObjectInventory(UUID objectID, UUID editorID, Scene scene) | ||
726 | { | ||
727 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
728 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
729 | |||
730 | SceneObjectPart part = m_scene.GetSceneObjectPart(objectID); | ||
731 | |||
732 | // If we selected a sub-prim to edit, the objectID won't represent the object, but only a part. | ||
733 | // We have to check the permissions of the group, though. | ||
734 | if (part.ParentID != 0) | ||
735 | { | ||
736 | objectID = part.ParentUUID; | ||
737 | part = m_scene.GetSceneObjectPart(objectID); | ||
738 | } | ||
739 | |||
740 | // TODO: add group support! | ||
741 | // | ||
742 | if (part.OwnerID != editorID) | ||
743 | return false; | ||
744 | |||
745 | return GenericObjectPermission(editorID, objectID, false); | ||
746 | } | ||
747 | |||
748 | private bool CanEditParcel(UUID user, ILandObject parcel, Scene scene) | ||
749 | { | ||
750 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
751 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
752 | |||
753 | return GenericParcelPermission(user, parcel); | ||
754 | } | ||
755 | |||
756 | /// <summary> | ||
757 | /// Check whether the specified user can edit the given script | ||
758 | /// </summary> | ||
759 | /// <param name="script"></param> | ||
760 | /// <param name="objectID"></param> | ||
761 | /// <param name="user"></param> | ||
762 | /// <param name="scene"></param> | ||
763 | /// <returns></returns> | ||
764 | private bool CanEditScript(UUID script, UUID objectID, UUID user, Scene scene) | ||
765 | { | ||
766 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
767 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
768 | |||
769 | if (m_allowedScriptEditors == UserSet.Administrators && !IsAdministrator(user)) | ||
770 | return false; | ||
771 | |||
772 | // Ordinarily, if you can view it, you can edit it | ||
773 | // There is no viewing a no mod script | ||
774 | // | ||
775 | return CanViewScript(script, objectID, user, scene); | ||
776 | } | ||
777 | |||
778 | /// <summary> | ||
779 | /// Check whether the specified user can edit the given notecard | ||
780 | /// </summary> | ||
781 | /// <param name="notecard"></param> | ||
782 | /// <param name="objectID"></param> | ||
783 | /// <param name="user"></param> | ||
784 | /// <param name="scene"></param> | ||
785 | /// <returns></returns> | ||
786 | private bool CanEditNotecard(UUID notecard, UUID objectID, UUID user, Scene scene) | ||
787 | { | ||
788 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
789 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
790 | |||
791 | if (objectID == UUID.Zero) // User inventory | ||
792 | { | ||
793 | CachedUserInfo userInfo = | ||
794 | scene.CommsManager.UserProfileCacheService.GetUserDetails(user); | ||
795 | |||
796 | if (userInfo == null) | ||
797 | { | ||
798 | m_log.ErrorFormat("[PERMISSIONS]: Could not find user {0} for edit notecard check", user); | ||
799 | return false; | ||
800 | } | ||
801 | |||
802 | if (userInfo.RootFolder == null) | ||
803 | return false; | ||
804 | |||
805 | InventoryItemBase assetRequestItem = userInfo.RootFolder.FindItem(notecard); | ||
806 | if (assetRequestItem == null) // Library item | ||
807 | { | ||
808 | assetRequestItem = scene.CommsManager.UserProfileCacheService.LibraryRoot.FindItem(notecard); | ||
809 | |||
810 | if (assetRequestItem != null) // Implicitly readable | ||
811 | return true; | ||
812 | } | ||
813 | |||
814 | // Notecards must be both mod and copy to be saveable | ||
815 | // This is because of they're not copy, you can't read | ||
816 | // them, and if they're not mod, well, then they're | ||
817 | // not mod. Duh. | ||
818 | // | ||
819 | if ((assetRequestItem.CurrentPermissions & | ||
820 | ((uint)PermissionMask.Modify | | ||
821 | (uint)PermissionMask.Copy)) != | ||
822 | ((uint)PermissionMask.Modify | | ||
823 | (uint)PermissionMask.Copy)) | ||
824 | return false; | ||
825 | } | ||
826 | else // Prim inventory | ||
827 | { | ||
828 | SceneObjectPart part = scene.GetSceneObjectPart(objectID); | ||
829 | |||
830 | if (part == null) | ||
831 | return false; | ||
832 | |||
833 | if (part.OwnerID != user) | ||
834 | return false; | ||
835 | |||
836 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | ||
837 | return false; | ||
838 | |||
839 | TaskInventoryItem ti = part.Inventory.GetInventoryItem(notecard); | ||
840 | |||
841 | if (ti == null) | ||
842 | return false; | ||
843 | |||
844 | if (ti.OwnerID != user) | ||
845 | return false; | ||
846 | |||
847 | // Require full perms | ||
848 | if ((ti.CurrentPermissions & | ||
849 | ((uint)PermissionMask.Modify | | ||
850 | (uint)PermissionMask.Copy)) != | ||
851 | ((uint)PermissionMask.Modify | | ||
852 | (uint)PermissionMask.Copy)) | ||
853 | return false; | ||
854 | } | ||
855 | |||
856 | return true; | ||
857 | } | ||
858 | |||
859 | private bool CanInstantMessage(UUID user, UUID target, Scene startScene) | ||
860 | { | ||
861 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
862 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
863 | |||
864 | // If the sender is an object, check owner instead | ||
865 | // | ||
866 | SceneObjectPart part = startScene.GetSceneObjectPart(user); | ||
867 | if (part != null) | ||
868 | user = part.OwnerID; | ||
869 | |||
870 | return GenericCommunicationPermission(user, target); | ||
871 | } | ||
872 | |||
873 | private bool CanInventoryTransfer(UUID user, UUID target, Scene startScene) | ||
874 | { | ||
875 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
876 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
877 | |||
878 | return GenericCommunicationPermission(user, target); | ||
879 | } | ||
880 | |||
881 | private bool CanIssueEstateCommand(UUID user, Scene requestFromScene, bool ownerCommand) | ||
882 | { | ||
883 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
884 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
885 | |||
886 | if (IsAdministrator(user)) | ||
887 | return true; | ||
888 | |||
889 | if (m_scene.RegionInfo.EstateSettings.IsEstateOwner(user)) | ||
890 | return true; | ||
891 | |||
892 | if (ownerCommand) | ||
893 | return false; | ||
894 | |||
895 | return GenericEstatePermission(user); | ||
896 | } | ||
897 | |||
898 | private bool CanMoveObject(UUID objectID, UUID moverID, Scene scene) | ||
899 | { | ||
900 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
901 | if (m_bypassPermissions) | ||
902 | { | ||
903 | SceneObjectPart part = scene.GetSceneObjectPart(objectID); | ||
904 | if (part.OwnerID != moverID) | ||
905 | { | ||
906 | if (part.ParentGroup != null && !part.ParentGroup.IsDeleted) | ||
907 | { | ||
908 | if (part.ParentGroup.IsAttachment) | ||
909 | return false; | ||
910 | } | ||
911 | } | ||
912 | return m_bypassPermissionsValue; | ||
913 | } | ||
914 | |||
915 | bool permission = GenericObjectPermission(moverID, objectID, true); | ||
916 | if (!permission) | ||
917 | { | ||
918 | if (!m_scene.Entities.ContainsKey(objectID)) | ||
919 | { | ||
920 | return false; | ||
921 | } | ||
922 | |||
923 | // The client | ||
924 | // may request to edit linked parts, and therefore, it needs | ||
925 | // to also check for SceneObjectPart | ||
926 | |||
927 | // If it's not an object, we cant edit it. | ||
928 | if ((!(m_scene.Entities[objectID] is SceneObjectGroup))) | ||
929 | { | ||
930 | return false; | ||
931 | } | ||
932 | |||
933 | |||
934 | SceneObjectGroup task = (SceneObjectGroup)m_scene.Entities[objectID]; | ||
935 | |||
936 | |||
937 | // UUID taskOwner = null; | ||
938 | // Added this because at this point in time it wouldn't be wise for | ||
939 | // the administrator object permissions to take effect. | ||
940 | // UUID objectOwner = task.OwnerID; | ||
941 | |||
942 | // Anyone can move | ||
943 | if ((task.RootPart.EveryoneMask & PERM_MOVE) != 0) | ||
944 | permission = true; | ||
945 | |||
946 | // Locked | ||
947 | if ((task.RootPart.OwnerMask & PERM_LOCKED) == 0) | ||
948 | permission = false; | ||
949 | } | ||
950 | else | ||
951 | { | ||
952 | bool locked = false; | ||
953 | if (!m_scene.Entities.ContainsKey(objectID)) | ||
954 | { | ||
955 | return false; | ||
956 | } | ||
957 | |||
958 | // If it's not an object, we cant edit it. | ||
959 | if ((!(m_scene.Entities[objectID] is SceneObjectGroup))) | ||
960 | { | ||
961 | return false; | ||
962 | } | ||
963 | |||
964 | SceneObjectGroup group = (SceneObjectGroup)m_scene.Entities[objectID]; | ||
965 | |||
966 | UUID objectOwner = group.OwnerID; | ||
967 | locked = ((group.RootPart.OwnerMask & PERM_LOCKED) == 0); | ||
968 | |||
969 | // This is an exception to the generic object permission. | ||
970 | // Administrators who lock their objects should not be able to move them, | ||
971 | // however generic object permission should return true. | ||
972 | // This keeps locked objects from being affected by random click + drag actions by accident | ||
973 | // and allows the administrator to grab or delete a locked object. | ||
974 | |||
975 | // Administrators and estate managers are still able to click+grab locked objects not | ||
976 | // owned by them in the scene | ||
977 | // This is by design. | ||
978 | |||
979 | if (locked && (moverID == objectOwner)) | ||
980 | return false; | ||
981 | } | ||
982 | return permission; | ||
983 | } | ||
984 | |||
985 | private bool CanObjectEntry(UUID objectID, bool enteringRegion, Vector3 newPoint, Scene scene) | ||
986 | { | ||
987 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
988 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
989 | |||
990 | if ((newPoint.X > 257f || newPoint.X < -1f || newPoint.Y > 257f || newPoint.Y < -1f)) | ||
991 | { | ||
992 | return true; | ||
993 | } | ||
994 | |||
995 | SceneObjectGroup task = (SceneObjectGroup)m_scene.Entities[objectID]; | ||
996 | |||
997 | ILandObject land = m_scene.LandChannel.GetLandObject(newPoint.X, newPoint.Y); | ||
998 | |||
999 | if (!enteringRegion) | ||
1000 | { | ||
1001 | ILandObject fromland = m_scene.LandChannel.GetLandObject(task.AbsolutePosition.X, task.AbsolutePosition.Y); | ||
1002 | |||
1003 | if (fromland == land) // Not entering | ||
1004 | return true; | ||
1005 | } | ||
1006 | |||
1007 | if (land == null) | ||
1008 | { | ||
1009 | return false; | ||
1010 | } | ||
1011 | |||
1012 | if ((land.landData.Flags & ((int)Parcel.ParcelFlags.AllowAPrimitiveEntry)) != 0) | ||
1013 | { | ||
1014 | return true; | ||
1015 | } | ||
1016 | |||
1017 | //TODO: check for group rights | ||
1018 | |||
1019 | if (!m_scene.Entities.ContainsKey(objectID)) | ||
1020 | { | ||
1021 | return false; | ||
1022 | } | ||
1023 | |||
1024 | // If it's not an object, we cant edit it. | ||
1025 | if (!(m_scene.Entities[objectID] is SceneObjectGroup)) | ||
1026 | { | ||
1027 | return false; | ||
1028 | } | ||
1029 | |||
1030 | |||
1031 | if (GenericParcelPermission(task.OwnerID, newPoint)) | ||
1032 | { | ||
1033 | return true; | ||
1034 | } | ||
1035 | |||
1036 | //Otherwise, false! | ||
1037 | return false; | ||
1038 | } | ||
1039 | |||
1040 | private bool CanReturnObject(UUID objectID, UUID returnerID, Scene scene) | ||
1041 | { | ||
1042 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1043 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1044 | |||
1045 | return GenericObjectPermission(returnerID, objectID, false); | ||
1046 | } | ||
1047 | |||
1048 | private bool CanRezObject(int objectCount, UUID owner, Vector3 objectPosition, Scene scene) | ||
1049 | { | ||
1050 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1051 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1052 | |||
1053 | bool permission = false; | ||
1054 | |||
1055 | ILandObject land = m_scene.LandChannel.GetLandObject(objectPosition.X, objectPosition.Y); | ||
1056 | if (land == null) return false; | ||
1057 | |||
1058 | if ((land.landData.Flags & ((int)Parcel.ParcelFlags.CreateObjects)) == | ||
1059 | (int)Parcel.ParcelFlags.CreateObjects) | ||
1060 | permission = true; | ||
1061 | |||
1062 | //TODO: check for group rights | ||
1063 | |||
1064 | if (IsAdministrator(owner)) | ||
1065 | { | ||
1066 | permission = true; | ||
1067 | } | ||
1068 | |||
1069 | if (GenericParcelPermission(owner, objectPosition)) | ||
1070 | { | ||
1071 | permission = true; | ||
1072 | } | ||
1073 | |||
1074 | return permission; | ||
1075 | } | ||
1076 | |||
1077 | private bool CanRunConsoleCommand(UUID user, Scene requestFromScene) | ||
1078 | { | ||
1079 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1080 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1081 | |||
1082 | |||
1083 | return IsAdministrator(user); | ||
1084 | } | ||
1085 | |||
1086 | private bool CanRunScript(UUID script, UUID objectID, UUID user, Scene scene) | ||
1087 | { | ||
1088 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1089 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1090 | |||
1091 | return true; | ||
1092 | } | ||
1093 | |||
1094 | private bool CanSellParcel(UUID user, ILandObject parcel, Scene scene) | ||
1095 | { | ||
1096 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1097 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1098 | |||
1099 | return GenericParcelPermission(user, parcel); | ||
1100 | } | ||
1101 | |||
1102 | private bool CanTakeObject(UUID objectID, UUID stealer, Scene scene) | ||
1103 | { | ||
1104 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1105 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1106 | |||
1107 | return GenericObjectPermission(stealer,objectID, false); | ||
1108 | } | ||
1109 | |||
1110 | private bool CanTakeCopyObject(UUID objectID, UUID userID, Scene inScene) | ||
1111 | { | ||
1112 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1113 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1114 | |||
1115 | bool permission = GenericObjectPermission(userID, objectID,false); | ||
1116 | if (!permission) | ||
1117 | { | ||
1118 | if (!m_scene.Entities.ContainsKey(objectID)) | ||
1119 | { | ||
1120 | return false; | ||
1121 | } | ||
1122 | |||
1123 | // If it's not an object, we cant edit it. | ||
1124 | if (!(m_scene.Entities[objectID] is SceneObjectGroup)) | ||
1125 | { | ||
1126 | return false; | ||
1127 | } | ||
1128 | |||
1129 | SceneObjectGroup task = (SceneObjectGroup)m_scene.Entities[objectID]; | ||
1130 | // UUID taskOwner = null; | ||
1131 | // Added this because at this point in time it wouldn't be wise for | ||
1132 | // the administrator object permissions to take effect. | ||
1133 | // UUID objectOwner = task.OwnerID; | ||
1134 | |||
1135 | if ((task.RootPart.EveryoneMask & PERM_COPY) != 0) | ||
1136 | permission = true; | ||
1137 | |||
1138 | if ((task.GetEffectivePermissions() & PERM_COPY) == 0) | ||
1139 | permission = false; | ||
1140 | } | ||
1141 | else | ||
1142 | { | ||
1143 | SceneObjectGroup task = (SceneObjectGroup)m_scene.Entities[objectID]; | ||
1144 | |||
1145 | if ((task.GetEffectivePermissions() & PERM_COPY) == 0) | ||
1146 | permission = false; | ||
1147 | } | ||
1148 | |||
1149 | return permission; | ||
1150 | } | ||
1151 | |||
1152 | private bool CanTerraformLand(UUID user, Vector3 position, Scene requestFromScene) | ||
1153 | { | ||
1154 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1155 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1156 | |||
1157 | // Estate override | ||
1158 | if (GenericEstatePermission(user)) | ||
1159 | return true; | ||
1160 | |||
1161 | float X = position.X; | ||
1162 | float Y = position.Y; | ||
1163 | |||
1164 | if (X > 255) | ||
1165 | X = 255; | ||
1166 | if (Y > 255) | ||
1167 | Y = 255; | ||
1168 | if (X < 0) | ||
1169 | X = 0; | ||
1170 | if (Y < 0) | ||
1171 | Y = 0; | ||
1172 | |||
1173 | ILandObject parcel = m_scene.LandChannel.GetLandObject(X, Y); | ||
1174 | if (parcel == null) | ||
1175 | return false; | ||
1176 | |||
1177 | // Others allowed to terraform? | ||
1178 | if ((parcel.landData.Flags & ((int)Parcel.ParcelFlags.AllowTerraform)) != 0) | ||
1179 | return true; | ||
1180 | |||
1181 | // Land owner can terraform too | ||
1182 | if (parcel != null && GenericParcelPermission(user, parcel)) | ||
1183 | return true; | ||
1184 | |||
1185 | return false; | ||
1186 | } | ||
1187 | |||
1188 | /// <summary> | ||
1189 | /// Check whether the specified user can view the given script | ||
1190 | /// </summary> | ||
1191 | /// <param name="script"></param> | ||
1192 | /// <param name="objectID"></param> | ||
1193 | /// <param name="user"></param> | ||
1194 | /// <param name="scene"></param> | ||
1195 | /// <returns></returns> | ||
1196 | private bool CanViewScript(UUID script, UUID objectID, UUID user, Scene scene) | ||
1197 | { | ||
1198 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1199 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1200 | |||
1201 | if (objectID == UUID.Zero) // User inventory | ||
1202 | { | ||
1203 | CachedUserInfo userInfo = | ||
1204 | scene.CommsManager.UserProfileCacheService.GetUserDetails(user); | ||
1205 | |||
1206 | if (userInfo == null) | ||
1207 | { | ||
1208 | m_log.ErrorFormat("[PERMISSIONS]: Could not find user {0} for administrator check", user); | ||
1209 | return false; | ||
1210 | } | ||
1211 | |||
1212 | if (userInfo.RootFolder == null) | ||
1213 | return false; | ||
1214 | |||
1215 | InventoryItemBase assetRequestItem = userInfo.RootFolder.FindItem(script); | ||
1216 | if (assetRequestItem == null) // Library item | ||
1217 | { | ||
1218 | assetRequestItem = m_scene.CommsManager.UserProfileCacheService.LibraryRoot.FindItem(script); | ||
1219 | |||
1220 | if (assetRequestItem != null) // Implicitly readable | ||
1221 | return true; | ||
1222 | } | ||
1223 | |||
1224 | // SL is rather harebrained here. In SL, a script you | ||
1225 | // have mod/copy no trans is readable. This subverts | ||
1226 | // permissions, but is used in some products, most | ||
1227 | // notably Hippo door plugin and HippoRent 5 networked | ||
1228 | // prim counter. | ||
1229 | // To enable this broken SL-ism, remove Transfer from | ||
1230 | // the below expressions. | ||
1231 | // Trying to improve on SL perms by making a script | ||
1232 | // readable only if it's really full perms | ||
1233 | // | ||
1234 | if ((assetRequestItem.CurrentPermissions & | ||
1235 | ((uint)PermissionMask.Modify | | ||
1236 | (uint)PermissionMask.Copy | | ||
1237 | (uint)PermissionMask.Transfer)) != | ||
1238 | ((uint)PermissionMask.Modify | | ||
1239 | (uint)PermissionMask.Copy | | ||
1240 | (uint)PermissionMask.Transfer)) | ||
1241 | return false; | ||
1242 | } | ||
1243 | else // Prim inventory | ||
1244 | { | ||
1245 | SceneObjectPart part = scene.GetSceneObjectPart(objectID); | ||
1246 | |||
1247 | if (part == null) | ||
1248 | return false; | ||
1249 | |||
1250 | if (part.OwnerID != user) | ||
1251 | return false; | ||
1252 | |||
1253 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | ||
1254 | return false; | ||
1255 | |||
1256 | TaskInventoryItem ti = part.Inventory.GetInventoryItem(script); | ||
1257 | |||
1258 | if (ti == null) | ||
1259 | return false; | ||
1260 | |||
1261 | if (ti.OwnerID != user) | ||
1262 | return false; | ||
1263 | |||
1264 | // Require full perms | ||
1265 | if ((ti.CurrentPermissions & | ||
1266 | ((uint)PermissionMask.Modify | | ||
1267 | (uint)PermissionMask.Copy | | ||
1268 | (uint)PermissionMask.Transfer)) != | ||
1269 | ((uint)PermissionMask.Modify | | ||
1270 | (uint)PermissionMask.Copy | | ||
1271 | (uint)PermissionMask.Transfer)) | ||
1272 | return false; | ||
1273 | } | ||
1274 | |||
1275 | return true; | ||
1276 | } | ||
1277 | |||
1278 | /// <summary> | ||
1279 | /// Check whether the specified user can view the given notecard | ||
1280 | /// </summary> | ||
1281 | /// <param name="script"></param> | ||
1282 | /// <param name="objectID"></param> | ||
1283 | /// <param name="user"></param> | ||
1284 | /// <param name="scene"></param> | ||
1285 | /// <returns></returns> | ||
1286 | private bool CanViewNotecard(UUID notecard, UUID objectID, UUID user, Scene scene) | ||
1287 | { | ||
1288 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1289 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1290 | |||
1291 | if (objectID == UUID.Zero) // User inventory | ||
1292 | { | ||
1293 | CachedUserInfo userInfo = | ||
1294 | scene.CommsManager.UserProfileCacheService.GetUserDetails(user); | ||
1295 | |||
1296 | if (userInfo == null) | ||
1297 | { | ||
1298 | m_log.ErrorFormat("[PERMISSIONS]: Could not find user {0} for view notecard check", user); | ||
1299 | return false; | ||
1300 | } | ||
1301 | |||
1302 | if (userInfo.RootFolder == null) | ||
1303 | return false; | ||
1304 | |||
1305 | InventoryItemBase assetRequestItem = userInfo.RootFolder.FindItem(notecard); | ||
1306 | if (assetRequestItem == null) // Library item | ||
1307 | { | ||
1308 | assetRequestItem = m_scene.CommsManager.UserProfileCacheService.LibraryRoot.FindItem(notecard); | ||
1309 | |||
1310 | if (assetRequestItem != null) // Implicitly readable | ||
1311 | return true; | ||
1312 | } | ||
1313 | |||
1314 | // Notecards are always readable unless no copy | ||
1315 | // | ||
1316 | if ((assetRequestItem.CurrentPermissions & | ||
1317 | (uint)PermissionMask.Copy) != | ||
1318 | (uint)PermissionMask.Copy) | ||
1319 | return false; | ||
1320 | } | ||
1321 | else // Prim inventory | ||
1322 | { | ||
1323 | SceneObjectPart part = scene.GetSceneObjectPart(objectID); | ||
1324 | |||
1325 | if (part == null) | ||
1326 | return false; | ||
1327 | |||
1328 | if (part.OwnerID != user) | ||
1329 | return false; | ||
1330 | |||
1331 | if ((part.OwnerMask & (uint)PermissionMask.Modify) == 0) | ||
1332 | return false; | ||
1333 | |||
1334 | TaskInventoryItem ti = part.Inventory.GetInventoryItem(notecard); | ||
1335 | |||
1336 | if (ti == null) | ||
1337 | return false; | ||
1338 | |||
1339 | if (ti.OwnerID != user) | ||
1340 | return false; | ||
1341 | |||
1342 | // Notecards are always readable unless no copy | ||
1343 | // | ||
1344 | if ((ti.CurrentPermissions & | ||
1345 | (uint)PermissionMask.Copy) != | ||
1346 | (uint)PermissionMask.Copy) | ||
1347 | return false; | ||
1348 | } | ||
1349 | |||
1350 | return true; | ||
1351 | } | ||
1352 | |||
1353 | #endregion | ||
1354 | |||
1355 | private bool CanLinkObject(UUID userID, UUID objectID) | ||
1356 | { | ||
1357 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1358 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1359 | |||
1360 | return true; | ||
1361 | } | ||
1362 | |||
1363 | private bool CanDelinkObject(UUID userID, UUID objectID) | ||
1364 | { | ||
1365 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1366 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1367 | |||
1368 | return true; | ||
1369 | } | ||
1370 | |||
1371 | private bool CanBuyLand(UUID userID, ILandObject parcel, Scene scene) | ||
1372 | { | ||
1373 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1374 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1375 | |||
1376 | return true; | ||
1377 | } | ||
1378 | |||
1379 | private bool CanCopyObjectInventory(UUID itemID, UUID objectID, UUID userID) | ||
1380 | { | ||
1381 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1382 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1383 | |||
1384 | return true; | ||
1385 | } | ||
1386 | |||
1387 | private bool CanDeleteObjectInventory(UUID itemID, UUID objectID, UUID userID) | ||
1388 | { | ||
1389 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1390 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1391 | |||
1392 | return true; | ||
1393 | } | ||
1394 | |||
1395 | /// <summary> | ||
1396 | /// Check whether the specified user is allowed to directly create the given inventory type in a prim's | ||
1397 | /// inventory (e.g. the New Script button in the 1.21 Linden Lab client). | ||
1398 | /// </summary> | ||
1399 | /// <param name="invType"></param> | ||
1400 | /// <param name="objectID"></param> | ||
1401 | /// <param name="userID"></param> | ||
1402 | /// <returns></returns> | ||
1403 | private bool CanCreateObjectInventory(int invType, UUID objectID, UUID userID) | ||
1404 | { | ||
1405 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1406 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1407 | |||
1408 | if ((int)InventoryType.LSL == invType) | ||
1409 | if (m_allowedScriptCreators == UserSet.Administrators && !IsAdministrator(userID)) | ||
1410 | return false; | ||
1411 | |||
1412 | return true; | ||
1413 | } | ||
1414 | |||
1415 | /// <summary> | ||
1416 | /// Check whether the specified user is allowed to create the given inventory type in their inventory. | ||
1417 | /// </summary> | ||
1418 | /// <param name="invType"></param> | ||
1419 | /// <param name="userID"></param> | ||
1420 | /// <returns></returns> | ||
1421 | private bool CanCreateUserInventory(int invType, UUID userID) | ||
1422 | { | ||
1423 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1424 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1425 | |||
1426 | if ((int)InventoryType.LSL == invType) | ||
1427 | if (m_allowedScriptCreators == UserSet.Administrators && !IsAdministrator(userID)) | ||
1428 | return false; | ||
1429 | |||
1430 | return true; | ||
1431 | } | ||
1432 | |||
1433 | /// <summary> | ||
1434 | /// Check whether the specified user is allowed to copy the given inventory type in their inventory. | ||
1435 | /// </summary> | ||
1436 | /// <param name="itemID"></param> | ||
1437 | /// <param name="userID"></param> | ||
1438 | /// <returns></returns> | ||
1439 | private bool CanCopyUserInventory(UUID itemID, UUID userID) | ||
1440 | { | ||
1441 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1442 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1443 | |||
1444 | return true; | ||
1445 | } | ||
1446 | |||
1447 | /// <summary> | ||
1448 | /// Check whether the specified user is allowed to edit the given inventory item within their own inventory. | ||
1449 | /// </summary> | ||
1450 | /// <param name="itemID"></param> | ||
1451 | /// <param name="userID"></param> | ||
1452 | /// <returns></returns> | ||
1453 | private bool CanEditUserInventory(UUID itemID, UUID userID) | ||
1454 | { | ||
1455 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1456 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1457 | |||
1458 | return true; | ||
1459 | } | ||
1460 | |||
1461 | /// <summary> | ||
1462 | /// Check whether the specified user is allowed to delete the given inventory item from their own inventory. | ||
1463 | /// </summary> | ||
1464 | /// <param name="itemID"></param> | ||
1465 | /// <param name="userID"></param> | ||
1466 | /// <returns></returns> | ||
1467 | private bool CanDeleteUserInventory(UUID itemID, UUID userID) | ||
1468 | { | ||
1469 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1470 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1471 | |||
1472 | return true; | ||
1473 | } | ||
1474 | |||
1475 | private bool CanTeleport(UUID userID) | ||
1476 | { | ||
1477 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1478 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1479 | |||
1480 | return true; | ||
1481 | } | ||
1482 | |||
1483 | private bool CanResetScript(UUID prim, UUID script, UUID agentID, Scene scene) | ||
1484 | { | ||
1485 | DebugPermissionInformation(MethodInfo.GetCurrentMethod().Name); | ||
1486 | if (m_bypassPermissions) return m_bypassPermissionsValue; | ||
1487 | |||
1488 | SceneObjectPart part = m_scene.GetSceneObjectPart(prim); | ||
1489 | |||
1490 | // If we selected a sub-prim to reset, prim won't represent the object, but only a part. | ||
1491 | // We have to check the permissions of the object, though. | ||
1492 | if (part.ParentID != 0) prim = part.ParentUUID; | ||
1493 | |||
1494 | // You can reset the scripts in any object you can edit | ||
1495 | return GenericObjectPermission(agentID, prim, false); | ||
1496 | } | ||
1497 | } | ||
1498 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Serialiser/IFileSerialiser.cs b/OpenSim/Region/CoreModules/World/Serialiser/IFileSerialiser.cs new file mode 100644 index 0000000..acc7bb8 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Serialiser/IFileSerialiser.cs | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Scenes; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Serialiser | ||
31 | { | ||
32 | internal interface IFileSerialiser | ||
33 | { | ||
34 | string WriteToFile(Scene scene, string dir); | ||
35 | } | ||
36 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Serialiser/SerialiseObjects.cs b/OpenSim/Region/CoreModules/World/Serialiser/SerialiseObjects.cs new file mode 100644 index 0000000..ed6448f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Serialiser/SerialiseObjects.cs | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Collections.Generic; | ||
29 | using System.IO; | ||
30 | using System.IO.Compression; | ||
31 | using System.Text; | ||
32 | using System.Xml; | ||
33 | using OpenSim.Region.Framework.Scenes; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Serialiser | ||
36 | { | ||
37 | internal class SerialiseObjects : IFileSerialiser | ||
38 | { | ||
39 | #region IFileSerialiser Members | ||
40 | |||
41 | public string WriteToFile(Scene scene, string dir) | ||
42 | { | ||
43 | string targetFileName = dir + "objects.xml"; | ||
44 | |||
45 | SaveSerialisedToFile(targetFileName, scene); | ||
46 | |||
47 | return "objects.xml"; | ||
48 | } | ||
49 | |||
50 | #endregion | ||
51 | |||
52 | public void SaveSerialisedToFile(string fileName, Scene scene) | ||
53 | { | ||
54 | string xmlstream = GetObjectXml(scene); | ||
55 | |||
56 | MemoryStream stream = ReformatXmlString(xmlstream); | ||
57 | |||
58 | stream.Seek(0, SeekOrigin.Begin); | ||
59 | CreateXmlFile(stream, fileName); | ||
60 | |||
61 | stream.Seek(0, SeekOrigin.Begin); | ||
62 | CreateCompressedXmlFile(stream, fileName); | ||
63 | } | ||
64 | |||
65 | private static MemoryStream ReformatXmlString(string xmlstream) | ||
66 | { | ||
67 | MemoryStream stream = new MemoryStream(); | ||
68 | XmlTextWriter formatter = new XmlTextWriter(stream, Encoding.UTF8); | ||
69 | XmlDocument doc = new XmlDocument(); | ||
70 | |||
71 | doc.LoadXml(xmlstream); | ||
72 | formatter.Formatting = Formatting.Indented; | ||
73 | doc.WriteContentTo(formatter); | ||
74 | formatter.Flush(); | ||
75 | return stream; | ||
76 | } | ||
77 | |||
78 | private static string GetObjectXml(Scene scene) | ||
79 | { | ||
80 | string xmlstream = "<scene>"; | ||
81 | |||
82 | List<EntityBase> EntityList = scene.GetEntities(); | ||
83 | List<string> EntityXml = new List<string>(); | ||
84 | |||
85 | foreach (EntityBase ent in EntityList) | ||
86 | { | ||
87 | if (ent is SceneObjectGroup) | ||
88 | { | ||
89 | EntityXml.Add(((SceneObjectGroup) ent).ToXmlString2()); | ||
90 | } | ||
91 | } | ||
92 | EntityXml.Sort(); | ||
93 | |||
94 | foreach (string xml in EntityXml) | ||
95 | xmlstream += xml; | ||
96 | |||
97 | xmlstream += "</scene>"; | ||
98 | return xmlstream; | ||
99 | } | ||
100 | |||
101 | private static void CreateXmlFile(MemoryStream xmlStream, string fileName) | ||
102 | { | ||
103 | FileStream objectsFile = new FileStream(fileName, FileMode.Create); | ||
104 | |||
105 | xmlStream.WriteTo(objectsFile); | ||
106 | objectsFile.Flush(); | ||
107 | objectsFile.Close(); | ||
108 | } | ||
109 | |||
110 | private static void CreateCompressedXmlFile(MemoryStream xmlStream, string fileName) | ||
111 | { | ||
112 | #region GZip Compressed Version | ||
113 | |||
114 | FileStream objectsFileCompressed = new FileStream(fileName + ".gzs", FileMode.Create); | ||
115 | MemoryStream gzipMSStream = new MemoryStream(); | ||
116 | GZipStream gzipStream = new GZipStream(gzipMSStream, CompressionMode.Compress); | ||
117 | xmlStream.WriteTo(gzipStream); | ||
118 | gzipMSStream.WriteTo(objectsFileCompressed); | ||
119 | objectsFileCompressed.Flush(); | ||
120 | objectsFileCompressed.Close(); | ||
121 | |||
122 | #endregion | ||
123 | } | ||
124 | } | ||
125 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Serialiser/SerialiseTerrain.cs b/OpenSim/Region/CoreModules/World/Serialiser/SerialiseTerrain.cs new file mode 100644 index 0000000..924218a --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Serialiser/SerialiseTerrain.cs | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Serialiser | ||
33 | { | ||
34 | internal class SerialiseTerrain : IFileSerialiser | ||
35 | { | ||
36 | #region IFileSerialiser Members | ||
37 | |||
38 | public string WriteToFile(Scene scene, string dir) | ||
39 | { | ||
40 | ITerrainLoader fileSystemExporter = new RAW32(); | ||
41 | string targetFileName = dir + "heightmap.r32"; | ||
42 | |||
43 | lock (scene.Heightmap) | ||
44 | { | ||
45 | fileSystemExporter.SaveFile(targetFileName, scene.Heightmap); | ||
46 | } | ||
47 | |||
48 | return "heightmap.r32"; | ||
49 | } | ||
50 | |||
51 | #endregion | ||
52 | } | ||
53 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Serialiser/SerialiserModule.cs b/OpenSim/Region/CoreModules/World/Serialiser/SerialiserModule.cs new file mode 100644 index 0000000..7080d5f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Serialiser/SerialiserModule.cs | |||
@@ -0,0 +1,226 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using OpenMetaverse; | ||
32 | using Nini.Config; | ||
33 | using OpenSim.Region.Framework.Interfaces; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||
36 | |||
37 | namespace OpenSim.Region.CoreModules.World.Serialiser | ||
38 | { | ||
39 | public class SerialiserModule : IRegionModule, IRegionSerialiserModule | ||
40 | { | ||
41 | private Commander m_commander = new Commander("export"); | ||
42 | private List<Scene> m_regions = new List<Scene>(); | ||
43 | private string m_savedir = "exports" + "/"; | ||
44 | private List<IFileSerialiser> m_serialisers = new List<IFileSerialiser>(); | ||
45 | |||
46 | #region IRegionModule Members | ||
47 | |||
48 | public void Initialise(Scene scene, IConfigSource source) | ||
49 | { | ||
50 | scene.RegisterModuleCommander(m_commander); | ||
51 | scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||
52 | scene.RegisterModuleInterface<IRegionSerialiserModule>(this); | ||
53 | |||
54 | lock (m_regions) | ||
55 | { | ||
56 | m_regions.Add(scene); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | public void PostInitialise() | ||
61 | { | ||
62 | lock (m_serialisers) | ||
63 | { | ||
64 | m_serialisers.Add(new SerialiseTerrain()); | ||
65 | m_serialisers.Add(new SerialiseObjects()); | ||
66 | } | ||
67 | |||
68 | LoadCommanderCommands(); | ||
69 | } | ||
70 | |||
71 | public void Close() | ||
72 | { | ||
73 | m_regions.Clear(); | ||
74 | } | ||
75 | |||
76 | public string Name | ||
77 | { | ||
78 | get { return "ExportSerialisationModule"; } | ||
79 | } | ||
80 | |||
81 | public bool IsSharedModule | ||
82 | { | ||
83 | get { return true; } | ||
84 | } | ||
85 | |||
86 | #endregion | ||
87 | |||
88 | #region IRegionSerialiser Members | ||
89 | |||
90 | public void LoadPrimsFromXml(Scene scene, string fileName, bool newIDS, Vector3 loadOffset) | ||
91 | { | ||
92 | SceneXmlLoader.LoadPrimsFromXml(scene, fileName, newIDS, loadOffset); | ||
93 | } | ||
94 | |||
95 | public void SavePrimsToXml(Scene scene, string fileName) | ||
96 | { | ||
97 | SceneXmlLoader.SavePrimsToXml(scene, fileName); | ||
98 | } | ||
99 | |||
100 | public void LoadPrimsFromXml2(Scene scene, string fileName) | ||
101 | { | ||
102 | SceneXmlLoader.LoadPrimsFromXml2(scene, fileName); | ||
103 | } | ||
104 | |||
105 | public void LoadPrimsFromXml2(Scene scene, TextReader reader, bool startScripts) | ||
106 | { | ||
107 | SceneXmlLoader.LoadPrimsFromXml2(scene, reader, startScripts); | ||
108 | } | ||
109 | |||
110 | public void SavePrimsToXml2(Scene scene, string fileName) | ||
111 | { | ||
112 | SceneXmlLoader.SavePrimsToXml2(scene, fileName); | ||
113 | } | ||
114 | |||
115 | public void SavePrimsToXml2(Scene scene, TextWriter stream, Vector3 min, Vector3 max) | ||
116 | { | ||
117 | SceneXmlLoader.SavePrimsToXml2(scene, stream, min, max); | ||
118 | } | ||
119 | |||
120 | public void SaveNamedPrimsToXml2(Scene scene, string primName, string fileName) | ||
121 | { | ||
122 | SceneXmlLoader.SaveNamedPrimsToXml2(scene, primName, fileName); | ||
123 | } | ||
124 | |||
125 | public SceneObjectGroup DeserializeGroupFromXml2(string xmlString) | ||
126 | { | ||
127 | return SceneXmlLoader.DeserializeGroupFromXml2(xmlString); | ||
128 | } | ||
129 | |||
130 | public string SaveGroupToXml2(SceneObjectGroup grp) | ||
131 | { | ||
132 | return SceneXmlLoader.SaveGroupToXml2(grp); | ||
133 | } | ||
134 | |||
135 | public void SavePrimListToXml2(List<EntityBase> entityList, string fileName) | ||
136 | { | ||
137 | SceneXmlLoader.SavePrimListToXml2(entityList, fileName); | ||
138 | } | ||
139 | |||
140 | public void SavePrimListToXml2(List<EntityBase> entityList, TextWriter stream, Vector3 min, Vector3 max) | ||
141 | { | ||
142 | SceneXmlLoader.SavePrimListToXml2(entityList, stream, min, max); | ||
143 | } | ||
144 | |||
145 | public List<string> SerialiseRegion(Scene scene, string saveDir) | ||
146 | { | ||
147 | List<string> results = new List<string>(); | ||
148 | |||
149 | if (!Directory.Exists(saveDir)) | ||
150 | { | ||
151 | Directory.CreateDirectory(saveDir); | ||
152 | } | ||
153 | |||
154 | lock (m_serialisers) | ||
155 | { | ||
156 | foreach (IFileSerialiser serialiser in m_serialisers) | ||
157 | { | ||
158 | results.Add(serialiser.WriteToFile(scene, saveDir)); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | TextWriter regionInfoWriter = new StreamWriter(saveDir + "README.TXT"); | ||
163 | regionInfoWriter.WriteLine("Region Name: " + scene.RegionInfo.RegionName); | ||
164 | regionInfoWriter.WriteLine("Region ID: " + scene.RegionInfo.RegionID.ToString()); | ||
165 | regionInfoWriter.WriteLine("Backup Time: UTC " + DateTime.UtcNow.ToString()); | ||
166 | regionInfoWriter.WriteLine("Serialise Version: 0.1"); | ||
167 | regionInfoWriter.Close(); | ||
168 | |||
169 | TextWriter manifestWriter = new StreamWriter(saveDir + "region.manifest"); | ||
170 | foreach (string line in results) | ||
171 | { | ||
172 | manifestWriter.WriteLine(line); | ||
173 | } | ||
174 | manifestWriter.Close(); | ||
175 | |||
176 | return results; | ||
177 | } | ||
178 | |||
179 | #endregion | ||
180 | |||
181 | private void EventManager_OnPluginConsole(string[] args) | ||
182 | { | ||
183 | if (args[0] == "export") | ||
184 | { | ||
185 | string[] tmpArgs = new string[args.Length - 2]; | ||
186 | int i = 0; | ||
187 | for (i = 2; i < args.Length; i++) | ||
188 | tmpArgs[i - 2] = args[i]; | ||
189 | |||
190 | m_commander.ProcessConsoleCommand(args[1], tmpArgs); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | private void InterfaceSaveRegion(Object[] args) | ||
195 | { | ||
196 | foreach (Scene region in m_regions) | ||
197 | { | ||
198 | if (region.RegionInfo.RegionName == (string) args[0]) | ||
199 | { | ||
200 | // List<string> results = SerialiseRegion(region, m_savedir + region.RegionInfo.RegionID.ToString() + "/"); | ||
201 | SerialiseRegion(region, m_savedir + region.RegionInfo.RegionID.ToString() + "/"); | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | private void InterfaceSaveAllRegions(Object[] args) | ||
207 | { | ||
208 | foreach (Scene region in m_regions) | ||
209 | { | ||
210 | // List<string> results = SerialiseRegion(region, m_savedir + region.RegionInfo.RegionID.ToString() + "/"); | ||
211 | SerialiseRegion(region, m_savedir + region.RegionInfo.RegionID.ToString() + "/"); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | private void LoadCommanderCommands() | ||
216 | { | ||
217 | Command serialiseSceneCommand = new Command("save", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceSaveRegion, "Saves the named region into the exports directory."); | ||
218 | serialiseSceneCommand.AddArgument("region-name", "The name of the region you wish to export", "String"); | ||
219 | |||
220 | Command serialiseAllScenesCommand = new Command("save-all",CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceSaveAllRegions, "Saves all regions into the exports directory."); | ||
221 | |||
222 | m_commander.RegisterCommand("save", serialiseSceneCommand); | ||
223 | m_commander.RegisterCommand("save-all", serialiseAllScenesCommand); | ||
224 | } | ||
225 | } | ||
226 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs new file mode 100644 index 0000000..ffd6b8d --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using Nini.Config; | ||
31 | using log4net; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Framework.Interfaces; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | |||
37 | namespace OpenSim.Region.CoreModules.World.Sound | ||
38 | { | ||
39 | public class SoundModule : IRegionModule, ISoundModule | ||
40 | { | ||
41 | //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
42 | |||
43 | protected Scene m_scene; | ||
44 | |||
45 | public void Initialise(Scene scene, IConfigSource source) | ||
46 | { | ||
47 | m_scene = scene; | ||
48 | |||
49 | m_scene.EventManager.OnNewClient += OnNewClient; | ||
50 | |||
51 | m_scene.RegisterModuleInterface<ISoundModule>(this); | ||
52 | } | ||
53 | |||
54 | public void PostInitialise() {} | ||
55 | public void Close() {} | ||
56 | public string Name { get { return "Sound Module"; } } | ||
57 | public bool IsSharedModule { get { return false; } } | ||
58 | |||
59 | private void OnNewClient(IClientAPI client) | ||
60 | { | ||
61 | client.OnSoundTrigger += TriggerSound; | ||
62 | } | ||
63 | |||
64 | public virtual void PlayAttachedSound( | ||
65 | UUID soundID, UUID ownerID, UUID objectID, double gain, Vector3 position, byte flags) | ||
66 | { | ||
67 | foreach (ScenePresence p in m_scene.GetAvatars()) | ||
68 | { | ||
69 | double dis = Util.GetDistanceTo(p.AbsolutePosition, position); | ||
70 | if (dis > 100.0) // Max audio distance | ||
71 | continue; | ||
72 | |||
73 | // Scale by distance | ||
74 | gain = (float)((double)gain*((100.0 - dis) / 100.0)); | ||
75 | |||
76 | p.ControllingClient.SendPlayAttachedSound(soundID, objectID, ownerID, (float)gain, flags); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | public virtual void TriggerSound( | ||
81 | UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle) | ||
82 | { | ||
83 | foreach (ScenePresence p in m_scene.GetAvatars()) | ||
84 | { | ||
85 | double dis = Util.GetDistanceTo(p.AbsolutePosition, position); | ||
86 | if (dis > 100.0) // Max audio distance | ||
87 | continue; | ||
88 | |||
89 | // Scale by distance | ||
90 | gain = (float)((double)gain*((100.0 - dis) / 100.0)); | ||
91 | |||
92 | p.ControllingClient.SendTriggeredSound( | ||
93 | soundId, ownerID, objectID, parentID, handle, position, (float)gain); | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Sun/SunModule.cs b/OpenSim/Region/CoreModules/World/Sun/SunModule.cs new file mode 100644 index 0000000..b36684c --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Sun/SunModule.cs | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using OpenMetaverse; | ||
31 | using Nini.Config; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Framework.Interfaces; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | |||
36 | namespace OpenSim.Region.CoreModules | ||
37 | { | ||
38 | public class SunModule : IRegionModule | ||
39 | { | ||
40 | |||
41 | private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
42 | |||
43 | private const double SeasonalTilt = 0.03 * Math.PI; // A daily shift of approximately 1.7188 degrees | ||
44 | private const double AverageTilt = -0.25 * Math.PI; // A 45 degree tilt | ||
45 | private const double SunCycle = 2.0D * Math.PI; // A perfect circle measured in radians | ||
46 | private const double SeasonalCycle = 2.0D * Math.PI; // Ditto | ||
47 | |||
48 | // | ||
49 | // Per Region Values | ||
50 | // | ||
51 | |||
52 | private bool ready = false; | ||
53 | |||
54 | // Configurable values | ||
55 | private string m_mode = "SL"; | ||
56 | private int m_frame_mod = 0; | ||
57 | private double m_day_length = 0; | ||
58 | private int m_year_length = 0; | ||
59 | private double m_day_night = 0; | ||
60 | // private double m_longitude = 0; | ||
61 | // private double m_latitude = 0; | ||
62 | // Configurable defaults Defaults close to SL | ||
63 | private string d_mode = "SL"; | ||
64 | private int d_frame_mod = 100; // Every 10 seconds (actually less) | ||
65 | private double d_day_length = 4; // A VW day is 4 RW hours long | ||
66 | private int d_year_length = 60; // There are 60 VW days in a VW year | ||
67 | private double d_day_night = 0.45; // axis offset: ratio of light-to-dark, approx 1:3 | ||
68 | // private double d_longitude = -73.53; | ||
69 | // private double d_latitude = 41.29; | ||
70 | |||
71 | // Frame counter | ||
72 | private uint m_frame = 0; | ||
73 | |||
74 | // Cached Scene reference | ||
75 | private Scene m_scene = null; | ||
76 | |||
77 | // Calculated Once in the lifetime of a region | ||
78 | private long TicksToEpoch; // Elapsed time for 1/1/1970 | ||
79 | private uint SecondsPerSunCycle; // Length of a virtual day in RW seconds | ||
80 | private uint SecondsPerYear; // Length of a virtual year in RW seconds | ||
81 | private double SunSpeed; // Rate of passage in radians/second | ||
82 | private double SeasonSpeed; // Rate of change for seasonal effects | ||
83 | // private double HoursToRadians; // Rate of change for seasonal effects | ||
84 | private long TicksOffset = 0; // seconds offset from UTC | ||
85 | // Calculated every update | ||
86 | private float OrbitalPosition; // Orbital placement at a point in time | ||
87 | private double HorizonShift; // Axis offset to skew day and night | ||
88 | private double TotalDistanceTravelled; // Distance since beginning of time (in radians) | ||
89 | private double SeasonalOffset; // Seaonal variation of tilt | ||
90 | private float Magnitude; // Normal tilt | ||
91 | // private double VWTimeRatio; // VW time as a ratio of real time | ||
92 | |||
93 | // Working values | ||
94 | private Vector3 Position = Vector3.Zero; | ||
95 | private Vector3 Velocity = Vector3.Zero; | ||
96 | private Quaternion Tilt = new Quaternion(1.0f, 0.0f, 0.0f, 0.0f); | ||
97 | |||
98 | private long LindenHourOffset = 0; | ||
99 | private bool sunFixed = false; | ||
100 | |||
101 | private Dictionary<UUID, ulong> m_rootAgents = new Dictionary<UUID, ulong>(); | ||
102 | |||
103 | // Current time in elapsed seconds since Jan 1st 1970 | ||
104 | private ulong CurrentTime | ||
105 | { | ||
106 | get { | ||
107 | return (ulong)(((System.DateTime.Now.Ticks) - TicksToEpoch + TicksOffset + LindenHourOffset)/10000000); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | private float GetLindenEstateHourFromCurrentTime() | ||
112 | { | ||
113 | float ticksleftover = ((float)CurrentTime) % ((float)SecondsPerSunCycle); | ||
114 | |||
115 | float hour = (24 * (ticksleftover / SecondsPerSunCycle)) + 6; | ||
116 | |||
117 | return hour; | ||
118 | } | ||
119 | |||
120 | private void SetTimeByLindenHour(float LindenHour) | ||
121 | { | ||
122 | // Linden hour is 24 hours with a 6 hour offset. 6-30 | ||
123 | |||
124 | if (LindenHour - 6 == 0) | ||
125 | { | ||
126 | LindenHourOffset = 0; | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | // Remove LindenHourOffset to calculate it from LocalTime | ||
131 | float ticksleftover = ((float)(((long)(CurrentTime * 10000000) - (long)LindenHourOffset)/ 10000000) % ((float)SecondsPerSunCycle)); | ||
132 | float hour = (24 * (ticksleftover / SecondsPerSunCycle)); | ||
133 | |||
134 | float offsethours = 0; | ||
135 | |||
136 | if (LindenHour - 6 > hour) | ||
137 | { | ||
138 | offsethours = hour + ((LindenHour-6) - hour); | ||
139 | } | ||
140 | else | ||
141 | { | ||
142 | offsethours = hour - (hour - (LindenHour - 6)); | ||
143 | } | ||
144 | //m_log.Debug("[OFFSET]: " + hour + " - " + LindenHour + " - " + offsethours.ToString()); | ||
145 | |||
146 | LindenHourOffset = (long)((float)offsethours * (36000000000/m_day_length)); | ||
147 | m_log.Debug("[SUN]: Directive from the Estate Tools to set the sun phase to LindenHour " + GetLindenEstateHourFromCurrentTime().ToString()); | ||
148 | } | ||
149 | |||
150 | // Called immediately after the module is loaded for a given region | ||
151 | // i.e. Immediately after instance creation. | ||
152 | public void Initialise(Scene scene, IConfigSource config) | ||
153 | { | ||
154 | m_scene = scene; | ||
155 | |||
156 | m_frame = 0; | ||
157 | |||
158 | TimeZone local = TimeZone.CurrentTimeZone; | ||
159 | TicksOffset = local.GetUtcOffset(local.ToLocalTime(DateTime.Now)).Ticks; | ||
160 | m_log.Debug("[SUN]: localtime offset is " + TicksOffset); | ||
161 | |||
162 | // Align ticks with Second Life | ||
163 | |||
164 | TicksToEpoch = new System.DateTime(1970,1,1).Ticks; | ||
165 | |||
166 | // Just in case they don't have the stanzas | ||
167 | try | ||
168 | { | ||
169 | // Mode: determines how the sun is handled | ||
170 | m_mode = config.Configs["Sun"].GetString("mode", d_mode); | ||
171 | // Mode: determines how the sun is handled | ||
172 | // m_latitude = config.Configs["Sun"].GetDouble("latitude", d_latitude); | ||
173 | // Mode: determines how the sun is handled | ||
174 | // m_longitude = config.Configs["Sun"].GetDouble("longitude", d_longitude); | ||
175 | // Year length in days | ||
176 | m_year_length = config.Configs["Sun"].GetInt("year_length", d_year_length); | ||
177 | // Day length in decimal hours | ||
178 | m_day_length = config.Configs["Sun"].GetDouble("day_length", d_day_length); | ||
179 | // Day to Night Ratio | ||
180 | m_day_night = config.Configs["Sun"].GetDouble("day_night_offset", d_day_night); | ||
181 | // Update frequency in frames | ||
182 | m_frame_mod = config.Configs["Sun"].GetInt("update_interval", d_frame_mod); | ||
183 | } | ||
184 | catch (Exception e) | ||
185 | { | ||
186 | m_log.Debug("[SUN]: Configuration access failed, using defaults. Reason: "+e.Message); | ||
187 | m_mode = d_mode; | ||
188 | m_year_length = d_year_length; | ||
189 | m_day_length = d_day_length; | ||
190 | m_day_night = d_day_night; | ||
191 | m_frame_mod = d_frame_mod; | ||
192 | // m_latitude = d_latitude; | ||
193 | // m_longitude = d_longitude; | ||
194 | } | ||
195 | |||
196 | switch (m_mode) | ||
197 | { | ||
198 | case "T1": | ||
199 | default: | ||
200 | case "SL": | ||
201 | // Time taken to complete a cycle (day and season) | ||
202 | |||
203 | SecondsPerSunCycle = (uint) (m_day_length * 60 * 60); | ||
204 | SecondsPerYear = (uint) (SecondsPerSunCycle*m_year_length); | ||
205 | |||
206 | // Ration of real-to-virtual time | ||
207 | |||
208 | // VWTimeRatio = 24/m_day_length; | ||
209 | |||
210 | // Speed of rotation needed to complete a cycle in the | ||
211 | // designated period (day and season) | ||
212 | |||
213 | SunSpeed = SunCycle/SecondsPerSunCycle; | ||
214 | SeasonSpeed = SeasonalCycle/SecondsPerYear; | ||
215 | |||
216 | // Horizon translation | ||
217 | |||
218 | HorizonShift = m_day_night; // Z axis translation | ||
219 | // HoursToRadians = (SunCycle/24)*VWTimeRatio; | ||
220 | |||
221 | // Insert our event handling hooks | ||
222 | |||
223 | scene.EventManager.OnFrame += SunUpdate; | ||
224 | scene.EventManager.OnMakeChildAgent += MakeChildAgent; | ||
225 | scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; | ||
226 | scene.EventManager.OnClientClosed += ClientLoggedOut; | ||
227 | scene.EventManager.OnEstateToolsTimeUpdate += EstateToolsTimeUpdate; | ||
228 | scene.EventManager.OnGetSunLindenHour += GetLindenEstateHourFromCurrentTime; | ||
229 | |||
230 | ready = true; | ||
231 | |||
232 | m_log.Debug("[SUN]: Mode is "+m_mode); | ||
233 | m_log.Debug("[SUN]: Initialization completed. Day is "+SecondsPerSunCycle+" seconds, and year is "+m_year_length+" days"); | ||
234 | m_log.Debug("[SUN]: Axis offset is "+m_day_night); | ||
235 | m_log.Debug("[SUN]: Positional data updated every "+m_frame_mod+" frames"); | ||
236 | |||
237 | break; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | public void PostInitialise() | ||
242 | { | ||
243 | } | ||
244 | |||
245 | public void Close() | ||
246 | { | ||
247 | ready = false; | ||
248 | |||
249 | // Remove our hooks | ||
250 | m_scene.EventManager.OnFrame -= SunUpdate; | ||
251 | m_scene.EventManager.OnMakeChildAgent -= MakeChildAgent; | ||
252 | m_scene.EventManager.OnAvatarEnteringNewParcel -= AvatarEnteringParcel; | ||
253 | m_scene.EventManager.OnClientClosed -= ClientLoggedOut; | ||
254 | m_scene.EventManager.OnEstateToolsTimeUpdate -= EstateToolsTimeUpdate; | ||
255 | m_scene.EventManager.OnGetSunLindenHour -= GetLindenEstateHourFromCurrentTime; | ||
256 | } | ||
257 | |||
258 | public string Name | ||
259 | { | ||
260 | get { return "SunModule"; } | ||
261 | } | ||
262 | |||
263 | public bool IsSharedModule | ||
264 | { | ||
265 | get { return false; } | ||
266 | } | ||
267 | |||
268 | public void SunToClient(IClientAPI client) | ||
269 | { | ||
270 | if (m_mode != "T1") | ||
271 | { | ||
272 | if (ready) | ||
273 | { | ||
274 | if (!sunFixed) | ||
275 | GenSunPos(); // Generate shared values once | ||
276 | client.SendSunPos(Position, Velocity, CurrentTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | public void SunUpdate() | ||
282 | { | ||
283 | if (((m_frame++%m_frame_mod) != 0) || !ready || sunFixed) | ||
284 | { | ||
285 | return; | ||
286 | } | ||
287 | |||
288 | GenSunPos(); // Generate shared values once | ||
289 | |||
290 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
291 | foreach (ScenePresence avatar in avatars) | ||
292 | { | ||
293 | if (!avatar.IsChildAgent) | ||
294 | avatar.ControllingClient.SendSunPos(Position, Velocity, CurrentTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition); | ||
295 | } | ||
296 | |||
297 | // set estate settings for region access to sun position | ||
298 | m_scene.RegionInfo.RegionSettings.SunVector = Position; | ||
299 | //m_scene.RegionInfo.EstateSettings.sunHour = GetLindenEstateHourFromCurrentTime(); | ||
300 | } | ||
301 | |||
302 | public void ForceSunUpdateToAllClients() | ||
303 | { | ||
304 | GenSunPos(); // Generate shared values once | ||
305 | |||
306 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
307 | foreach (ScenePresence avatar in avatars) | ||
308 | { | ||
309 | if (!avatar.IsChildAgent) | ||
310 | avatar.ControllingClient.SendSunPos(Position, Velocity, CurrentTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition); | ||
311 | } | ||
312 | |||
313 | // set estate settings for region access to sun position | ||
314 | m_scene.RegionInfo.RegionSettings.SunVector = Position; | ||
315 | m_scene.RegionInfo.RegionSettings.SunPosition = GetLindenEstateHourFromCurrentTime(); | ||
316 | } | ||
317 | |||
318 | /// <summary> | ||
319 | /// Calculate the sun's orbital position and its velocity. | ||
320 | /// </summary> | ||
321 | private void GenSunPos() | ||
322 | { | ||
323 | TotalDistanceTravelled = SunSpeed * CurrentTime; // distance measured in radians | ||
324 | OrbitalPosition = (float) (TotalDistanceTravelled%SunCycle); // position measured in radians | ||
325 | |||
326 | // TotalDistanceTravelled += HoursToRadians-(0.25*Math.PI)*Math.Cos(HoursToRadians)-OrbitalPosition; | ||
327 | // OrbitalPosition = (float) (TotalDistanceTravelled%SunCycle); | ||
328 | |||
329 | SeasonalOffset = SeasonSpeed * CurrentTime; // Present season determined as total radians travelled around season cycle | ||
330 | |||
331 | Tilt.W = (float) (AverageTilt + (SeasonalTilt*Math.Sin(SeasonalOffset))); // Calculate seasonal orbital N/S tilt | ||
332 | |||
333 | // m_log.Debug("[SUN] Total distance travelled = "+TotalDistanceTravelled+", present position = "+OrbitalPosition+"."); | ||
334 | // m_log.Debug("[SUN] Total seasonal progress = "+SeasonalOffset+", present tilt = "+Tilt.W+"."); | ||
335 | |||
336 | // The sun rotates about the Z axis | ||
337 | |||
338 | Position.X = (float) Math.Cos(-TotalDistanceTravelled); | ||
339 | Position.Y = (float) Math.Sin(-TotalDistanceTravelled); | ||
340 | Position.Z = 0; | ||
341 | |||
342 | // For interest we rotate it slightly about the X access. | ||
343 | // Celestial tilt is a value that ranges .025 | ||
344 | |||
345 | Position *= Tilt; | ||
346 | |||
347 | // Finally we shift the axis so that more of the | ||
348 | // circle is above the horizon than below. This | ||
349 | // makes the nights shorter than the days. | ||
350 | |||
351 | Position.Z = Position.Z + (float) HorizonShift; | ||
352 | Position = Vector3.Normalize(Position); | ||
353 | |||
354 | // m_log.Debug("[SUN] Position("+Position.X+","+Position.Y+","+Position.Z+")"); | ||
355 | |||
356 | Velocity.X = 0; | ||
357 | Velocity.Y = 0; | ||
358 | Velocity.Z = (float) SunSpeed; | ||
359 | |||
360 | // Correct angular velocity to reflect the seasonal rotation | ||
361 | |||
362 | Magnitude = Position.Length(); | ||
363 | if (sunFixed) | ||
364 | { | ||
365 | Velocity.X = 0; | ||
366 | Velocity.Y = 0; | ||
367 | Velocity.Z = 0; | ||
368 | return; | ||
369 | } | ||
370 | |||
371 | Velocity = (Velocity * Tilt) * (1.0f / Magnitude); | ||
372 | |||
373 | // m_log.Debug("[SUN] Velocity("+Velocity.X+","+Velocity.Y+","+Velocity.Z+")"); | ||
374 | } | ||
375 | |||
376 | private void ClientLoggedOut(UUID AgentId) | ||
377 | { | ||
378 | lock (m_rootAgents) | ||
379 | { | ||
380 | if (m_rootAgents.ContainsKey(AgentId)) | ||
381 | { | ||
382 | m_rootAgents.Remove(AgentId); | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | |||
387 | private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID) | ||
388 | { | ||
389 | lock (m_rootAgents) | ||
390 | { | ||
391 | if (m_rootAgents.ContainsKey(avatar.UUID)) | ||
392 | { | ||
393 | m_rootAgents[avatar.UUID] = avatar.RegionHandle; | ||
394 | } | ||
395 | else | ||
396 | { | ||
397 | m_rootAgents.Add(avatar.UUID, avatar.RegionHandle); | ||
398 | SunToClient(avatar.ControllingClient); | ||
399 | } | ||
400 | } | ||
401 | //m_log.Info("[FRIEND]: " + avatar.Name + " status:" + (!avatar.IsChildAgent).ToString()); | ||
402 | } | ||
403 | |||
404 | private void MakeChildAgent(ScenePresence avatar) | ||
405 | { | ||
406 | lock (m_rootAgents) | ||
407 | { | ||
408 | if (m_rootAgents.ContainsKey(avatar.UUID)) | ||
409 | { | ||
410 | if (m_rootAgents[avatar.UUID] == avatar.RegionHandle) | ||
411 | { | ||
412 | m_rootAgents.Remove(avatar.UUID); | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
418 | public void EstateToolsTimeUpdate(ulong regionHandle, bool FixedTime, bool useEstateTime, float LindenHour) | ||
419 | { | ||
420 | if (m_scene.RegionInfo.RegionHandle == regionHandle) | ||
421 | { | ||
422 | SetTimeByLindenHour(LindenHour); | ||
423 | |||
424 | //if (useEstateTime) | ||
425 | //LindenHourOffset = 0; | ||
426 | |||
427 | ForceSunUpdateToAllClients(); | ||
428 | sunFixed = FixedTime; | ||
429 | if (sunFixed) | ||
430 | GenSunPos(); | ||
431 | } | ||
432 | } | ||
433 | } | ||
434 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/DefaultEffects/ChannelDigger.cs b/OpenSim/Region/CoreModules/World/Terrain/DefaultEffects/ChannelDigger.cs new file mode 100644 index 0000000..f96ab88 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/DefaultEffects/ChannelDigger.cs | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.CoreModules.World.Terrain; | ||
31 | using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; | ||
32 | |||
33 | namespace OpenSim.Region.Modules.Terrain.Extensions.DefaultEffects.Effects | ||
34 | { | ||
35 | public class ChannelDigger : ITerrainEffect | ||
36 | { | ||
37 | private readonly int num_h = 4; | ||
38 | private readonly int num_w = 4; | ||
39 | |||
40 | private readonly ITerrainFloodEffect raiseFunction = new RaiseArea(); | ||
41 | private readonly ITerrainFloodEffect smoothFunction = new SmoothArea(); | ||
42 | |||
43 | #region ITerrainEffect Members | ||
44 | |||
45 | public void RunEffect(ITerrainChannel map) | ||
46 | { | ||
47 | FillMap(map, 15); | ||
48 | BuildTiles(map, 7); | ||
49 | SmoothMap(map, 3); | ||
50 | } | ||
51 | |||
52 | #endregion | ||
53 | |||
54 | private void SmoothMap(ITerrainChannel map, int rounds) | ||
55 | { | ||
56 | Boolean[,] bitmap = new bool[map.Width,map.Height]; | ||
57 | for (int x = 0; x < map.Width; x++) | ||
58 | { | ||
59 | for (int y = 0; y < map.Height; y++) | ||
60 | { | ||
61 | bitmap[x, y] = true; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | for (int i = 0; i < rounds; i++) | ||
66 | { | ||
67 | smoothFunction.FloodEffect(map, bitmap, 1.0); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | private void FillMap(ITerrainChannel map, double val) | ||
72 | { | ||
73 | for (int x = 0; x < map.Width; x++) | ||
74 | for (int y = 0; y < map.Height; y++) | ||
75 | map[x, y] = val; | ||
76 | } | ||
77 | |||
78 | private void BuildTiles(ITerrainChannel map, double height) | ||
79 | { | ||
80 | int channelWidth = (int) Math.Floor((map.Width / num_w) * 0.8); | ||
81 | int channelHeight = (int) Math.Floor((map.Height / num_h) * 0.8); | ||
82 | int channelXOffset = (map.Width / num_w) - channelWidth; | ||
83 | int channelYOffset = (map.Height / num_h) - channelHeight; | ||
84 | |||
85 | for (int x = 0; x < num_w; x++) | ||
86 | { | ||
87 | for (int y = 0; y < num_h; y++) | ||
88 | { | ||
89 | int xoff = ((channelXOffset + channelWidth) * x) + (channelXOffset / 2); | ||
90 | int yoff = ((channelYOffset + channelHeight) * y) + (channelYOffset / 2); | ||
91 | |||
92 | Boolean[,] bitmap = new bool[map.Width,map.Height]; | ||
93 | |||
94 | for (int dx = 0; dx < channelWidth; dx++) | ||
95 | { | ||
96 | for (int dy = 0; dy < channelHeight; dy++) | ||
97 | { | ||
98 | bitmap[dx + xoff, dy + yoff] = true; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | raiseFunction.FloodEffect(map, bitmap, height); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Effects/CookieCutter.cs b/OpenSim/Region/CoreModules/World/Terrain/Effects/CookieCutter.cs new file mode 100644 index 0000000..cb8112c --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Effects/CookieCutter.cs | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | using System; | ||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | using OpenSim.Region.Framework.Scenes; | ||
30 | using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.Effects | ||
33 | { | ||
34 | internal class CookieCutter : ITerrainEffect | ||
35 | { | ||
36 | #region ITerrainEffect Members | ||
37 | |||
38 | public void RunEffect(ITerrainChannel map) | ||
39 | { | ||
40 | ITerrainPaintableEffect eroder = new WeatherSphere(); | ||
41 | |||
42 | bool[,] cliffMask = new bool[map.Width,map.Height]; | ||
43 | bool[,] channelMask = new bool[map.Width,map.Height]; | ||
44 | bool[,] smoothMask = new bool[map.Width,map.Height]; | ||
45 | bool[,] allowMask = new bool[map.Width,map.Height]; | ||
46 | |||
47 | Console.WriteLine("S1"); | ||
48 | |||
49 | // Step one, generate rough mask | ||
50 | int x, y; | ||
51 | for (x = 0; x < map.Width; x++) | ||
52 | { | ||
53 | for (y = 0; y < map.Height; y++) | ||
54 | { | ||
55 | Console.Write("."); | ||
56 | smoothMask[x, y] = true; | ||
57 | allowMask[x,y] = true; | ||
58 | |||
59 | // Start underwater | ||
60 | map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 5; | ||
61 | // Add a little height. (terrain should now be above water, mostly.) | ||
62 | map[x, y] += 20; | ||
63 | |||
64 | const int channelsX = 4; | ||
65 | int channelWidth = (map.Width / channelsX / 4); | ||
66 | const int channelsY = 4; | ||
67 | int channelHeight = (map.Height / channelsY / 4); | ||
68 | |||
69 | SetLowerChannel(map, cliffMask, channelMask, x, y, channelsX, channelWidth, map.Width, x); | ||
70 | SetLowerChannel(map, cliffMask, channelMask, x, y, channelsY, channelHeight, map.Height, y); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | Console.WriteLine("S2"); | ||
75 | //smooth.FloodEffect(map, smoothMask, 4.0); | ||
76 | |||
77 | Console.WriteLine("S3"); | ||
78 | for (x = 0; x < map.Width; x++) | ||
79 | { | ||
80 | for (y = 0; y < map.Height; y++) | ||
81 | { | ||
82 | if (cliffMask[x, y]) | ||
83 | eroder.PaintEffect(map, allowMask, x, y, -1, 4, 0.1); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | for (x = 0; x < map.Width; x += 2) | ||
88 | { | ||
89 | for (y = 0; y < map.Height; y += 2) | ||
90 | { | ||
91 | if (map[x, y] < 0.1) | ||
92 | map[x, y] = 0.1; | ||
93 | if (map[x, y] > 256) | ||
94 | map[x, y] = 256; | ||
95 | } | ||
96 | } | ||
97 | //smooth.FloodEffect(map, smoothMask, 4.0); | ||
98 | } | ||
99 | |||
100 | #endregion | ||
101 | |||
102 | private static void SetLowerChannel(ITerrainChannel map, bool[,] cliffMask, bool[,] channelMask, int x, int y, int numChannels, int channelWidth, | ||
103 | int mapSize, int rp) | ||
104 | { | ||
105 | for (int i = 0; i < numChannels; i++) | ||
106 | { | ||
107 | double distanceToLine = Math.Abs(rp - ((mapSize / numChannels) * i)); | ||
108 | |||
109 | if (distanceToLine < channelWidth) | ||
110 | { | ||
111 | if (channelMask[x, y]) | ||
112 | return; | ||
113 | |||
114 | // Remove channels | ||
115 | map[x, y] -= 10; | ||
116 | channelMask[x, y] = true; | ||
117 | } | ||
118 | if (distanceToLine < 1) | ||
119 | { | ||
120 | cliffMask[x, y] = true; | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs b/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs new file mode 100644 index 0000000..da6ee12 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | using OpenSim.Framework; | ||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | using OpenSim.Region.Framework.Scenes; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.Effects | ||
32 | { | ||
33 | internal class DefaultTerrainGenerator : ITerrainEffect | ||
34 | { | ||
35 | #region ITerrainEffect Members | ||
36 | |||
37 | public void RunEffect(ITerrainChannel map) | ||
38 | { | ||
39 | int x, y; | ||
40 | for (x = 0; x < map.Width; x++) | ||
41 | { | ||
42 | for (y = 0; y < map.Height; y++) | ||
43 | { | ||
44 | map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 10; | ||
45 | double spherFac = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2, Constants.RegionSize / 2, 50) * 0.01; | ||
46 | if (map[x, y] < spherFac) | ||
47 | { | ||
48 | map[x, y] = spherFac; | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | #endregion | ||
55 | } | ||
56 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs new file mode 100644 index 0000000..4f395b5 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Drawing; | ||
29 | using System.Drawing.Imaging; | ||
30 | using System.IO; | ||
31 | using OpenSim.Region.Framework.Interfaces; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
34 | { | ||
35 | /// <summary> | ||
36 | /// A generic windows bitmap loader. | ||
37 | /// Should be capable of handling 24-bit RGB images. | ||
38 | /// | ||
39 | /// Uses the System.Drawing filesystem loader. | ||
40 | /// </summary> | ||
41 | internal class BMP : GenericSystemDrawing | ||
42 | { | ||
43 | /// <summary> | ||
44 | /// Exports a file to a image on the disk using a System.Drawing exporter. | ||
45 | /// </summary> | ||
46 | /// <param name="filename">The target filename</param> | ||
47 | /// <param name="map">The terrain channel being saved</param> | ||
48 | public override void SaveFile(string filename, ITerrainChannel map) | ||
49 | { | ||
50 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
51 | |||
52 | colours.Save(filename, ImageFormat.Bmp); | ||
53 | } | ||
54 | |||
55 | /// <summary> | ||
56 | /// Exports a stream using a System.Drawing exporter. | ||
57 | /// </summary> | ||
58 | /// <param name="stream">The target stream</param> | ||
59 | /// <param name="map">The terrain channel being saved</param> | ||
60 | public override void SaveStream(Stream stream, ITerrainChannel map) | ||
61 | { | ||
62 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
63 | |||
64 | colours.Save(stream, ImageFormat.Png); | ||
65 | } | ||
66 | |||
67 | /// <summary> | ||
68 | /// The human readable version of the file format(s) this loader handles | ||
69 | /// </summary> | ||
70 | /// <returns></returns> | ||
71 | public override string ToString() | ||
72 | { | ||
73 | return "BMP"; | ||
74 | } | ||
75 | } | ||
76 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs new file mode 100644 index 0000000..cff82d1 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Drawing; | ||
29 | using System.Drawing.Imaging; | ||
30 | using System.IO; | ||
31 | using OpenSim.Region.Framework.Interfaces; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
34 | { | ||
35 | internal class GIF : GenericSystemDrawing | ||
36 | { | ||
37 | public override void SaveFile(string filename, ITerrainChannel map) | ||
38 | { | ||
39 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
40 | |||
41 | colours.Save(filename, ImageFormat.Gif); | ||
42 | } | ||
43 | |||
44 | /// <summary> | ||
45 | /// Exports a stream using a System.Drawing exporter. | ||
46 | /// </summary> | ||
47 | /// <param name="stream">The target stream</param> | ||
48 | /// <param name="map">The terrain channel being saved</param> | ||
49 | public override void SaveStream(Stream stream, ITerrainChannel map) | ||
50 | { | ||
51 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
52 | |||
53 | colours.Save(stream, ImageFormat.Gif); | ||
54 | } | ||
55 | |||
56 | public override string ToString() | ||
57 | { | ||
58 | return "GIF"; | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs new file mode 100644 index 0000000..477c73c --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Drawing; | ||
30 | using System.Drawing.Imaging; | ||
31 | using System.IO; | ||
32 | using OpenSim.Region.Framework.Interfaces; | ||
33 | using OpenSim.Region.Framework.Scenes; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
36 | { | ||
37 | /// <summary> | ||
38 | /// A virtual class designed to have methods overloaded, | ||
39 | /// this class provides an interface for a generic image | ||
40 | /// saving and loading mechanism, but does not specify the | ||
41 | /// format. It should not be insubstantiated directly. | ||
42 | /// </summary> | ||
43 | public class GenericSystemDrawing : ITerrainLoader | ||
44 | { | ||
45 | #region ITerrainLoader Members | ||
46 | |||
47 | public string FileExtension | ||
48 | { | ||
49 | get { return ".gsd"; } | ||
50 | } | ||
51 | |||
52 | /// <summary> | ||
53 | /// Loads a file from a specified filename on the disk, | ||
54 | /// parses the image using the System.Drawing parsers | ||
55 | /// then returns a terrain channel. Values are | ||
56 | /// returned based on HSL brightness between 0m and 128m | ||
57 | /// </summary> | ||
58 | /// <param name="filename">The target image to load</param> | ||
59 | /// <returns>A terrain channel generated from the image.</returns> | ||
60 | public virtual ITerrainChannel LoadFile(string filename) | ||
61 | { | ||
62 | return LoadBitmap(new Bitmap(filename)); | ||
63 | } | ||
64 | |||
65 | public ITerrainChannel LoadFile(string filename, int x, int y, int fileWidth, int fileHeight, int w, int h) | ||
66 | { | ||
67 | throw new NotImplementedException(); | ||
68 | } | ||
69 | |||
70 | public virtual ITerrainChannel LoadStream(Stream stream) | ||
71 | { | ||
72 | return LoadBitmap(new Bitmap(stream)); | ||
73 | } | ||
74 | |||
75 | protected virtual ITerrainChannel LoadBitmap(Bitmap bitmap) | ||
76 | { | ||
77 | ITerrainChannel retval = new TerrainChannel(bitmap.Width, bitmap.Height); | ||
78 | |||
79 | int x; | ||
80 | for (x = 0; x < bitmap.Width; x++) | ||
81 | { | ||
82 | int y; | ||
83 | for (y = 0; y < bitmap.Height; y++) | ||
84 | { | ||
85 | retval[x, y] = bitmap.GetPixel(x, bitmap.Height - y - 1).GetBrightness() * 128; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | return retval; | ||
90 | } | ||
91 | |||
92 | /// <summary> | ||
93 | /// Exports a file to a image on the disk using a System.Drawing exporter. | ||
94 | /// </summary> | ||
95 | /// <param name="filename">The target filename</param> | ||
96 | /// <param name="map">The terrain channel being saved</param> | ||
97 | public virtual void SaveFile(string filename, ITerrainChannel map) | ||
98 | { | ||
99 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
100 | |||
101 | colours.Save(filename, ImageFormat.Png); | ||
102 | } | ||
103 | |||
104 | /// <summary> | ||
105 | /// Exports a stream using a System.Drawing exporter. | ||
106 | /// </summary> | ||
107 | /// <param name="stream">The target stream</param> | ||
108 | /// <param name="map">The terrain channel being saved</param> | ||
109 | public virtual void SaveStream(Stream stream, ITerrainChannel map) | ||
110 | { | ||
111 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
112 | |||
113 | colours.Save(stream, ImageFormat.Png); | ||
114 | } | ||
115 | |||
116 | #endregion | ||
117 | |||
118 | public override string ToString() | ||
119 | { | ||
120 | return "SYS.DRAWING"; | ||
121 | } | ||
122 | |||
123 | /// <summary> | ||
124 | /// Protected method, generates a grayscale bitmap | ||
125 | /// image from a specified terrain channel. | ||
126 | /// </summary> | ||
127 | /// <param name="map">The terrain channel to export to bitmap</param> | ||
128 | /// <returns>A System.Drawing.Bitmap containing a grayscale image</returns> | ||
129 | protected static Bitmap CreateGrayscaleBitmapFromMap(ITerrainChannel map) | ||
130 | { | ||
131 | Bitmap bmp = new Bitmap(map.Width, map.Height); | ||
132 | |||
133 | const int pallete = 256; | ||
134 | |||
135 | Color[] grays = new Color[pallete]; | ||
136 | for (int i = 0; i < grays.Length; i++) | ||
137 | { | ||
138 | grays[i] = Color.FromArgb(i, i, i); | ||
139 | } | ||
140 | |||
141 | for (int y = 0; y < map.Height; y++) | ||
142 | { | ||
143 | for (int x = 0; x < map.Width; x++) | ||
144 | { | ||
145 | // 512 is the largest possible height before colours clamp | ||
146 | int colorindex = (int) (Math.Max(Math.Min(1.0, map[x, y] / 128.0), 0.0) * (pallete - 1)); | ||
147 | |||
148 | // Handle error conditions | ||
149 | if (colorindex > pallete - 1 || colorindex < 0) | ||
150 | bmp.SetPixel(x, map.Height - y - 1, Color.Red); | ||
151 | else | ||
152 | bmp.SetPixel(x, map.Height - y - 1, grays[colorindex]); | ||
153 | } | ||
154 | } | ||
155 | return bmp; | ||
156 | } | ||
157 | |||
158 | /// <summary> | ||
159 | /// Protected method, generates a coloured bitmap | ||
160 | /// image from a specified terrain channel. | ||
161 | /// </summary> | ||
162 | /// <param name="map">The terrain channel to export to bitmap</param> | ||
163 | /// <returns>A System.Drawing.Bitmap containing a coloured image</returns> | ||
164 | protected static Bitmap CreateBitmapFromMap(ITerrainChannel map) | ||
165 | { | ||
166 | Bitmap gradientmapLd = new Bitmap("defaultstripe.png"); | ||
167 | |||
168 | int pallete = gradientmapLd.Height; | ||
169 | |||
170 | Bitmap bmp = new Bitmap(map.Width, map.Height); | ||
171 | Color[] colours = new Color[pallete]; | ||
172 | |||
173 | for (int i = 0; i < pallete; i++) | ||
174 | { | ||
175 | colours[i] = gradientmapLd.GetPixel(0, i); | ||
176 | } | ||
177 | |||
178 | for (int y = 0; y < map.Height; y++) | ||
179 | { | ||
180 | for (int x = 0; x < map.Width; x++) | ||
181 | { | ||
182 | // 512 is the largest possible height before colours clamp | ||
183 | int colorindex = (int) (Math.Max(Math.Min(1.0, map[x, y] / 512.0), 0.0) * (pallete - 1)); | ||
184 | |||
185 | // Handle error conditions | ||
186 | if (colorindex > pallete - 1 || colorindex < 0) | ||
187 | bmp.SetPixel(x, map.Height - y - 1, Color.Red); | ||
188 | else | ||
189 | bmp.SetPixel(x, map.Height - y - 1, colours[colorindex]); | ||
190 | } | ||
191 | } | ||
192 | return bmp; | ||
193 | } | ||
194 | } | ||
195 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs new file mode 100644 index 0000000..f8e31f8 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Drawing; | ||
30 | using System.Drawing.Imaging; | ||
31 | using System.IO; | ||
32 | using OpenSim.Region.Framework.Interfaces; | ||
33 | |||
34 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
35 | { | ||
36 | public class JPEG : ITerrainLoader | ||
37 | { | ||
38 | #region ITerrainLoader Members | ||
39 | |||
40 | public string FileExtension | ||
41 | { | ||
42 | get { return ".jpg"; } | ||
43 | } | ||
44 | |||
45 | public ITerrainChannel LoadFile(string filename) | ||
46 | { | ||
47 | throw new NotImplementedException(); | ||
48 | } | ||
49 | |||
50 | public ITerrainChannel LoadFile(string filename, int x, int y, int fileWidth, int fileHeight, int w, int h) | ||
51 | { | ||
52 | throw new NotImplementedException(); | ||
53 | } | ||
54 | |||
55 | public ITerrainChannel LoadStream(Stream stream) | ||
56 | { | ||
57 | throw new NotImplementedException(); | ||
58 | } | ||
59 | |||
60 | public void SaveFile(string filename, ITerrainChannel map) | ||
61 | { | ||
62 | Bitmap colours = CreateBitmapFromMap(map); | ||
63 | |||
64 | colours.Save(filename, ImageFormat.Jpeg); | ||
65 | } | ||
66 | |||
67 | /// <summary> | ||
68 | /// Exports a stream using a System.Drawing exporter. | ||
69 | /// </summary> | ||
70 | /// <param name="stream">The target stream</param> | ||
71 | /// <param name="map">The terrain channel being saved</param> | ||
72 | public void SaveStream(Stream stream, ITerrainChannel map) | ||
73 | { | ||
74 | Bitmap colours = CreateBitmapFromMap(map); | ||
75 | |||
76 | colours.Save(stream, ImageFormat.Jpeg); | ||
77 | } | ||
78 | |||
79 | #endregion | ||
80 | |||
81 | public override string ToString() | ||
82 | { | ||
83 | return "JPEG"; | ||
84 | } | ||
85 | |||
86 | private static Bitmap CreateBitmapFromMap(ITerrainChannel map) | ||
87 | { | ||
88 | Bitmap gradientmapLd = new Bitmap("defaultstripe.png"); | ||
89 | |||
90 | int pallete = gradientmapLd.Height; | ||
91 | |||
92 | Bitmap bmp = new Bitmap(map.Width, map.Height); | ||
93 | Color[] colours = new Color[pallete]; | ||
94 | |||
95 | for (int i = 0; i < pallete; i++) | ||
96 | { | ||
97 | colours[i] = gradientmapLd.GetPixel(0, i); | ||
98 | } | ||
99 | |||
100 | for (int y = 0; y < map.Height; y++) | ||
101 | { | ||
102 | for (int x = 0; x < map.Width; x++) | ||
103 | { | ||
104 | // 512 is the largest possible height before colours clamp | ||
105 | int colorindex = (int) (Math.Max(Math.Min(1.0, map[x, y] / 512.0), 0.0) * (pallete - 1)); | ||
106 | bmp.SetPixel(x, map.Height - y - 1, colours[colorindex]); | ||
107 | } | ||
108 | } | ||
109 | return bmp; | ||
110 | } | ||
111 | } | ||
112 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs new file mode 100644 index 0000000..a86ae00 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs | |||
@@ -0,0 +1,250 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using OpenSim.Region.Framework.Interfaces; | ||
31 | using OpenSim.Region.Framework.Scenes; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
34 | { | ||
35 | public class LLRAW : ITerrainLoader | ||
36 | { | ||
37 | public struct HeightmapLookupValue : IComparable<HeightmapLookupValue> | ||
38 | { | ||
39 | public int Index; | ||
40 | public double Value; | ||
41 | |||
42 | public HeightmapLookupValue(int index, double value) | ||
43 | { | ||
44 | Index = index; | ||
45 | Value = value; | ||
46 | } | ||
47 | |||
48 | public int CompareTo(HeightmapLookupValue val) | ||
49 | { | ||
50 | return Value.CompareTo(val.Value); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /// <summary>Lookup table to speed up terrain exports</summary> | ||
55 | HeightmapLookupValue[] LookupHeightTable; | ||
56 | |||
57 | public LLRAW() | ||
58 | { | ||
59 | LookupHeightTable = new HeightmapLookupValue[256 * 256]; | ||
60 | |||
61 | for (int i = 0; i < 256; i++) | ||
62 | { | ||
63 | for (int j = 0; j < 256; j++) | ||
64 | { | ||
65 | LookupHeightTable[i + (j * 256)] = new HeightmapLookupValue(i + (j * 256), ((double)i * ((double)j / 128.0d))); | ||
66 | } | ||
67 | } | ||
68 | Array.Sort<HeightmapLookupValue>(LookupHeightTable); | ||
69 | } | ||
70 | |||
71 | #region ITerrainLoader Members | ||
72 | |||
73 | public ITerrainChannel LoadFile(string filename) | ||
74 | { | ||
75 | FileInfo file = new FileInfo(filename); | ||
76 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
77 | ITerrainChannel retval = LoadStream(s); | ||
78 | |||
79 | s.Close(); | ||
80 | |||
81 | return retval; | ||
82 | } | ||
83 | |||
84 | public ITerrainChannel LoadFile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight) | ||
85 | { | ||
86 | TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight); | ||
87 | |||
88 | FileInfo file = new FileInfo(filename); | ||
89 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
90 | BinaryReader bs = new BinaryReader(s); | ||
91 | |||
92 | int currFileYOffset = fileHeight - 1; | ||
93 | |||
94 | // if our region isn't on the first Y section of the areas to be landscaped, then | ||
95 | // advance to our section of the file | ||
96 | while (currFileYOffset > offsetY) | ||
97 | { | ||
98 | // read a whole strip of regions | ||
99 | int heightsToRead = sectionHeight * (fileWidth * sectionWidth); | ||
100 | bs.ReadBytes(heightsToRead * 13); // because there are 13 fun channels | ||
101 | currFileYOffset--; | ||
102 | } | ||
103 | |||
104 | // got to the Y start offset within the file of our region | ||
105 | // so read the file bits associated with our region | ||
106 | int y; | ||
107 | // for each Y within our Y offset | ||
108 | for (y = sectionHeight - 1; y >= 0; y--) | ||
109 | { | ||
110 | int currFileXOffset = 0; | ||
111 | |||
112 | // if our region isn't the first X section of the areas to be landscaped, then | ||
113 | // advance the stream to the X start pos of our section in the file | ||
114 | // i.e. eat X upto where we start | ||
115 | while (currFileXOffset < offsetX) | ||
116 | { | ||
117 | bs.ReadBytes(sectionWidth * 13); | ||
118 | currFileXOffset++; | ||
119 | } | ||
120 | |||
121 | // got to our X offset, so write our regions X line | ||
122 | int x; | ||
123 | for (x = 0; x < sectionWidth; x++) | ||
124 | { | ||
125 | // Read a strip and continue | ||
126 | retval[x, y] = bs.ReadByte() * (bs.ReadByte() / 128.0); | ||
127 | bs.ReadBytes(11); | ||
128 | } | ||
129 | // record that we wrote it | ||
130 | currFileXOffset++; | ||
131 | |||
132 | // if our region isn't the last X section of the areas to be landscaped, then | ||
133 | // advance the stream to the end of this Y column | ||
134 | while (currFileXOffset < fileWidth) | ||
135 | { | ||
136 | // eat the next regions x line | ||
137 | bs.ReadBytes(sectionWidth * 13); //The 13 channels again | ||
138 | currFileXOffset++; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | bs.Close(); | ||
143 | s.Close(); | ||
144 | |||
145 | return retval; | ||
146 | } | ||
147 | |||
148 | public ITerrainChannel LoadStream(Stream s) | ||
149 | { | ||
150 | TerrainChannel retval = new TerrainChannel(); | ||
151 | |||
152 | BinaryReader bs = new BinaryReader(s); | ||
153 | int y; | ||
154 | for (y = 0; y < retval.Height; y++) | ||
155 | { | ||
156 | int x; | ||
157 | for (x = 0; x < retval.Width; x++) | ||
158 | { | ||
159 | retval[x, (retval.Height - 1) - y] = bs.ReadByte() * (bs.ReadByte() / 128.0); | ||
160 | bs.ReadBytes(11); // Advance the stream to next bytes. | ||
161 | } | ||
162 | } | ||
163 | |||
164 | bs.Close(); | ||
165 | |||
166 | return retval; | ||
167 | } | ||
168 | |||
169 | public void SaveFile(string filename, ITerrainChannel map) | ||
170 | { | ||
171 | FileInfo file = new FileInfo(filename); | ||
172 | FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write); | ||
173 | SaveStream(s, map); | ||
174 | |||
175 | s.Close(); | ||
176 | } | ||
177 | |||
178 | public void SaveStream(Stream s, ITerrainChannel map) | ||
179 | { | ||
180 | BinaryWriter binStream = new BinaryWriter(s); | ||
181 | |||
182 | // Output the calculated raw | ||
183 | for (int y = 0; y < map.Height; y++) | ||
184 | { | ||
185 | for (int x = 0; x < map.Width; x++) | ||
186 | { | ||
187 | double t = map[x, (map.Height - 1) - y]; | ||
188 | //if height is less than 0, set it to 0 as | ||
189 | //can't save -ve values in a LLRAW file | ||
190 | if (t < 0d) | ||
191 | { | ||
192 | t = 0d; | ||
193 | } | ||
194 | |||
195 | int index = 0; | ||
196 | |||
197 | // The lookup table is pre-sorted, so we either find an exact match or | ||
198 | // the next closest (smaller) match with a binary search | ||
199 | index = Array.BinarySearch<HeightmapLookupValue>(LookupHeightTable, new HeightmapLookupValue(0, t)); | ||
200 | if (index < 0) | ||
201 | index = ~index - 1; | ||
202 | |||
203 | index = LookupHeightTable[index].Index; | ||
204 | |||
205 | byte red = (byte) (index & 0xFF); | ||
206 | byte green = (byte) ((index >> 8) & 0xFF); | ||
207 | const byte blue = 20; | ||
208 | const byte alpha1 = 0; | ||
209 | const byte alpha2 = 0; | ||
210 | const byte alpha3 = 0; | ||
211 | const byte alpha4 = 0; | ||
212 | const byte alpha5 = 255; | ||
213 | const byte alpha6 = 255; | ||
214 | const byte alpha7 = 255; | ||
215 | const byte alpha8 = 255; | ||
216 | byte alpha9 = red; | ||
217 | byte alpha10 = green; | ||
218 | |||
219 | binStream.Write(red); | ||
220 | binStream.Write(green); | ||
221 | binStream.Write(blue); | ||
222 | binStream.Write(alpha1); | ||
223 | binStream.Write(alpha2); | ||
224 | binStream.Write(alpha3); | ||
225 | binStream.Write(alpha4); | ||
226 | binStream.Write(alpha5); | ||
227 | binStream.Write(alpha6); | ||
228 | binStream.Write(alpha7); | ||
229 | binStream.Write(alpha8); | ||
230 | binStream.Write(alpha9); | ||
231 | binStream.Write(alpha10); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | binStream.Close(); | ||
236 | } | ||
237 | |||
238 | public string FileExtension | ||
239 | { | ||
240 | get { return ".raw"; } | ||
241 | } | ||
242 | |||
243 | #endregion | ||
244 | |||
245 | public override string ToString() | ||
246 | { | ||
247 | return "LL/SL RAW"; | ||
248 | } | ||
249 | } | ||
250 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs new file mode 100644 index 0000000..0dea282 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Drawing; | ||
29 | using System.Drawing.Imaging; | ||
30 | using System.IO; | ||
31 | using OpenSim.Region.Framework.Interfaces; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
34 | { | ||
35 | internal class PNG : GenericSystemDrawing | ||
36 | { | ||
37 | public override void SaveFile(string filename, ITerrainChannel map) | ||
38 | { | ||
39 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
40 | |||
41 | colours.Save(filename, ImageFormat.Png); | ||
42 | } | ||
43 | |||
44 | /// <summary> | ||
45 | /// Exports a stream using a System.Drawing exporter. | ||
46 | /// </summary> | ||
47 | /// <param name="stream">The target stream</param> | ||
48 | /// <param name="map">The terrain channel being saved</param> | ||
49 | public override void SaveStream(Stream stream, ITerrainChannel map) | ||
50 | { | ||
51 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
52 | |||
53 | colours.Save(stream, ImageFormat.Png); | ||
54 | } | ||
55 | |||
56 | public override string ToString() | ||
57 | { | ||
58 | return "PNG"; | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs new file mode 100644 index 0000000..178104f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.IO; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
33 | { | ||
34 | public class RAW32 : ITerrainLoader | ||
35 | { | ||
36 | #region ITerrainLoader Members | ||
37 | |||
38 | public string FileExtension | ||
39 | { | ||
40 | get { return ".r32"; } | ||
41 | } | ||
42 | |||
43 | public ITerrainChannel LoadFile(string filename) | ||
44 | { | ||
45 | FileInfo file = new FileInfo(filename); | ||
46 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
47 | ITerrainChannel retval = LoadStream(s); | ||
48 | |||
49 | s.Close(); | ||
50 | |||
51 | return retval; | ||
52 | } | ||
53 | |||
54 | public ITerrainChannel LoadFile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight) | ||
55 | { | ||
56 | TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight); | ||
57 | |||
58 | FileInfo file = new FileInfo(filename); | ||
59 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
60 | BinaryReader bs = new BinaryReader(s); | ||
61 | |||
62 | int currFileYOffset = 0; | ||
63 | |||
64 | // if our region isn't on the first Y section of the areas to be landscaped, then | ||
65 | // advance to our section of the file | ||
66 | while (currFileYOffset < offsetY) | ||
67 | { | ||
68 | // read a whole strip of regions | ||
69 | int heightsToRead = sectionHeight * (fileWidth * sectionWidth); | ||
70 | bs.ReadBytes(heightsToRead * 4); // because the floats are 4 bytes in the file | ||
71 | currFileYOffset++; | ||
72 | } | ||
73 | |||
74 | // got to the Y start offset within the file of our region | ||
75 | // so read the file bits associated with our region | ||
76 | int y; | ||
77 | // for each Y within our Y offset | ||
78 | for (y = 0; y < sectionHeight; y++) | ||
79 | { | ||
80 | int currFileXOffset = 0; | ||
81 | |||
82 | // if our region isn't the first X section of the areas to be landscaped, then | ||
83 | // advance the stream to the X start pos of our section in the file | ||
84 | // i.e. eat X upto where we start | ||
85 | while (currFileXOffset < offsetX) | ||
86 | { | ||
87 | bs.ReadBytes(sectionWidth * 4); // 4 bytes = single | ||
88 | currFileXOffset++; | ||
89 | } | ||
90 | |||
91 | // got to our X offset, so write our regions X line | ||
92 | int x; | ||
93 | for (x = 0; x < sectionWidth; x++) | ||
94 | { | ||
95 | // Read a strip and continue | ||
96 | retval[x, y] = bs.ReadSingle(); | ||
97 | } | ||
98 | // record that we wrote it | ||
99 | currFileXOffset++; | ||
100 | |||
101 | // if our region isn't the last X section of the areas to be landscaped, then | ||
102 | // advance the stream to the end of this Y column | ||
103 | while (currFileXOffset < fileWidth) | ||
104 | { | ||
105 | // eat the next regions x line | ||
106 | bs.ReadBytes(sectionWidth * 4); // 4 bytes = single | ||
107 | currFileXOffset++; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | bs.Close(); | ||
112 | s.Close(); | ||
113 | |||
114 | return retval; | ||
115 | } | ||
116 | |||
117 | public ITerrainChannel LoadStream(Stream s) | ||
118 | { | ||
119 | TerrainChannel retval = new TerrainChannel(); | ||
120 | |||
121 | BinaryReader bs = new BinaryReader(s); | ||
122 | int y; | ||
123 | for (y = 0; y < retval.Height; y++) | ||
124 | { | ||
125 | int x; | ||
126 | for (x = 0; x < retval.Width; x++) | ||
127 | { | ||
128 | retval[x, y] = bs.ReadSingle(); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | bs.Close(); | ||
133 | |||
134 | return retval; | ||
135 | } | ||
136 | |||
137 | public void SaveFile(string filename, ITerrainChannel map) | ||
138 | { | ||
139 | FileInfo file = new FileInfo(filename); | ||
140 | FileStream s = file.Open(FileMode.Create, FileAccess.Write); | ||
141 | SaveStream(s, map); | ||
142 | |||
143 | s.Close(); | ||
144 | } | ||
145 | |||
146 | public void SaveStream(Stream s, ITerrainChannel map) | ||
147 | { | ||
148 | BinaryWriter bs = new BinaryWriter(s); | ||
149 | |||
150 | int y; | ||
151 | for (y = 0; y < map.Height; y++) | ||
152 | { | ||
153 | int x; | ||
154 | for (x = 0; x < map.Width; x++) | ||
155 | { | ||
156 | bs.Write((float) map[x, y]); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | bs.Close(); | ||
161 | } | ||
162 | |||
163 | #endregion | ||
164 | |||
165 | public override string ToString() | ||
166 | { | ||
167 | return "RAW32"; | ||
168 | } | ||
169 | } | ||
170 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs new file mode 100644 index 0000000..220431f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Drawing; | ||
29 | using System.Drawing.Imaging; | ||
30 | using System.IO; | ||
31 | using OpenSim.Region.Framework.Interfaces; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
34 | { | ||
35 | internal class TIFF : GenericSystemDrawing | ||
36 | { | ||
37 | public override void SaveFile(string filename, ITerrainChannel map) | ||
38 | { | ||
39 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
40 | |||
41 | colours.Save(filename, ImageFormat.Tiff); | ||
42 | } | ||
43 | |||
44 | /// <summary> | ||
45 | /// Exports a stream using a System.Drawing exporter. | ||
46 | /// </summary> | ||
47 | /// <param name="stream">The target stream</param> | ||
48 | /// <param name="map">The terrain channel being saved</param> | ||
49 | public override void SaveStream(Stream stream, ITerrainChannel map) | ||
50 | { | ||
51 | Bitmap colours = CreateGrayscaleBitmapFromMap(map); | ||
52 | |||
53 | colours.Save(stream, ImageFormat.Tiff); | ||
54 | } | ||
55 | |||
56 | public override string ToString() | ||
57 | { | ||
58 | return "TIFF"; | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs new file mode 100644 index 0000000..426708d --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Text; | ||
31 | using OpenSim.Region.Framework.Interfaces; | ||
32 | using OpenSim.Region.Framework.Scenes; | ||
33 | |||
34 | namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | ||
35 | { | ||
36 | /// <summary> | ||
37 | /// Terragen File Format Loader | ||
38 | /// Built from specification at | ||
39 | /// http://www.planetside.co.uk/terragen/dev/tgterrain.html | ||
40 | /// </summary> | ||
41 | internal class Terragen : ITerrainLoader | ||
42 | { | ||
43 | #region ITerrainLoader Members | ||
44 | |||
45 | public ITerrainChannel LoadFile(string filename) | ||
46 | { | ||
47 | FileInfo file = new FileInfo(filename); | ||
48 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
49 | ITerrainChannel retval = LoadStream(s); | ||
50 | |||
51 | s.Close(); | ||
52 | |||
53 | return retval; | ||
54 | } | ||
55 | |||
56 | public ITerrainChannel LoadStream(Stream s) | ||
57 | { | ||
58 | TerrainChannel retval = new TerrainChannel(); | ||
59 | |||
60 | BinaryReader bs = new BinaryReader(s); | ||
61 | |||
62 | bool eof = false; | ||
63 | if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ") | ||
64 | { | ||
65 | int w = 256; | ||
66 | int h = 256; | ||
67 | |||
68 | // Terragen file | ||
69 | while (eof == false) | ||
70 | { | ||
71 | string tmp = Encoding.ASCII.GetString(bs.ReadBytes(4)); | ||
72 | switch (tmp) | ||
73 | { | ||
74 | case "SIZE": | ||
75 | int sztmp = bs.ReadInt16() + 1; | ||
76 | w = sztmp; | ||
77 | h = sztmp; | ||
78 | bs.ReadInt16(); | ||
79 | break; | ||
80 | case "XPTS": | ||
81 | w = bs.ReadInt16(); | ||
82 | bs.ReadInt16(); | ||
83 | break; | ||
84 | case "YPTS": | ||
85 | h = bs.ReadInt16(); | ||
86 | bs.ReadInt16(); | ||
87 | break; | ||
88 | case "ALTW": | ||
89 | eof = true; | ||
90 | Int16 heightScale = bs.ReadInt16(); | ||
91 | Int16 baseHeight = bs.ReadInt16(); | ||
92 | retval = new TerrainChannel(w, h); | ||
93 | int x; | ||
94 | for (x = 0; x < w; x++) | ||
95 | { | ||
96 | int y; | ||
97 | for (y = 0; y < h; y++) | ||
98 | { | ||
99 | retval[x, y] = baseHeight + bs.ReadInt16() * (double) heightScale / 65536.0; | ||
100 | } | ||
101 | } | ||
102 | break; | ||
103 | default: | ||
104 | bs.ReadInt32(); | ||
105 | break; | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | bs.Close(); | ||
111 | |||
112 | return retval; | ||
113 | } | ||
114 | |||
115 | public void SaveFile(string filename, ITerrainChannel map) | ||
116 | { | ||
117 | throw new NotImplementedException(); | ||
118 | } | ||
119 | |||
120 | public void SaveStream(Stream stream, ITerrainChannel map) | ||
121 | { | ||
122 | throw new NotImplementedException(); | ||
123 | } | ||
124 | |||
125 | public string FileExtension | ||
126 | { | ||
127 | get { return ".ter"; } | ||
128 | } | ||
129 | |||
130 | public ITerrainChannel LoadFile(string filename, int x, int y, int fileWidth, int fileHeight, int w, int h) | ||
131 | { | ||
132 | throw new NotImplementedException(); | ||
133 | } | ||
134 | |||
135 | #endregion | ||
136 | |||
137 | public override string ToString() | ||
138 | { | ||
139 | return "Terragen"; | ||
140 | } | ||
141 | } | ||
142 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/FlattenArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/FlattenArea.cs new file mode 100644 index 0000000..fe79c0b --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/FlattenArea.cs | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | ||
31 | { | ||
32 | public class FlattenArea : ITerrainFloodEffect | ||
33 | { | ||
34 | #region ITerrainFloodEffect Members | ||
35 | |||
36 | public void FloodEffect(ITerrainChannel map, bool[,] fillArea, double strength) | ||
37 | { | ||
38 | double sum = 0.0; | ||
39 | double steps = 0.0; | ||
40 | |||
41 | int x, y; | ||
42 | for (x = 0; x < map.Width; x++) | ||
43 | { | ||
44 | for (y = 0; y < map.Height; y++) | ||
45 | { | ||
46 | if (fillArea[x, y]) | ||
47 | { | ||
48 | sum += map[x, y]; | ||
49 | steps += 1.0; | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | double avg = sum / steps; | ||
55 | |||
56 | double str = 0.1 * strength; // == 0.2 in the default client | ||
57 | |||
58 | for (x = 0; x < map.Width; x++) | ||
59 | { | ||
60 | for (y = 0; y < map.Height; y++) | ||
61 | { | ||
62 | if (fillArea[x, y]) | ||
63 | map[x, y] = (map[x, y] * (1.0 - str)) + (avg * str); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | #endregion | ||
69 | } | ||
70 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/LowerArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/LowerArea.cs new file mode 100644 index 0000000..5c0aace --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/LowerArea.cs | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | ||
31 | { | ||
32 | public class LowerArea : ITerrainFloodEffect | ||
33 | { | ||
34 | #region ITerrainFloodEffect Members | ||
35 | |||
36 | public void FloodEffect(ITerrainChannel map, bool[,] fillArea, double strength) | ||
37 | { | ||
38 | int x; | ||
39 | for (x = 0; x < map.Width; x++) | ||
40 | { | ||
41 | int y; | ||
42 | for (y = 0; y < map.Height; y++) | ||
43 | { | ||
44 | if (fillArea[x, y]) | ||
45 | { | ||
46 | map[x, y] -= strength; | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | #endregion | ||
53 | } | ||
54 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs new file mode 100644 index 0000000..02f2b53 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Framework; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | ||
33 | { | ||
34 | public class NoiseArea : ITerrainFloodEffect | ||
35 | { | ||
36 | #region ITerrainFloodEffect Members | ||
37 | |||
38 | public void FloodEffect(ITerrainChannel map, bool[,] fillArea, double strength) | ||
39 | { | ||
40 | int x; | ||
41 | for (x = 0; x < map.Width; x++) | ||
42 | { | ||
43 | int y; | ||
44 | for (y = 0; y < map.Height; y++) | ||
45 | { | ||
46 | if (fillArea[x, y]) | ||
47 | { | ||
48 | double noise = TerrainUtil.PerlinNoise2D((double) x / Constants.RegionSize, (double) y / Constants.RegionSize, 8, 1.0); | ||
49 | |||
50 | map[x, y] += noise * strength; | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | #endregion | ||
57 | } | ||
58 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/RaiseArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/RaiseArea.cs new file mode 100644 index 0000000..768b31f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/RaiseArea.cs | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | ||
31 | { | ||
32 | public class RaiseArea : ITerrainFloodEffect | ||
33 | { | ||
34 | #region ITerrainFloodEffect Members | ||
35 | |||
36 | public void FloodEffect(ITerrainChannel map, bool[,] fillArea, double strength) | ||
37 | { | ||
38 | int x; | ||
39 | for (x = 0; x < map.Width; x++) | ||
40 | { | ||
41 | int y; | ||
42 | for (y = 0; y < map.Height; y++) | ||
43 | { | ||
44 | if (fillArea[x, y]) | ||
45 | { | ||
46 | map[x, y] += strength; | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | #endregion | ||
53 | } | ||
54 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/RevertArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/RevertArea.cs new file mode 100644 index 0000000..66b9055 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/RevertArea.cs | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | ||
31 | { | ||
32 | public class RevertArea : ITerrainFloodEffect | ||
33 | { | ||
34 | private readonly ITerrainChannel m_revertmap; | ||
35 | |||
36 | public RevertArea(ITerrainChannel revertmap) | ||
37 | { | ||
38 | m_revertmap = revertmap; | ||
39 | } | ||
40 | |||
41 | #region ITerrainFloodEffect Members | ||
42 | |||
43 | /// <summary> | ||
44 | /// reverts an area of the map to the heightfield stored in the revertmap | ||
45 | /// </summary> | ||
46 | /// <param name="map">the current heightmap</param> | ||
47 | /// <param name="fillArea">array indicating which sections of the map are to be reverted</param> | ||
48 | /// <param name="strength">unused</param> | ||
49 | public void FloodEffect(ITerrainChannel map, bool[,] fillArea, double strength) | ||
50 | { | ||
51 | int x; | ||
52 | for (x = 0; x < map.Width; x++) | ||
53 | { | ||
54 | int y; | ||
55 | for (y = 0; y < map.Height; y++) | ||
56 | { | ||
57 | if (fillArea[x, y]) | ||
58 | { | ||
59 | map[x, y] = m_revertmap[x, y]; | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | #endregion | ||
66 | } | ||
67 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/SmoothArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/SmoothArea.cs new file mode 100644 index 0000000..a75dde1 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/SmoothArea.cs | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | ||
31 | { | ||
32 | public class SmoothArea : ITerrainFloodEffect | ||
33 | { | ||
34 | #region ITerrainFloodEffect Members | ||
35 | |||
36 | public void FloodEffect(ITerrainChannel map, bool[,] fillArea, double strength) | ||
37 | { | ||
38 | double area = strength; | ||
39 | double step = strength / 4.0; | ||
40 | |||
41 | double[,] manipulate = new double[map.Width,map.Height]; | ||
42 | int x, y; | ||
43 | for (x = 0; x < map.Width; x++) | ||
44 | { | ||
45 | for (y = 0; y < map.Height; y++) | ||
46 | { | ||
47 | if (!fillArea[x, y]) | ||
48 | continue; | ||
49 | |||
50 | double average = 0.0; | ||
51 | int avgsteps = 0; | ||
52 | |||
53 | double n; | ||
54 | for (n = 0.0 - area; n < area; n += step) | ||
55 | { | ||
56 | double l; | ||
57 | for (l = 0.0 - area; l < area; l += step) | ||
58 | { | ||
59 | avgsteps++; | ||
60 | average += GetBilinearInterpolate(x + n, y + l, map); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | manipulate[x, y] = average / avgsteps; | ||
65 | } | ||
66 | } | ||
67 | for (x = 0; x < map.Width; x++) | ||
68 | { | ||
69 | for (y = 0; y < map.Height; y++) | ||
70 | { | ||
71 | if (!fillArea[x, y]) | ||
72 | continue; | ||
73 | |||
74 | map[x, y] = manipulate[x, y]; | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | #endregion | ||
80 | |||
81 | private static double GetBilinearInterpolate(double x, double y, ITerrainChannel map) | ||
82 | { | ||
83 | int w = map.Width; | ||
84 | int h = map.Height; | ||
85 | |||
86 | if (x > w - 2.0) | ||
87 | x = w - 2.0; | ||
88 | if (y > h - 2.0) | ||
89 | y = h - 2.0; | ||
90 | if (x < 0.0) | ||
91 | x = 0.0; | ||
92 | if (y < 0.0) | ||
93 | y = 0.0; | ||
94 | |||
95 | const int stepSize = 1; | ||
96 | double h00 = map[(int) x, (int) y]; | ||
97 | double h10 = map[(int) x + stepSize, (int) y]; | ||
98 | double h01 = map[(int) x, (int) y + stepSize]; | ||
99 | double h11 = map[(int) x + stepSize, (int) y + stepSize]; | ||
100 | double h1 = h00; | ||
101 | double h2 = h10; | ||
102 | double h3 = h01; | ||
103 | double h4 = h11; | ||
104 | double a00 = h1; | ||
105 | double a10 = h2 - h1; | ||
106 | double a01 = h3 - h1; | ||
107 | double a11 = h1 - h2 - h3 + h4; | ||
108 | double partialx = x - (int) x; | ||
109 | double partialz = y - (int) y; | ||
110 | double hi = a00 + (a10 * partialx) + (a01 * partialz) + (a11 * partialx * partialz); | ||
111 | return hi; | ||
112 | } | ||
113 | } | ||
114 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainEffect.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainEffect.cs new file mode 100644 index 0000000..40b9f5a --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainEffect.cs | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
31 | { | ||
32 | public interface ITerrainEffect | ||
33 | { | ||
34 | void RunEffect(ITerrainChannel map); | ||
35 | } | ||
36 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainFloodEffect.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainFloodEffect.cs new file mode 100644 index 0000000..eee7a83 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainFloodEffect.cs | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
32 | { | ||
33 | public interface ITerrainFloodEffect | ||
34 | { | ||
35 | void FloodEffect(ITerrainChannel map, Boolean[,] fillArea, double strength); | ||
36 | } | ||
37 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs new file mode 100644 index 0000000..c62b897 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.IO; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
32 | { | ||
33 | public interface ITerrainLoader | ||
34 | { | ||
35 | string FileExtension { get; } | ||
36 | ITerrainChannel LoadFile(string filename); | ||
37 | ITerrainChannel LoadFile(string filename, int fileStartX, int fileStartY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight); | ||
38 | ITerrainChannel LoadStream(Stream stream); | ||
39 | void SaveFile(string filename, ITerrainChannel map); | ||
40 | void SaveStream(Stream stream, ITerrainChannel map); | ||
41 | } | ||
42 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainModule.cs new file mode 100644 index 0000000..8c5d1d9 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainModule.cs | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | |||
29 | using System.IO; | ||
30 | using OpenMetaverse; | ||
31 | using OpenSim.Framework; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
34 | { | ||
35 | public interface ITerrainModule | ||
36 | { | ||
37 | void LoadFromFile(string filename); | ||
38 | void SaveToFile(string filename); | ||
39 | void ModifyTerrain(UUID user, Vector3 pos, byte size, byte action, UUID agentId); | ||
40 | |||
41 | /// <summary> | ||
42 | /// Load a terrain from a stream. | ||
43 | /// </summary> | ||
44 | /// <param name="filename"> | ||
45 | /// Only required here to identify the image type. Not otherwise used in the loading itself. | ||
46 | /// </param> | ||
47 | /// <param name="stream"></param> | ||
48 | void LoadFromStream(string filename, Stream stream); | ||
49 | |||
50 | /// <summary> | ||
51 | /// Save a terrain to a stream. | ||
52 | /// </summary> | ||
53 | /// <param name="filename"> | ||
54 | /// Only required here to identify the image type. Not otherwise used in the saving itself. | ||
55 | /// </param> | ||
56 | /// <param name="stream"></param> | ||
57 | void SaveToStream(string filename, Stream stream); | ||
58 | |||
59 | void InstallPlugin(string name, ITerrainEffect plug); | ||
60 | } | ||
61 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainPaintableEffect.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainPaintableEffect.cs new file mode 100644 index 0000000..15d9f6e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainPaintableEffect.cs | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
31 | { | ||
32 | public interface ITerrainPaintableEffect | ||
33 | { | ||
34 | void PaintEffect(ITerrainChannel map, bool[,] allowMask, double x, double y, double z, double strength, double duration); | ||
35 | } | ||
36 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/ErodeSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/ErodeSphere.cs new file mode 100644 index 0000000..6ce6994 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/ErodeSphere.cs | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
33 | { | ||
34 | /// <summary> | ||
35 | /// Hydraulic Erosion Brush | ||
36 | /// </summary> | ||
37 | public class ErodeSphere : ITerrainPaintableEffect | ||
38 | { | ||
39 | private const double rainHeight = 0.2; | ||
40 | private const int rounds = 10; | ||
41 | private const NeighbourSystem type = NeighbourSystem.Moore; | ||
42 | private const double waterSaturation = 0.30; | ||
43 | |||
44 | #region Supporting Functions | ||
45 | |||
46 | private static int[] Neighbours(NeighbourSystem neighbourType, int index) | ||
47 | { | ||
48 | int[] coord = new int[2]; | ||
49 | |||
50 | index++; | ||
51 | |||
52 | switch (neighbourType) | ||
53 | { | ||
54 | case NeighbourSystem.Moore: | ||
55 | switch (index) | ||
56 | { | ||
57 | case 1: | ||
58 | coord[0] = -1; | ||
59 | coord[1] = -1; | ||
60 | break; | ||
61 | |||
62 | case 2: | ||
63 | coord[0] = -0; | ||
64 | coord[1] = -1; | ||
65 | break; | ||
66 | |||
67 | case 3: | ||
68 | coord[0] = +1; | ||
69 | coord[1] = -1; | ||
70 | break; | ||
71 | |||
72 | case 4: | ||
73 | coord[0] = -1; | ||
74 | coord[1] = -0; | ||
75 | break; | ||
76 | |||
77 | case 5: | ||
78 | coord[0] = -0; | ||
79 | coord[1] = -0; | ||
80 | break; | ||
81 | |||
82 | case 6: | ||
83 | coord[0] = +1; | ||
84 | coord[1] = -0; | ||
85 | break; | ||
86 | |||
87 | case 7: | ||
88 | coord[0] = -1; | ||
89 | coord[1] = +1; | ||
90 | break; | ||
91 | |||
92 | case 8: | ||
93 | coord[0] = -0; | ||
94 | coord[1] = +1; | ||
95 | break; | ||
96 | |||
97 | case 9: | ||
98 | coord[0] = +1; | ||
99 | coord[1] = +1; | ||
100 | break; | ||
101 | |||
102 | default: | ||
103 | break; | ||
104 | } | ||
105 | break; | ||
106 | |||
107 | case NeighbourSystem.VonNeumann: | ||
108 | switch (index) | ||
109 | { | ||
110 | case 1: | ||
111 | coord[0] = 0; | ||
112 | coord[1] = -1; | ||
113 | break; | ||
114 | |||
115 | case 2: | ||
116 | coord[0] = -1; | ||
117 | coord[1] = 0; | ||
118 | break; | ||
119 | |||
120 | case 3: | ||
121 | coord[0] = +1; | ||
122 | coord[1] = 0; | ||
123 | break; | ||
124 | |||
125 | case 4: | ||
126 | coord[0] = 0; | ||
127 | coord[1] = +1; | ||
128 | break; | ||
129 | |||
130 | case 5: | ||
131 | coord[0] = -0; | ||
132 | coord[1] = -0; | ||
133 | break; | ||
134 | |||
135 | default: | ||
136 | break; | ||
137 | } | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | return coord; | ||
142 | } | ||
143 | |||
144 | private enum NeighbourSystem | ||
145 | { | ||
146 | Moore, | ||
147 | VonNeumann | ||
148 | } ; | ||
149 | |||
150 | #endregion | ||
151 | |||
152 | #region ITerrainPaintableEffect Members | ||
153 | |||
154 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
155 | { | ||
156 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
157 | |||
158 | int x, y; | ||
159 | // Using one 'rain' round for this, so skipping a useless loop | ||
160 | // Will need to adapt back in for the Flood brush | ||
161 | |||
162 | ITerrainChannel water = new TerrainChannel(map.Width, map.Height); | ||
163 | ITerrainChannel sediment = new TerrainChannel(map.Width, map.Height); | ||
164 | |||
165 | // Fill with rain | ||
166 | for (x = 0; x < water.Width; x++) | ||
167 | for (y = 0; y < water.Height; y++) | ||
168 | water[x, y] = Math.Max(0.0, TerrainUtil.SphericalFactor(x, y, rx, ry, strength) * rainHeight * duration); | ||
169 | |||
170 | for (int i = 0; i < rounds; i++) | ||
171 | { | ||
172 | // Erode underlying terrain | ||
173 | for (x = 0; x < water.Width; x++) | ||
174 | { | ||
175 | for (y = 0; y < water.Height; y++) | ||
176 | { | ||
177 | if (mask[x,y]) | ||
178 | { | ||
179 | const double solConst = (1.0 / rounds); | ||
180 | double sedDelta = water[x, y] * solConst; | ||
181 | map[x, y] -= sedDelta; | ||
182 | sediment[x, y] += sedDelta; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | // Move water | ||
188 | for (x = 0; x < water.Width; x++) | ||
189 | { | ||
190 | for (y = 0; y < water.Height; y++) | ||
191 | { | ||
192 | if (water[x, y] <= 0) | ||
193 | continue; | ||
194 | |||
195 | // Step 1. Calculate average of neighbours | ||
196 | |||
197 | int neighbours = 0; | ||
198 | double altitudeTotal = 0.0; | ||
199 | double altitudeMe = map[x, y] + water[x, y]; | ||
200 | |||
201 | const int NEIGHBOUR_ME = 4; | ||
202 | const int NEIGHBOUR_MAX = 9; | ||
203 | |||
204 | for (int j = 0; j < NEIGHBOUR_MAX; j++) | ||
205 | { | ||
206 | if (j != NEIGHBOUR_ME) | ||
207 | { | ||
208 | int[] coords = Neighbours(type, j); | ||
209 | |||
210 | coords[0] += x; | ||
211 | coords[1] += y; | ||
212 | |||
213 | if (coords[0] > map.Width - 1) | ||
214 | continue; | ||
215 | if (coords[1] > map.Height - 1) | ||
216 | continue; | ||
217 | if (coords[0] < 0) | ||
218 | continue; | ||
219 | if (coords[1] < 0) | ||
220 | continue; | ||
221 | |||
222 | // Calculate total height of this neighbour | ||
223 | double altitudeNeighbour = water[coords[0], coords[1]] + map[coords[0], coords[1]]; | ||
224 | |||
225 | // If it's greater than me... | ||
226 | if (altitudeNeighbour - altitudeMe < 0) | ||
227 | { | ||
228 | // Add it to our calculations | ||
229 | neighbours++; | ||
230 | altitudeTotal += altitudeNeighbour; | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
235 | if (neighbours == 0) | ||
236 | continue; | ||
237 | |||
238 | double altitudeAvg = altitudeTotal / neighbours; | ||
239 | |||
240 | // Step 2. Allocate water to neighbours. | ||
241 | for (int j = 0; j < NEIGHBOUR_MAX; j++) | ||
242 | { | ||
243 | if (j != NEIGHBOUR_ME) | ||
244 | { | ||
245 | int[] coords = Neighbours(type, j); | ||
246 | |||
247 | coords[0] += x; | ||
248 | coords[1] += y; | ||
249 | |||
250 | if (coords[0] > map.Width - 1) | ||
251 | continue; | ||
252 | if (coords[1] > map.Height - 1) | ||
253 | continue; | ||
254 | if (coords[0] < 0) | ||
255 | continue; | ||
256 | if (coords[1] < 0) | ||
257 | continue; | ||
258 | |||
259 | // Skip if we dont have water to begin with. | ||
260 | if (water[x, y] < 0) | ||
261 | continue; | ||
262 | |||
263 | // Calculate our delta average | ||
264 | double altitudeDelta = altitudeMe - altitudeAvg; | ||
265 | |||
266 | if (altitudeDelta < 0) | ||
267 | continue; | ||
268 | |||
269 | // Calculate how much water we can move | ||
270 | double waterMin = Math.Min(water[x, y], altitudeDelta); | ||
271 | double waterDelta = waterMin * ((water[coords[0], coords[1]] + map[coords[0], coords[1]]) | ||
272 | / altitudeTotal); | ||
273 | |||
274 | double sedimentDelta = sediment[x, y] * (waterDelta / water[x, y]); | ||
275 | |||
276 | if (sedimentDelta > 0) | ||
277 | { | ||
278 | sediment[x, y] -= sedimentDelta; | ||
279 | sediment[coords[0], coords[1]] += sedimentDelta; | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | // Evaporate | ||
287 | |||
288 | for (x = 0; x < water.Width; x++) | ||
289 | { | ||
290 | for (y = 0; y < water.Height; y++) | ||
291 | { | ||
292 | water[x, y] *= 1.0 - (rainHeight / rounds); | ||
293 | |||
294 | double waterCapacity = waterSaturation * water[x, y]; | ||
295 | |||
296 | double sedimentDeposit = sediment[x, y] - waterCapacity; | ||
297 | if (sedimentDeposit > 0) | ||
298 | { | ||
299 | if (mask[x,y]) | ||
300 | { | ||
301 | sediment[x, y] -= sedimentDeposit; | ||
302 | map[x, y] += sedimentDeposit; | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | |||
309 | // Deposit any remainder (should be minimal) | ||
310 | for (x = 0; x < water.Width; x++) | ||
311 | for (y = 0; y < water.Height; y++) | ||
312 | if (mask[x,y] && sediment[x, y] > 0) | ||
313 | map[x, y] += sediment[x, y]; | ||
314 | } | ||
315 | |||
316 | #endregion | ||
317 | } | ||
318 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/FlattenSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/FlattenSphere.cs new file mode 100644 index 0000000..928a595 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/FlattenSphere.cs | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
33 | { | ||
34 | public class FlattenSphere : ITerrainPaintableEffect | ||
35 | { | ||
36 | #region ITerrainPaintableEffect Members | ||
37 | |||
38 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
39 | { | ||
40 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
41 | |||
42 | int x, y; | ||
43 | |||
44 | if (rz < 0) { | ||
45 | double sum = 0.0; | ||
46 | double step2 = 0.0; | ||
47 | duration = 0.009; //MCP Should be read from ini file | ||
48 | |||
49 | |||
50 | // compute delta map | ||
51 | for (x = 0; x < map.Width; x++) | ||
52 | { | ||
53 | for (y = 0; y < map.Height; y++) | ||
54 | { | ||
55 | double z = TerrainUtil.SphericalFactor(x, y, rx, ry, strength); | ||
56 | |||
57 | if (z > 0) // add in non-zero amount | ||
58 | { | ||
59 | sum += map[x, y] * z; | ||
60 | step2 += z; | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | rz = sum / step2; | ||
65 | } | ||
66 | |||
67 | |||
68 | // blend in map | ||
69 | for (x = 0; x < map.Width; x++) | ||
70 | { | ||
71 | for (y = 0; y < map.Height; y++) | ||
72 | { | ||
73 | if (!mask[x,y]) | ||
74 | continue; | ||
75 | |||
76 | double z = TerrainUtil.SphericalFactor(x, y, rx, ry, strength) * duration; | ||
77 | |||
78 | if (z > 0) // add in non-zero amount | ||
79 | { | ||
80 | if (z > 1.0) | ||
81 | z = 1.0; | ||
82 | |||
83 | map[x, y] = (map[x, y] * (1.0 - z)) + (rz * z); | ||
84 | } | ||
85 | |||
86 | double delta = rz - map[x, y]; | ||
87 | if (Math.Abs(delta) > 0.1) | ||
88 | delta *= 0.25; | ||
89 | |||
90 | if (delta != 0) // add in non-zero amount | ||
91 | { | ||
92 | map[x, y] += delta; | ||
93 | } | ||
94 | |||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | #endregion | ||
100 | } | ||
101 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/LowerSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/LowerSphere.cs new file mode 100644 index 0000000..8c40088 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/LowerSphere.cs | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
32 | { | ||
33 | public class LowerSphere : ITerrainPaintableEffect | ||
34 | { | ||
35 | #region ITerrainPaintableEffect Members | ||
36 | |||
37 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
38 | { | ||
39 | int s = (int) (Math.Pow(2, strength) + 0.5); | ||
40 | |||
41 | int x; | ||
42 | int xFrom = (int)(rx-s+0.5); | ||
43 | int xTo = (int)(rx+s+0.5) + 1; | ||
44 | int yFrom = (int)(ry-s+0.5); | ||
45 | int yTo = (int)(ry+s+0.5) + 1; | ||
46 | |||
47 | if (xFrom < 0) | ||
48 | xFrom = 0; | ||
49 | |||
50 | if (yFrom < 0) | ||
51 | yFrom = 0; | ||
52 | |||
53 | if (xTo > map.Width) | ||
54 | xTo = map.Width; | ||
55 | |||
56 | if (yTo > map.Width) | ||
57 | yTo = map.Width; | ||
58 | |||
59 | for (x = xFrom; x < xTo; x++) | ||
60 | { | ||
61 | int y; | ||
62 | for (y = yFrom; y < yTo; y++) | ||
63 | { | ||
64 | if (!mask[x,y]) | ||
65 | continue; | ||
66 | |||
67 | // Calculate a cos-sphere and add it to the heighmap | ||
68 | double r = Math.Sqrt((x-rx) * (x-rx) + ((y-ry) * (y-ry))); | ||
69 | double z = Math.Cos(r * Math.PI / (s * 2)); | ||
70 | if (z > 0.0) | ||
71 | { | ||
72 | double newz = map[x, y] - z * duration; | ||
73 | if (newz < 0.0) | ||
74 | map[x, y] = 0.0; | ||
75 | else | ||
76 | map[x, y] = newz; | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | |||
81 | } | ||
82 | #endregion | ||
83 | } | ||
84 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs new file mode 100644 index 0000000..95a8c33 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Framework; | ||
30 | using OpenSim.Region.Framework.Interfaces; | ||
31 | using OpenSim.Region.Framework.Scenes; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
34 | { | ||
35 | public class NoiseSphere : ITerrainPaintableEffect | ||
36 | { | ||
37 | #region ITerrainPaintableEffect Members | ||
38 | |||
39 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
40 | { | ||
41 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
42 | |||
43 | int x; | ||
44 | for (x = 0; x < map.Width; x++) | ||
45 | { | ||
46 | int y; | ||
47 | for (y = 0; y < map.Height; y++) | ||
48 | { | ||
49 | if (!mask[x,y]) | ||
50 | continue; | ||
51 | |||
52 | // Calculate a sphere and add it to the heighmap | ||
53 | double z = strength; | ||
54 | z *= z; | ||
55 | z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry)); | ||
56 | |||
57 | double noise = TerrainUtil.PerlinNoise2D(x / (double) Constants.RegionSize, y / (double) Constants.RegionSize, 8, 1.0); | ||
58 | |||
59 | if (z > 0.0) | ||
60 | map[x, y] += noise * z * duration; | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | #endregion | ||
66 | } | ||
67 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/OlsenSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/OlsenSphere.cs new file mode 100644 index 0000000..1a2528a --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/OlsenSphere.cs | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
33 | { | ||
34 | /// <summary> | ||
35 | /// Speed-Optimised Hybrid Erosion Brush | ||
36 | /// | ||
37 | /// As per Jacob Olsen's Paper | ||
38 | /// http://www.oddlabs.com/download/terrain_generation.pdf | ||
39 | /// </summary> | ||
40 | public class OlsenSphere : ITerrainPaintableEffect | ||
41 | { | ||
42 | private const double nConst = 1024.0; | ||
43 | private const NeighbourSystem type = NeighbourSystem.Moore; | ||
44 | |||
45 | #region Supporting Functions | ||
46 | |||
47 | private static int[] Neighbours(NeighbourSystem neighbourType, int index) | ||
48 | { | ||
49 | int[] coord = new int[2]; | ||
50 | |||
51 | index++; | ||
52 | |||
53 | switch (neighbourType) | ||
54 | { | ||
55 | case NeighbourSystem.Moore: | ||
56 | switch (index) | ||
57 | { | ||
58 | case 1: | ||
59 | coord[0] = -1; | ||
60 | coord[1] = -1; | ||
61 | break; | ||
62 | |||
63 | case 2: | ||
64 | coord[0] = -0; | ||
65 | coord[1] = -1; | ||
66 | break; | ||
67 | |||
68 | case 3: | ||
69 | coord[0] = +1; | ||
70 | coord[1] = -1; | ||
71 | break; | ||
72 | |||
73 | case 4: | ||
74 | coord[0] = -1; | ||
75 | coord[1] = -0; | ||
76 | break; | ||
77 | |||
78 | case 5: | ||
79 | coord[0] = -0; | ||
80 | coord[1] = -0; | ||
81 | break; | ||
82 | |||
83 | case 6: | ||
84 | coord[0] = +1; | ||
85 | coord[1] = -0; | ||
86 | break; | ||
87 | |||
88 | case 7: | ||
89 | coord[0] = -1; | ||
90 | coord[1] = +1; | ||
91 | break; | ||
92 | |||
93 | case 8: | ||
94 | coord[0] = -0; | ||
95 | coord[1] = +1; | ||
96 | break; | ||
97 | |||
98 | case 9: | ||
99 | coord[0] = +1; | ||
100 | coord[1] = +1; | ||
101 | break; | ||
102 | |||
103 | default: | ||
104 | break; | ||
105 | } | ||
106 | break; | ||
107 | |||
108 | case NeighbourSystem.VonNeumann: | ||
109 | switch (index) | ||
110 | { | ||
111 | case 1: | ||
112 | coord[0] = 0; | ||
113 | coord[1] = -1; | ||
114 | break; | ||
115 | |||
116 | case 2: | ||
117 | coord[0] = -1; | ||
118 | coord[1] = 0; | ||
119 | break; | ||
120 | |||
121 | case 3: | ||
122 | coord[0] = +1; | ||
123 | coord[1] = 0; | ||
124 | break; | ||
125 | |||
126 | case 4: | ||
127 | coord[0] = 0; | ||
128 | coord[1] = +1; | ||
129 | break; | ||
130 | |||
131 | case 5: | ||
132 | coord[0] = -0; | ||
133 | coord[1] = -0; | ||
134 | break; | ||
135 | |||
136 | default: | ||
137 | break; | ||
138 | } | ||
139 | break; | ||
140 | } | ||
141 | |||
142 | return coord; | ||
143 | } | ||
144 | |||
145 | private enum NeighbourSystem | ||
146 | { | ||
147 | Moore, | ||
148 | VonNeumann | ||
149 | } ; | ||
150 | |||
151 | #endregion | ||
152 | |||
153 | #region ITerrainPaintableEffect Members | ||
154 | |||
155 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
156 | { | ||
157 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
158 | |||
159 | int x; | ||
160 | |||
161 | for (x = 0; x < map.Width; x++) | ||
162 | { | ||
163 | int y; | ||
164 | for (y = 0; y < map.Height; y++) | ||
165 | { | ||
166 | if (!mask[x,y]) | ||
167 | continue; | ||
168 | |||
169 | double z = TerrainUtil.SphericalFactor(x, y, rx, ry, strength); | ||
170 | |||
171 | if (z > 0) // add in non-zero amount | ||
172 | { | ||
173 | const int NEIGHBOUR_ME = 4; | ||
174 | const int NEIGHBOUR_MAX = 9; | ||
175 | |||
176 | double max = Double.MinValue; | ||
177 | int loc = 0; | ||
178 | |||
179 | |||
180 | for (int j = 0; j < NEIGHBOUR_MAX; j++) | ||
181 | { | ||
182 | if (j != NEIGHBOUR_ME) | ||
183 | { | ||
184 | int[] coords = Neighbours(type, j); | ||
185 | |||
186 | coords[0] += x; | ||
187 | coords[1] += y; | ||
188 | |||
189 | if (coords[0] > map.Width - 1) | ||
190 | continue; | ||
191 | if (coords[1] > map.Height - 1) | ||
192 | continue; | ||
193 | if (coords[0] < 0) | ||
194 | continue; | ||
195 | if (coords[1] < 0) | ||
196 | continue; | ||
197 | |||
198 | double cellmax = map[x, y] - map[coords[0], coords[1]]; | ||
199 | if (cellmax > max) | ||
200 | { | ||
201 | max = cellmax; | ||
202 | loc = j; | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | double T = nConst / ((map.Width + map.Height) / 2.0); | ||
208 | // Apply results | ||
209 | if (0 < max && max <= T) | ||
210 | { | ||
211 | int[] maxCoords = Neighbours(type, loc); | ||
212 | double heightDelta = 0.5 * max * z * duration; | ||
213 | map[x, y] -= heightDelta; | ||
214 | map[x + maxCoords[0], y + maxCoords[1]] += heightDelta; | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | #endregion | ||
222 | } | ||
223 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/RaiseSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/RaiseSphere.cs new file mode 100644 index 0000000..c53bb7d --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/RaiseSphere.cs | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
33 | { | ||
34 | public class RaiseSphere : ITerrainPaintableEffect | ||
35 | { | ||
36 | #region ITerrainPaintableEffect Members | ||
37 | |||
38 | |||
39 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
40 | { | ||
41 | int s = (int) (Math.Pow(2, strength) + 0.5); | ||
42 | |||
43 | int x; | ||
44 | int xFrom = (int)(rx-s+0.5); | ||
45 | int xTo = (int)(rx+s+0.5) + 1; | ||
46 | int yFrom = (int)(ry-s+0.5); | ||
47 | int yTo = (int)(ry+s+0.5) + 1; | ||
48 | |||
49 | if (xFrom < 0) | ||
50 | xFrom = 0; | ||
51 | |||
52 | if (yFrom < 0) | ||
53 | yFrom = 0; | ||
54 | |||
55 | if (xTo > map.Width) | ||
56 | xTo = map.Width; | ||
57 | |||
58 | if (yTo > map.Width) | ||
59 | yTo = map.Width; | ||
60 | |||
61 | for (x = xFrom; x < xTo; x++) | ||
62 | { | ||
63 | int y; | ||
64 | for (y = yFrom; y < yTo; y++) | ||
65 | { | ||
66 | if (!mask[x,y]) | ||
67 | continue; | ||
68 | |||
69 | // Calculate a cos-sphere and add it to the heighmap | ||
70 | double r = Math.Sqrt((x-rx) * (x-rx) + ((y-ry) * (y-ry))); | ||
71 | double z = Math.Cos(r * Math.PI / (s * 2)); | ||
72 | if (z > 0.0) | ||
73 | map[x, y] += z * duration; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #endregion | ||
79 | } | ||
80 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/RevertSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/RevertSphere.cs new file mode 100644 index 0000000..4ed8a13 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/RevertSphere.cs | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
33 | { | ||
34 | public class RevertSphere : ITerrainPaintableEffect | ||
35 | { | ||
36 | private readonly ITerrainChannel m_revertmap; | ||
37 | |||
38 | public RevertSphere(ITerrainChannel revertmap) | ||
39 | { | ||
40 | m_revertmap = revertmap; | ||
41 | } | ||
42 | |||
43 | #region ITerrainPaintableEffect Members | ||
44 | |||
45 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
46 | { | ||
47 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
48 | duration = 0.03; //MCP Should be read from ini file | ||
49 | |||
50 | if (duration > 1.0) | ||
51 | duration = 1.0; | ||
52 | if (duration < 0) | ||
53 | return; | ||
54 | |||
55 | int x; | ||
56 | for (x = 0; x < map.Width; x++) | ||
57 | { | ||
58 | int y; | ||
59 | for (y = 0; y < map.Height; y++) | ||
60 | { | ||
61 | if (!mask[x,y]) | ||
62 | continue; | ||
63 | |||
64 | // Calculate a sphere and add it to the heighmap | ||
65 | double z = strength; | ||
66 | z *= z; | ||
67 | z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry)); | ||
68 | |||
69 | if (z > 0.0) | ||
70 | { | ||
71 | z *= duration; | ||
72 | map[x, y] = (map[x, y] * (1.0 - z)) + (m_revertmap[x, y] * z); | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #endregion | ||
79 | } | ||
80 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/SmoothSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/SmoothSphere.cs new file mode 100644 index 0000000..6636d8f --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/SmoothSphere.cs | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | using OpenSim.Region.Framework.Scenes; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
32 | { | ||
33 | public class SmoothSphere : ITerrainPaintableEffect | ||
34 | { | ||
35 | #region ITerrainPaintableEffect Members | ||
36 | |||
37 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
38 | { | ||
39 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
40 | |||
41 | int x, y; | ||
42 | double[,] tweak = new double[map.Width,map.Height]; | ||
43 | |||
44 | double area = strength; | ||
45 | double step = strength / 4.0; | ||
46 | duration = 0.03; //MCP Should be read from ini file | ||
47 | |||
48 | |||
49 | // compute delta map | ||
50 | for (x = 0; x < map.Width; x++) | ||
51 | { | ||
52 | for (y = 0; y < map.Height; y++) | ||
53 | { | ||
54 | double z = TerrainUtil.SphericalFactor(x, y, rx, ry, strength); | ||
55 | |||
56 | if (z > 0) // add in non-zero amount | ||
57 | { | ||
58 | double average = 0.0; | ||
59 | int avgsteps = 0; | ||
60 | |||
61 | double n; | ||
62 | for (n = 0.0 - area; n < area; n += step) | ||
63 | { | ||
64 | double l; | ||
65 | for (l = 0.0 - area; l < area; l += step) | ||
66 | { | ||
67 | avgsteps++; | ||
68 | average += TerrainUtil.GetBilinearInterpolate(x + n, y + l, map); | ||
69 | } | ||
70 | } | ||
71 | tweak[x, y] = average / avgsteps; | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | // blend in map | ||
76 | for (x = 0; x < map.Width; x++) | ||
77 | { | ||
78 | for (y = 0; y < map.Height; y++) | ||
79 | { | ||
80 | if (!mask[x,y]) | ||
81 | continue; | ||
82 | |||
83 | double z = TerrainUtil.SphericalFactor(x, y, rx, ry, strength); | ||
84 | |||
85 | if (z > 0) // add in non-zero amount | ||
86 | { | ||
87 | double da = z; | ||
88 | double a = (map[x, y] - tweak[x, y]) * da; | ||
89 | double newz = map[x, y] - (a * duration); | ||
90 | |||
91 | if (newz > 0.0) | ||
92 | map[x, y] = newz; | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | #endregion | ||
99 | } | ||
100 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/WeatherSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/WeatherSphere.cs new file mode 100644 index 0000000..6b00cc8 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/WeatherSphere.cs | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenSim.Region.Framework.Interfaces; | ||
29 | using OpenSim.Region.Framework.Scenes; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | ||
32 | { | ||
33 | /// <summary> | ||
34 | /// Thermal Weathering Paint Brush | ||
35 | /// </summary> | ||
36 | public class WeatherSphere : ITerrainPaintableEffect | ||
37 | { | ||
38 | private const double talus = 0.2; | ||
39 | private const NeighbourSystem type = NeighbourSystem.Moore; | ||
40 | |||
41 | #region Supporting Functions | ||
42 | |||
43 | private static int[] Neighbours(NeighbourSystem neighbourType, int index) | ||
44 | { | ||
45 | int[] coord = new int[2]; | ||
46 | |||
47 | index++; | ||
48 | |||
49 | switch (neighbourType) | ||
50 | { | ||
51 | case NeighbourSystem.Moore: | ||
52 | switch (index) | ||
53 | { | ||
54 | case 1: | ||
55 | coord[0] = -1; | ||
56 | coord[1] = -1; | ||
57 | break; | ||
58 | |||
59 | case 2: | ||
60 | coord[0] = -0; | ||
61 | coord[1] = -1; | ||
62 | break; | ||
63 | |||
64 | case 3: | ||
65 | coord[0] = +1; | ||
66 | coord[1] = -1; | ||
67 | break; | ||
68 | |||
69 | case 4: | ||
70 | coord[0] = -1; | ||
71 | coord[1] = -0; | ||
72 | break; | ||
73 | |||
74 | case 5: | ||
75 | coord[0] = -0; | ||
76 | coord[1] = -0; | ||
77 | break; | ||
78 | |||
79 | case 6: | ||
80 | coord[0] = +1; | ||
81 | coord[1] = -0; | ||
82 | break; | ||
83 | |||
84 | case 7: | ||
85 | coord[0] = -1; | ||
86 | coord[1] = +1; | ||
87 | break; | ||
88 | |||
89 | case 8: | ||
90 | coord[0] = -0; | ||
91 | coord[1] = +1; | ||
92 | break; | ||
93 | |||
94 | case 9: | ||
95 | coord[0] = +1; | ||
96 | coord[1] = +1; | ||
97 | break; | ||
98 | |||
99 | default: | ||
100 | break; | ||
101 | } | ||
102 | break; | ||
103 | |||
104 | case NeighbourSystem.VonNeumann: | ||
105 | switch (index) | ||
106 | { | ||
107 | case 1: | ||
108 | coord[0] = 0; | ||
109 | coord[1] = -1; | ||
110 | break; | ||
111 | |||
112 | case 2: | ||
113 | coord[0] = -1; | ||
114 | coord[1] = 0; | ||
115 | break; | ||
116 | |||
117 | case 3: | ||
118 | coord[0] = +1; | ||
119 | coord[1] = 0; | ||
120 | break; | ||
121 | |||
122 | case 4: | ||
123 | coord[0] = 0; | ||
124 | coord[1] = +1; | ||
125 | break; | ||
126 | |||
127 | case 5: | ||
128 | coord[0] = -0; | ||
129 | coord[1] = -0; | ||
130 | break; | ||
131 | |||
132 | default: | ||
133 | break; | ||
134 | } | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | return coord; | ||
139 | } | ||
140 | |||
141 | private enum NeighbourSystem | ||
142 | { | ||
143 | Moore, | ||
144 | VonNeumann | ||
145 | } ; | ||
146 | |||
147 | #endregion | ||
148 | |||
149 | #region ITerrainPaintableEffect Members | ||
150 | |||
151 | public void PaintEffect(ITerrainChannel map, bool[,] mask, double rx, double ry, double rz, double strength, double duration) | ||
152 | { | ||
153 | strength = TerrainUtil.MetersToSphericalStrength(strength); | ||
154 | |||
155 | int x; | ||
156 | |||
157 | for (x = 0; x < map.Width; x++) | ||
158 | { | ||
159 | int y; | ||
160 | for (y = 0; y < map.Height; y++) | ||
161 | { | ||
162 | if (!mask[x,y]) | ||
163 | continue; | ||
164 | |||
165 | double z = TerrainUtil.SphericalFactor(x, y, rx, ry, strength); | ||
166 | |||
167 | if (z > 0) // add in non-zero amount | ||
168 | { | ||
169 | const int NEIGHBOUR_ME = 4; | ||
170 | const int NEIGHBOUR_MAX = 9; | ||
171 | |||
172 | for (int j = 0; j < NEIGHBOUR_MAX; j++) | ||
173 | { | ||
174 | if (j != NEIGHBOUR_ME) | ||
175 | { | ||
176 | int[] coords = Neighbours(type, j); | ||
177 | |||
178 | coords[0] += x; | ||
179 | coords[1] += y; | ||
180 | |||
181 | if (coords[0] > map.Width - 1) | ||
182 | continue; | ||
183 | if (coords[1] > map.Height - 1) | ||
184 | continue; | ||
185 | if (coords[0] < 0) | ||
186 | continue; | ||
187 | if (coords[1] < 0) | ||
188 | continue; | ||
189 | |||
190 | double heightF = map[x, y]; | ||
191 | double target = map[coords[0], coords[1]]; | ||
192 | |||
193 | if (target > heightF + talus) | ||
194 | { | ||
195 | double calc = duration * ((target - heightF) - talus) * z; | ||
196 | heightF += calc; | ||
197 | target -= calc; | ||
198 | } | ||
199 | |||
200 | map[x, y] = heightF; | ||
201 | map[coords[0], coords[1]] = target; | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | |||
209 | #endregion | ||
210 | } | ||
211 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainException.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainException.cs new file mode 100644 index 0000000..ff9b8ec --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainException.cs | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | |||
30 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
31 | { | ||
32 | public class TerrainException : Exception | ||
33 | { | ||
34 | public TerrainException() | ||
35 | { | ||
36 | } | ||
37 | |||
38 | public TerrainException(string msg) : base(msg) | ||
39 | { | ||
40 | } | ||
41 | |||
42 | public TerrainException(string msg, Exception e) : base(msg, e) | ||
43 | { | ||
44 | } | ||
45 | } | ||
46 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs new file mode 100644 index 0000000..9de7338 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | |||
@@ -0,0 +1,1001 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using OpenMetaverse; | ||
33 | using log4net; | ||
34 | using Nini.Config; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||
39 | using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; | ||
40 | using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; | ||
41 | using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; | ||
42 | |||
43 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
44 | { | ||
45 | public class TerrainModule : IRegionModule, ICommandableModule, ITerrainModule | ||
46 | { | ||
47 | #region StandardTerrainEffects enum | ||
48 | |||
49 | /// <summary> | ||
50 | /// A standard set of terrain brushes and effects recognised by viewers | ||
51 | /// </summary> | ||
52 | public enum StandardTerrainEffects : byte | ||
53 | { | ||
54 | Flatten = 0, | ||
55 | Raise = 1, | ||
56 | Lower = 2, | ||
57 | Smooth = 3, | ||
58 | Noise = 4, | ||
59 | Revert = 5, | ||
60 | |||
61 | // Extended brushes | ||
62 | Erode = 255, | ||
63 | Weather = 254, | ||
64 | Olsen = 253 | ||
65 | } | ||
66 | |||
67 | #endregion | ||
68 | |||
69 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
70 | |||
71 | private readonly Commander m_commander = new Commander("terrain"); | ||
72 | |||
73 | private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = | ||
74 | new Dictionary<StandardTerrainEffects, ITerrainFloodEffect>(); | ||
75 | |||
76 | private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>(); | ||
77 | |||
78 | private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = | ||
79 | new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>(); | ||
80 | |||
81 | private ITerrainChannel m_channel; | ||
82 | private Dictionary<string, ITerrainEffect> m_plugineffects; | ||
83 | private ITerrainChannel m_revert; | ||
84 | private Scene m_scene; | ||
85 | private bool m_tainted; | ||
86 | |||
87 | #region ICommandableModule Members | ||
88 | |||
89 | public ICommander CommandInterface | ||
90 | { | ||
91 | get { return m_commander; } | ||
92 | } | ||
93 | |||
94 | #endregion | ||
95 | |||
96 | #region IRegionModule Members | ||
97 | |||
98 | /// <summary> | ||
99 | /// Creates and initialises a terrain module for a region | ||
100 | /// </summary> | ||
101 | /// <param name="scene">Region initialising</param> | ||
102 | /// <param name="config">Config for the region</param> | ||
103 | public void Initialise(Scene scene, IConfigSource config) | ||
104 | { | ||
105 | m_scene = scene; | ||
106 | |||
107 | // Install terrain module in the simulator | ||
108 | if (m_scene.Heightmap == null) | ||
109 | { | ||
110 | lock (m_scene) | ||
111 | { | ||
112 | m_channel = new TerrainChannel(); | ||
113 | m_scene.Heightmap = m_channel; | ||
114 | m_revert = new TerrainChannel(); | ||
115 | UpdateRevertMap(); | ||
116 | } | ||
117 | } | ||
118 | else | ||
119 | { | ||
120 | m_channel = m_scene.Heightmap; | ||
121 | m_revert = new TerrainChannel(); | ||
122 | UpdateRevertMap(); | ||
123 | } | ||
124 | |||
125 | m_scene.RegisterModuleInterface<ITerrainModule>(this); | ||
126 | m_scene.EventManager.OnNewClient += EventManager_OnNewClient; | ||
127 | m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||
128 | m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; | ||
129 | } | ||
130 | |||
131 | /// <summary> | ||
132 | /// Enables terrain module when called | ||
133 | /// </summary> | ||
134 | public void PostInitialise() | ||
135 | { | ||
136 | InstallDefaultEffects(); | ||
137 | InstallInterfaces(); | ||
138 | LoadPlugins(); | ||
139 | } | ||
140 | |||
141 | public void Close() | ||
142 | { | ||
143 | } | ||
144 | |||
145 | public string Name | ||
146 | { | ||
147 | get { return "TerrainModule"; } | ||
148 | } | ||
149 | |||
150 | public bool IsSharedModule | ||
151 | { | ||
152 | get { return false; } | ||
153 | } | ||
154 | |||
155 | #endregion | ||
156 | |||
157 | #region ITerrainModule Members | ||
158 | |||
159 | /// <summary> | ||
160 | /// Loads a terrain file from disk and installs it in the scene. | ||
161 | /// </summary> | ||
162 | /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> | ||
163 | public void LoadFromFile(string filename) | ||
164 | { | ||
165 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | ||
166 | { | ||
167 | if (filename.EndsWith(loader.Key)) | ||
168 | { | ||
169 | lock (m_scene) | ||
170 | { | ||
171 | try | ||
172 | { | ||
173 | ITerrainChannel channel = loader.Value.LoadFile(filename); | ||
174 | if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) | ||
175 | { | ||
176 | // TerrainChannel expects a RegionSize x RegionSize map, currently | ||
177 | throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", | ||
178 | Constants.RegionSize, Constants.RegionSize)); | ||
179 | } | ||
180 | m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); | ||
181 | m_scene.Heightmap = channel; | ||
182 | m_channel = channel; | ||
183 | UpdateRevertMap(); | ||
184 | } | ||
185 | catch (NotImplementedException) | ||
186 | { | ||
187 | m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + | ||
188 | " parser does not support file loading. (May be save only)"); | ||
189 | throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value)); | ||
190 | } | ||
191 | catch (FileNotFoundException) | ||
192 | { | ||
193 | m_log.Error( | ||
194 | "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)"); | ||
195 | throw new TerrainException( | ||
196 | String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename)); | ||
197 | } | ||
198 | catch (ArgumentException e) | ||
199 | { | ||
200 | m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message); | ||
201 | throw new TerrainException( | ||
202 | String.Format("Unable to load heightmap: {0}", e.Message)); | ||
203 | } | ||
204 | } | ||
205 | CheckForTerrainUpdates(); | ||
206 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); | ||
207 | return; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format."); | ||
212 | throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename)); | ||
213 | } | ||
214 | |||
215 | /// <summary> | ||
216 | /// Saves the current heightmap to a specified file. | ||
217 | /// </summary> | ||
218 | /// <param name="filename">The destination filename</param> | ||
219 | public void SaveToFile(string filename) | ||
220 | { | ||
221 | try | ||
222 | { | ||
223 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | ||
224 | { | ||
225 | if (filename.EndsWith(loader.Key)) | ||
226 | { | ||
227 | loader.Value.SaveFile(filename, m_channel); | ||
228 | return; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | catch (NotImplementedException) | ||
233 | { | ||
234 | m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented."); | ||
235 | throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented")); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /// <summary> | ||
240 | /// Loads a terrain file from a stream and installs it in the scene. | ||
241 | /// </summary> | ||
242 | /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> | ||
243 | /// <param name="stream"></param> | ||
244 | public void LoadFromStream(string filename, Stream stream) | ||
245 | { | ||
246 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | ||
247 | { | ||
248 | if (@filename.EndsWith(loader.Key)) | ||
249 | { | ||
250 | lock (m_scene) | ||
251 | { | ||
252 | try | ||
253 | { | ||
254 | ITerrainChannel channel = loader.Value.LoadStream(stream); | ||
255 | m_scene.Heightmap = channel; | ||
256 | m_channel = channel; | ||
257 | UpdateRevertMap(); | ||
258 | } | ||
259 | catch (NotImplementedException) | ||
260 | { | ||
261 | m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + | ||
262 | " parser does not support file loading. (May be save only)"); | ||
263 | throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value)); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | CheckForTerrainUpdates(); | ||
268 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); | ||
269 | return; | ||
270 | } | ||
271 | } | ||
272 | m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format."); | ||
273 | throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename)); | ||
274 | } | ||
275 | |||
276 | /// <summary> | ||
277 | /// Modify Land | ||
278 | /// </summary> | ||
279 | /// <param name="pos">Land-position (X,Y,0)</param> | ||
280 | /// <param name="size">The size of the brush (0=small, 1=medium, 2=large)</param> | ||
281 | /// <param name="action">0=LAND_LEVEL, 1=LAND_RAISE, 2=LAND_LOWER, 3=LAND_SMOOTH, 4=LAND_NOISE, 5=LAND_REVERT</param> | ||
282 | /// <param name="agentId">UUID of script-owner</param> | ||
283 | public void ModifyTerrain(UUID user, Vector3 pos, byte size, byte action, UUID agentId) | ||
284 | { | ||
285 | client_OnModifyTerrain(user, (float)pos.Z, (float)0.25, size, action, pos.Y, pos.X, pos.Y, pos.X, agentId); | ||
286 | } | ||
287 | |||
288 | /// <summary> | ||
289 | /// Saves the current heightmap to a specified stream. | ||
290 | /// </summary> | ||
291 | /// <param name="filename">The destination filename. Used here only to identify the image type</param> | ||
292 | /// <param name="stream"></param> | ||
293 | public void SaveToStream(string filename, Stream stream) | ||
294 | { | ||
295 | try | ||
296 | { | ||
297 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | ||
298 | { | ||
299 | if (filename.EndsWith(loader.Key)) | ||
300 | { | ||
301 | loader.Value.SaveStream(stream, m_channel); | ||
302 | return; | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | catch (NotImplementedException) | ||
307 | { | ||
308 | m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented."); | ||
309 | throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented")); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | #region Plugin Loading Methods | ||
314 | |||
315 | private void LoadPlugins() | ||
316 | { | ||
317 | m_plugineffects = new Dictionary<string, ITerrainEffect>(); | ||
318 | // Load the files in the Terrain/ dir | ||
319 | string[] files = Directory.GetFiles("Terrain"); | ||
320 | foreach (string file in files) | ||
321 | { | ||
322 | m_log.Info("Loading effects in " + file); | ||
323 | try | ||
324 | { | ||
325 | Assembly library = Assembly.LoadFrom(file); | ||
326 | foreach (Type pluginType in library.GetTypes()) | ||
327 | { | ||
328 | try | ||
329 | { | ||
330 | if (pluginType.IsAbstract || pluginType.IsNotPublic) | ||
331 | continue; | ||
332 | |||
333 | string typeName = pluginType.Name; | ||
334 | |||
335 | if (pluginType.GetInterface("ITerrainEffect", false) != null) | ||
336 | { | ||
337 | ITerrainEffect terEffect = (ITerrainEffect) Activator.CreateInstance(library.GetType(pluginType.ToString())); | ||
338 | |||
339 | InstallPlugin(typeName, terEffect); | ||
340 | } | ||
341 | else if (pluginType.GetInterface("ITerrainLoader", false) != null) | ||
342 | { | ||
343 | ITerrainLoader terLoader = (ITerrainLoader) Activator.CreateInstance(library.GetType(pluginType.ToString())); | ||
344 | m_loaders[terLoader.FileExtension] = terLoader; | ||
345 | m_log.Info("L ... " + typeName); | ||
346 | } | ||
347 | } | ||
348 | catch (AmbiguousMatchException) | ||
349 | { | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | catch (BadImageFormatException) | ||
354 | { | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | public void InstallPlugin(string pluginName, ITerrainEffect effect) | ||
360 | { | ||
361 | lock (m_plugineffects) | ||
362 | { | ||
363 | if (!m_plugineffects.ContainsKey(pluginName)) | ||
364 | { | ||
365 | m_plugineffects.Add(pluginName, effect); | ||
366 | m_log.Info("E ... " + pluginName); | ||
367 | } | ||
368 | else | ||
369 | { | ||
370 | m_plugineffects[pluginName] = effect; | ||
371 | m_log.Warn("E ... " + pluginName + " (Replaced)"); | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | |||
376 | #endregion | ||
377 | |||
378 | #endregion | ||
379 | |||
380 | /// <summary> | ||
381 | /// Installs into terrain module the standard suite of brushes | ||
382 | /// </summary> | ||
383 | private void InstallDefaultEffects() | ||
384 | { | ||
385 | // Draggable Paint Brush Effects | ||
386 | m_painteffects[StandardTerrainEffects.Raise] = new RaiseSphere(); | ||
387 | m_painteffects[StandardTerrainEffects.Lower] = new LowerSphere(); | ||
388 | m_painteffects[StandardTerrainEffects.Smooth] = new SmoothSphere(); | ||
389 | m_painteffects[StandardTerrainEffects.Noise] = new NoiseSphere(); | ||
390 | m_painteffects[StandardTerrainEffects.Flatten] = new FlattenSphere(); | ||
391 | m_painteffects[StandardTerrainEffects.Revert] = new RevertSphere(m_revert); | ||
392 | m_painteffects[StandardTerrainEffects.Erode] = new ErodeSphere(); | ||
393 | m_painteffects[StandardTerrainEffects.Weather] = new WeatherSphere(); | ||
394 | m_painteffects[StandardTerrainEffects.Olsen] = new OlsenSphere(); | ||
395 | |||
396 | // Area of effect selection effects | ||
397 | m_floodeffects[StandardTerrainEffects.Raise] = new RaiseArea(); | ||
398 | m_floodeffects[StandardTerrainEffects.Lower] = new LowerArea(); | ||
399 | m_floodeffects[StandardTerrainEffects.Smooth] = new SmoothArea(); | ||
400 | m_floodeffects[StandardTerrainEffects.Noise] = new NoiseArea(); | ||
401 | m_floodeffects[StandardTerrainEffects.Flatten] = new FlattenArea(); | ||
402 | m_floodeffects[StandardTerrainEffects.Revert] = new RevertArea(m_revert); | ||
403 | |||
404 | // Filesystem load/save loaders | ||
405 | m_loaders[".r32"] = new RAW32(); | ||
406 | m_loaders[".f32"] = m_loaders[".r32"]; | ||
407 | m_loaders[".ter"] = new Terragen(); | ||
408 | m_loaders[".raw"] = new LLRAW(); | ||
409 | m_loaders[".jpg"] = new JPEG(); | ||
410 | m_loaders[".jpeg"] = m_loaders[".jpg"]; | ||
411 | m_loaders[".bmp"] = new BMP(); | ||
412 | m_loaders[".png"] = new PNG(); | ||
413 | m_loaders[".gif"] = new GIF(); | ||
414 | m_loaders[".tif"] = new TIFF(); | ||
415 | m_loaders[".tiff"] = m_loaders[".tif"]; | ||
416 | } | ||
417 | |||
418 | /// <summary> | ||
419 | /// Saves the current state of the region into the revert map buffer. | ||
420 | /// </summary> | ||
421 | public void UpdateRevertMap() | ||
422 | { | ||
423 | int x; | ||
424 | for (x = 0; x < m_channel.Width; x++) | ||
425 | { | ||
426 | int y; | ||
427 | for (y = 0; y < m_channel.Height; y++) | ||
428 | { | ||
429 | m_revert[x, y] = m_channel[x, y]; | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /// <summary> | ||
435 | /// Loads a tile from a larger terrain file and installs it into the region. | ||
436 | /// </summary> | ||
437 | /// <param name="filename">The terrain file to load</param> | ||
438 | /// <param name="fileWidth">The width of the file in units</param> | ||
439 | /// <param name="fileHeight">The height of the file in units</param> | ||
440 | /// <param name="fileStartX">Where to begin our slice</param> | ||
441 | /// <param name="fileStartY">Where to begin our slice</param> | ||
442 | public void LoadFromFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY) | ||
443 | { | ||
444 | int offsetX = (int) m_scene.RegionInfo.RegionLocX - fileStartX; | ||
445 | int offsetY = (int) m_scene.RegionInfo.RegionLocY - fileStartY; | ||
446 | |||
447 | if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight) | ||
448 | { | ||
449 | // this region is included in the tile request | ||
450 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | ||
451 | { | ||
452 | if (filename.EndsWith(loader.Key)) | ||
453 | { | ||
454 | lock (m_scene) | ||
455 | { | ||
456 | ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, | ||
457 | fileWidth, fileHeight, | ||
458 | (int) Constants.RegionSize, | ||
459 | (int) Constants.RegionSize); | ||
460 | m_scene.Heightmap = channel; | ||
461 | m_channel = channel; | ||
462 | UpdateRevertMap(); | ||
463 | } | ||
464 | return; | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | } | ||
469 | |||
470 | /// <summary> | ||
471 | /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections | ||
472 | /// </summary> | ||
473 | private void EventManager_OnTerrainTick() | ||
474 | { | ||
475 | if (m_tainted) | ||
476 | { | ||
477 | m_tainted = false; | ||
478 | m_scene.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised()); | ||
479 | m_scene.SaveTerrain(); | ||
480 | |||
481 | // Clients who look at the map will never see changes after they looked at the map, so i've commented this out. | ||
482 | //m_scene.CreateTerrainTexture(true); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | /// <summary> | ||
487 | /// Processes commandline input. Do not call directly. | ||
488 | /// </summary> | ||
489 | /// <param name="args">Commandline arguments</param> | ||
490 | private void EventManager_OnPluginConsole(string[] args) | ||
491 | { | ||
492 | if (args[0] == "terrain") | ||
493 | { | ||
494 | if (args.Length == 1) | ||
495 | { | ||
496 | m_commander.ProcessConsoleCommand("help", new string[0]); | ||
497 | return; | ||
498 | } | ||
499 | |||
500 | string[] tmpArgs = new string[args.Length - 2]; | ||
501 | int i; | ||
502 | for (i = 2; i < args.Length; i++) | ||
503 | tmpArgs[i - 2] = args[i]; | ||
504 | |||
505 | m_commander.ProcessConsoleCommand(args[1], tmpArgs); | ||
506 | } | ||
507 | } | ||
508 | |||
509 | /// <summary> | ||
510 | /// Installs terrain brush hook to IClientAPI | ||
511 | /// </summary> | ||
512 | /// <param name="client"></param> | ||
513 | private void EventManager_OnNewClient(IClientAPI client) | ||
514 | { | ||
515 | client.OnModifyTerrain += client_OnModifyTerrain; | ||
516 | client.OnBakeTerrain += client_OnBakeTerrain; | ||
517 | } | ||
518 | |||
519 | /// <summary> | ||
520 | /// Checks to see if the terrain has been modified since last check | ||
521 | /// but won't attempt to limit those changes to the limits specified in the estate settings | ||
522 | /// currently invoked by the command line operations in the region server only | ||
523 | /// </summary> | ||
524 | private void CheckForTerrainUpdates() | ||
525 | { | ||
526 | CheckForTerrainUpdates(false); | ||
527 | } | ||
528 | |||
529 | /// <summary> | ||
530 | /// Checks to see if the terrain has been modified since last check. | ||
531 | /// If it has been modified, every all the terrain patches are sent to the client. | ||
532 | /// If the call is asked to respect the estate settings for terrain_raise_limit and | ||
533 | /// terrain_lower_limit, it will clamp terrain updates between these values | ||
534 | /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces | ||
535 | /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param> | ||
536 | /// </summary> | ||
537 | private void CheckForTerrainUpdates(bool respectEstateSettings) | ||
538 | { | ||
539 | bool shouldTaint = false; | ||
540 | float[] serialised = m_channel.GetFloatsSerialised(); | ||
541 | int x; | ||
542 | for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) | ||
543 | { | ||
544 | int y; | ||
545 | for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize) | ||
546 | { | ||
547 | if (m_channel.Tainted(x, y)) | ||
548 | { | ||
549 | // if we should respect the estate settings then | ||
550 | // fixup and height deltas that don't respect them | ||
551 | if (respectEstateSettings && LimitChannelChanges(x, y)) | ||
552 | { | ||
553 | // this has been vetoed, so update | ||
554 | // what we are going to send to the client | ||
555 | serialised = m_channel.GetFloatsSerialised(); | ||
556 | } | ||
557 | |||
558 | SendToClients(serialised, x, y); | ||
559 | shouldTaint = true; | ||
560 | } | ||
561 | } | ||
562 | } | ||
563 | if (shouldTaint) | ||
564 | { | ||
565 | m_tainted = true; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | /// <summary> | ||
570 | /// Checks to see height deltas in the tainted terrain patch at xStart ,yStart | ||
571 | /// are all within the current estate limits | ||
572 | /// <returns>true if changes were limited, false otherwise</returns> | ||
573 | /// </summary> | ||
574 | private bool LimitChannelChanges(int xStart, int yStart) | ||
575 | { | ||
576 | bool changesLimited = false; | ||
577 | double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; | ||
578 | double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; | ||
579 | |||
580 | // loop through the height map for this patch and compare it against | ||
581 | // the revert map | ||
582 | for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) | ||
583 | { | ||
584 | for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) | ||
585 | { | ||
586 | |||
587 | double requestedHeight = m_channel[x, y]; | ||
588 | double bakedHeight = m_revert[x, y]; | ||
589 | double requestedDelta = requestedHeight - bakedHeight; | ||
590 | |||
591 | if (requestedDelta > maxDelta) | ||
592 | { | ||
593 | m_channel[x, y] = bakedHeight + maxDelta; | ||
594 | changesLimited = true; | ||
595 | } | ||
596 | else if (requestedDelta < minDelta) | ||
597 | { | ||
598 | m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta | ||
599 | changesLimited = true; | ||
600 | } | ||
601 | } | ||
602 | } | ||
603 | |||
604 | return changesLimited; | ||
605 | } | ||
606 | |||
607 | /// <summary> | ||
608 | /// Sends a copy of the current terrain to the scenes clients | ||
609 | /// </summary> | ||
610 | /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> | ||
611 | /// <param name="x">The patch corner to send</param> | ||
612 | /// <param name="y">The patch corner to send</param> | ||
613 | private void SendToClients(float[] serialised, int x, int y) | ||
614 | { | ||
615 | m_scene.ForEachClient( | ||
616 | delegate(IClientAPI controller) | ||
617 | { controller.SendLayerData( | ||
618 | x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); | ||
619 | } | ||
620 | ); | ||
621 | } | ||
622 | |||
623 | private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, | ||
624 | float north, float west, float south, float east, UUID agentId) | ||
625 | { | ||
626 | bool god = m_scene.Permissions.IsGod(user); | ||
627 | bool allowed = false; | ||
628 | if (north == south && east == west) | ||
629 | { | ||
630 | if (m_painteffects.ContainsKey((StandardTerrainEffects) action)) | ||
631 | { | ||
632 | bool[,] allowMask = new bool[m_channel.Width,m_channel.Height]; | ||
633 | allowMask.Initialize(); | ||
634 | int n = size + 1; | ||
635 | if (n > 2) | ||
636 | n = 4; | ||
637 | |||
638 | int zx = (int) (west + 0.5); | ||
639 | int zy = (int) (north + 0.5); | ||
640 | |||
641 | int dx; | ||
642 | for (dx=-n; dx<=n; dx++) | ||
643 | { | ||
644 | int dy; | ||
645 | for (dy=-n; dy<=n; dy++) | ||
646 | { | ||
647 | int x = zx + dx; | ||
648 | int y = zy + dy; | ||
649 | if (x>=0 && y>=0 && x<m_channel.Width && y<m_channel.Height) | ||
650 | { | ||
651 | if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) | ||
652 | { | ||
653 | allowMask[x, y] = true; | ||
654 | allowed = true; | ||
655 | } | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | if (allowed) | ||
660 | { | ||
661 | m_painteffects[(StandardTerrainEffects) action].PaintEffect( | ||
662 | m_channel, allowMask, west, south, height, size, seconds); | ||
663 | |||
664 | CheckForTerrainUpdates(!god); //revert changes outside estate limits | ||
665 | } | ||
666 | } | ||
667 | else | ||
668 | { | ||
669 | m_log.Debug("Unknown terrain brush type " + action); | ||
670 | } | ||
671 | } | ||
672 | else | ||
673 | { | ||
674 | if (m_floodeffects.ContainsKey((StandardTerrainEffects) action)) | ||
675 | { | ||
676 | bool[,] fillArea = new bool[m_channel.Width,m_channel.Height]; | ||
677 | fillArea.Initialize(); | ||
678 | |||
679 | int x; | ||
680 | for (x = 0; x < m_channel.Width; x++) | ||
681 | { | ||
682 | int y; | ||
683 | for (y = 0; y < m_channel.Height; y++) | ||
684 | { | ||
685 | if (x < east && x > west) | ||
686 | { | ||
687 | if (y < north && y > south) | ||
688 | { | ||
689 | if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) | ||
690 | { | ||
691 | fillArea[x, y] = true; | ||
692 | allowed = true; | ||
693 | } | ||
694 | } | ||
695 | } | ||
696 | } | ||
697 | } | ||
698 | |||
699 | if (allowed) | ||
700 | { | ||
701 | m_floodeffects[(StandardTerrainEffects) action].FloodEffect( | ||
702 | m_channel, fillArea, size); | ||
703 | |||
704 | CheckForTerrainUpdates(!god); //revert changes outside estate limits | ||
705 | } | ||
706 | } | ||
707 | else | ||
708 | { | ||
709 | m_log.Debug("Unknown terrain flood type " + action); | ||
710 | } | ||
711 | } | ||
712 | } | ||
713 | |||
714 | private void client_OnBakeTerrain(IClientAPI remoteClient) | ||
715 | { | ||
716 | // Not a good permissions check (see client_OnModifyTerrain above), need to check the entire area. | ||
717 | // for now check a point in the centre of the region | ||
718 | |||
719 | if (m_scene.Permissions.CanIssueEstateCommand(remoteClient.AgentId, true)) | ||
720 | { | ||
721 | InterfaceBakeTerrain(null); //bake terrain does not use the passed in parameter | ||
722 | } | ||
723 | } | ||
724 | |||
725 | #region Console Commands | ||
726 | |||
727 | private void InterfaceLoadFile(Object[] args) | ||
728 | { | ||
729 | LoadFromFile((string) args[0]); | ||
730 | CheckForTerrainUpdates(); | ||
731 | } | ||
732 | |||
733 | private void InterfaceLoadTileFile(Object[] args) | ||
734 | { | ||
735 | LoadFromFile((string) args[0], | ||
736 | (int) args[1], | ||
737 | (int) args[2], | ||
738 | (int) args[3], | ||
739 | (int) args[4]); | ||
740 | CheckForTerrainUpdates(); | ||
741 | } | ||
742 | |||
743 | private void InterfaceSaveFile(Object[] args) | ||
744 | { | ||
745 | SaveToFile((string) args[0]); | ||
746 | } | ||
747 | |||
748 | private void InterfaceBakeTerrain(Object[] args) | ||
749 | { | ||
750 | UpdateRevertMap(); | ||
751 | } | ||
752 | |||
753 | private void InterfaceRevertTerrain(Object[] args) | ||
754 | { | ||
755 | int x, y; | ||
756 | for (x = 0; x < m_channel.Width; x++) | ||
757 | for (y = 0; y < m_channel.Height; y++) | ||
758 | m_channel[x, y] = m_revert[x, y]; | ||
759 | |||
760 | CheckForTerrainUpdates(); | ||
761 | } | ||
762 | |||
763 | private void InterfaceFlipTerrain(Object[] args) | ||
764 | { | ||
765 | String direction = (String)args[0]; | ||
766 | |||
767 | if (direction.ToLower().StartsWith("y")) | ||
768 | { | ||
769 | for (int x = 0; x < Constants.RegionSize; x++) | ||
770 | { | ||
771 | for (int y = 0; y < Constants.RegionSize / 2; y++) | ||
772 | { | ||
773 | double height = m_channel[x, y]; | ||
774 | double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; | ||
775 | m_channel[x, y] = flippedHeight; | ||
776 | m_channel[x, (int)Constants.RegionSize - 1 - y] = height; | ||
777 | |||
778 | } | ||
779 | } | ||
780 | } | ||
781 | else if (direction.ToLower().StartsWith("x")) | ||
782 | { | ||
783 | for (int y = 0; y < Constants.RegionSize; y++) | ||
784 | { | ||
785 | for (int x = 0; x < Constants.RegionSize / 2; x++) | ||
786 | { | ||
787 | double height = m_channel[x, y]; | ||
788 | double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; | ||
789 | m_channel[x, y] = flippedHeight; | ||
790 | m_channel[(int)Constants.RegionSize - 1 - x, y] = height; | ||
791 | |||
792 | } | ||
793 | } | ||
794 | } | ||
795 | else | ||
796 | { | ||
797 | m_log.Error("Unrecognised direction - need x or y"); | ||
798 | } | ||
799 | |||
800 | |||
801 | CheckForTerrainUpdates(); | ||
802 | } | ||
803 | |||
804 | private void InterfaceElevateTerrain(Object[] args) | ||
805 | { | ||
806 | int x, y; | ||
807 | for (x = 0; x < m_channel.Width; x++) | ||
808 | for (y = 0; y < m_channel.Height; y++) | ||
809 | m_channel[x, y] += (double) args[0]; | ||
810 | CheckForTerrainUpdates(); | ||
811 | } | ||
812 | |||
813 | private void InterfaceMultiplyTerrain(Object[] args) | ||
814 | { | ||
815 | int x, y; | ||
816 | for (x = 0; x < m_channel.Width; x++) | ||
817 | for (y = 0; y < m_channel.Height; y++) | ||
818 | m_channel[x, y] *= (double) args[0]; | ||
819 | CheckForTerrainUpdates(); | ||
820 | } | ||
821 | |||
822 | private void InterfaceLowerTerrain(Object[] args) | ||
823 | { | ||
824 | int x, y; | ||
825 | for (x = 0; x < m_channel.Width; x++) | ||
826 | for (y = 0; y < m_channel.Height; y++) | ||
827 | m_channel[x, y] -= (double) args[0]; | ||
828 | CheckForTerrainUpdates(); | ||
829 | } | ||
830 | |||
831 | private void InterfaceFillTerrain(Object[] args) | ||
832 | { | ||
833 | int x, y; | ||
834 | |||
835 | for (x = 0; x < m_channel.Width; x++) | ||
836 | for (y = 0; y < m_channel.Height; y++) | ||
837 | m_channel[x, y] = (double) args[0]; | ||
838 | CheckForTerrainUpdates(); | ||
839 | } | ||
840 | |||
841 | private void InterfaceShowDebugStats(Object[] args) | ||
842 | { | ||
843 | double max = Double.MinValue; | ||
844 | double min = double.MaxValue; | ||
845 | double sum = 0; | ||
846 | |||
847 | int x; | ||
848 | for (x = 0; x < m_channel.Width; x++) | ||
849 | { | ||
850 | int y; | ||
851 | for (y = 0; y < m_channel.Height; y++) | ||
852 | { | ||
853 | sum += m_channel[x, y]; | ||
854 | if (max < m_channel[x, y]) | ||
855 | max = m_channel[x, y]; | ||
856 | if (min > m_channel[x, y]) | ||
857 | min = m_channel[x, y]; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | double avg = sum / (m_channel.Height * m_channel.Width); | ||
862 | |||
863 | m_log.Info("Channel " + m_channel.Width + "x" + m_channel.Height); | ||
864 | m_log.Info("max/min/avg/sum: " + max + "/" + min + "/" + avg + "/" + sum); | ||
865 | } | ||
866 | |||
867 | private void InterfaceEnableExperimentalBrushes(Object[] args) | ||
868 | { | ||
869 | if ((bool) args[0]) | ||
870 | { | ||
871 | m_painteffects[StandardTerrainEffects.Revert] = new WeatherSphere(); | ||
872 | m_painteffects[StandardTerrainEffects.Flatten] = new OlsenSphere(); | ||
873 | m_painteffects[StandardTerrainEffects.Smooth] = new ErodeSphere(); | ||
874 | } | ||
875 | else | ||
876 | { | ||
877 | InstallDefaultEffects(); | ||
878 | } | ||
879 | } | ||
880 | |||
881 | private void InterfaceRunPluginEffect(Object[] args) | ||
882 | { | ||
883 | if ((string) args[0] == "list") | ||
884 | { | ||
885 | m_log.Info("List of loaded plugins"); | ||
886 | foreach (KeyValuePair<string, ITerrainEffect> kvp in m_plugineffects) | ||
887 | { | ||
888 | m_log.Info(kvp.Key); | ||
889 | } | ||
890 | return; | ||
891 | } | ||
892 | if ((string) args[0] == "reload") | ||
893 | { | ||
894 | LoadPlugins(); | ||
895 | return; | ||
896 | } | ||
897 | if (m_plugineffects.ContainsKey((string) args[0])) | ||
898 | { | ||
899 | m_plugineffects[(string) args[0]].RunEffect(m_channel); | ||
900 | CheckForTerrainUpdates(); | ||
901 | } | ||
902 | else | ||
903 | { | ||
904 | m_log.Warn("No such plugin effect loaded."); | ||
905 | } | ||
906 | } | ||
907 | |||
908 | private void InstallInterfaces() | ||
909 | { | ||
910 | // Load / Save | ||
911 | string supportedFileExtensions = ""; | ||
912 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | ||
913 | supportedFileExtensions += " " + loader.Key + " (" + loader.Value + ")"; | ||
914 | |||
915 | Command loadFromFileCommand = | ||
916 | new Command("load", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLoadFile, "Loads a terrain from a specified file."); | ||
917 | loadFromFileCommand.AddArgument("filename", | ||
918 | "The file you wish to load from, the file extension determines the loader to be used. Supported extensions include: " + | ||
919 | supportedFileExtensions, "String"); | ||
920 | |||
921 | Command saveToFileCommand = | ||
922 | new Command("save", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceSaveFile, "Saves the current heightmap to a specified file."); | ||
923 | saveToFileCommand.AddArgument("filename", | ||
924 | "The destination filename for your heightmap, the file extension determines the format to save in. Supported extensions include: " + | ||
925 | supportedFileExtensions, "String"); | ||
926 | |||
927 | Command loadFromTileCommand = | ||
928 | new Command("load-tile", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLoadTileFile, "Loads a terrain from a section of a larger file."); | ||
929 | loadFromTileCommand.AddArgument("filename", | ||
930 | "The file you wish to load from, the file extension determines the loader to be used. Supported extensions include: " + | ||
931 | supportedFileExtensions, "String"); | ||
932 | loadFromTileCommand.AddArgument("file width", "The width of the file in tiles", "Integer"); | ||
933 | loadFromTileCommand.AddArgument("file height", "The height of the file in tiles", "Integer"); | ||
934 | loadFromTileCommand.AddArgument("minimum X tile", "The X region coordinate of the first section on the file", | ||
935 | "Integer"); | ||
936 | loadFromTileCommand.AddArgument("minimum Y tile", "The Y region coordinate of the first section on the file", | ||
937 | "Integer"); | ||
938 | |||
939 | // Terrain adjustments | ||
940 | Command fillRegionCommand = | ||
941 | new Command("fill", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFillTerrain, "Fills the current heightmap with a specified value."); | ||
942 | fillRegionCommand.AddArgument("value", "The numeric value of the height you wish to set your region to.", | ||
943 | "Double"); | ||
944 | |||
945 | Command elevateCommand = | ||
946 | new Command("elevate", CommandIntentions.COMMAND_HAZARDOUS, InterfaceElevateTerrain, "Raises the current heightmap by the specified amount."); | ||
947 | elevateCommand.AddArgument("amount", "The amount of height to add to the terrain in meters.", "Double"); | ||
948 | |||
949 | Command lowerCommand = | ||
950 | new Command("lower", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLowerTerrain, "Lowers the current heightmap by the specified amount."); | ||
951 | lowerCommand.AddArgument("amount", "The amount of height to remove from the terrain in meters.", "Double"); | ||
952 | |||
953 | Command multiplyCommand = | ||
954 | new Command("multiply", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMultiplyTerrain, "Multiplies the heightmap by the value specified."); | ||
955 | multiplyCommand.AddArgument("value", "The value to multiply the heightmap by.", "Double"); | ||
956 | |||
957 | Command bakeRegionCommand = | ||
958 | new Command("bake", CommandIntentions.COMMAND_HAZARDOUS, InterfaceBakeTerrain, "Saves the current terrain into the regions revert map."); | ||
959 | Command revertRegionCommand = | ||
960 | new Command("revert", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRevertTerrain, "Loads the revert map terrain into the regions heightmap."); | ||
961 | |||
962 | Command flipCommand = | ||
963 | new Command("flip", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFlipTerrain, "Flips the current terrain about the X or Y axis"); | ||
964 | flipCommand.AddArgument("direction", "[x|y] the direction to flip the terrain in", "String"); | ||
965 | |||
966 | // Debug | ||
967 | Command showDebugStatsCommand = | ||
968 | new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats, | ||
969 | "Shows some information about the regions heightmap for debugging purposes."); | ||
970 | |||
971 | Command experimentalBrushesCommand = | ||
972 | new Command("newbrushes", CommandIntentions.COMMAND_HAZARDOUS, InterfaceEnableExperimentalBrushes, | ||
973 | "Enables experimental brushes which replace the standard terrain brushes. WARNING: This is a debug setting and may be removed at any time."); | ||
974 | experimentalBrushesCommand.AddArgument("Enabled?", "true / false - Enable new brushes", "Boolean"); | ||
975 | |||
976 | //Plugins | ||
977 | Command pluginRunCommand = | ||
978 | new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect"); | ||
979 | pluginRunCommand.AddArgument("name", "The plugin effect you wish to run, or 'list' to see all plugins", "String"); | ||
980 | |||
981 | m_commander.RegisterCommand("load", loadFromFileCommand); | ||
982 | m_commander.RegisterCommand("load-tile", loadFromTileCommand); | ||
983 | m_commander.RegisterCommand("save", saveToFileCommand); | ||
984 | m_commander.RegisterCommand("fill", fillRegionCommand); | ||
985 | m_commander.RegisterCommand("elevate", elevateCommand); | ||
986 | m_commander.RegisterCommand("lower", lowerCommand); | ||
987 | m_commander.RegisterCommand("multiply", multiplyCommand); | ||
988 | m_commander.RegisterCommand("bake", bakeRegionCommand); | ||
989 | m_commander.RegisterCommand("revert", revertRegionCommand); | ||
990 | m_commander.RegisterCommand("newbrushes", experimentalBrushesCommand); | ||
991 | m_commander.RegisterCommand("stats", showDebugStatsCommand); | ||
992 | m_commander.RegisterCommand("effect", pluginRunCommand); | ||
993 | m_commander.RegisterCommand("flip", flipCommand); | ||
994 | |||
995 | // Add this to our scene so scripts can call these functions | ||
996 | m_scene.RegisterModuleCommander(m_commander); | ||
997 | } | ||
998 | |||
999 | #endregion | ||
1000 | } | ||
1001 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs new file mode 100644 index 0000000..e7f92d7 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using NUnit.Framework; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain.Tests | ||
34 | { | ||
35 | [TestFixture] | ||
36 | public class TerrainTest | ||
37 | { | ||
38 | [Test] | ||
39 | public void BrushTest() | ||
40 | { | ||
41 | bool[,] allowMask = new bool[256, 256]; | ||
42 | int x; | ||
43 | int y; | ||
44 | for (x=0; x<128; x++) | ||
45 | { | ||
46 | for (y=0; y<256; y++) | ||
47 | { | ||
48 | allowMask[x,y] = true; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | // | ||
53 | // Test RaiseSphere | ||
54 | // | ||
55 | TerrainChannel map = new TerrainChannel(256, 256); | ||
56 | ITerrainPaintableEffect effect = new RaiseSphere(); | ||
57 | |||
58 | effect.PaintEffect(map, allowMask, 128.0, 128.0, -1.0, 2, 0.1); | ||
59 | Assert.That(map[127, 128] > 0.0, "Raise brush should raising value at this point (127,128)."); | ||
60 | Assert.That(map[124, 128] > 0.0, "Raise brush should raising value at this point (124,128)."); | ||
61 | Assert.That(map[123, 128] == 0.0, "Raise brush should not change value at this point (123,128)."); | ||
62 | Assert.That(map[128, 128] == 0.0, "Raise brush should not change value at this point (128,128)."); | ||
63 | Assert.That(map[0, 128] == 0.0, "Raise brush should not change value at this point (0,128)."); | ||
64 | |||
65 | // | ||
66 | // Test LowerSphere | ||
67 | // | ||
68 | map = new TerrainChannel(256, 256); | ||
69 | for (x=0; x<map.Width; x++) | ||
70 | { | ||
71 | for (y=0; y<map.Height; y++) | ||
72 | { | ||
73 | map[x,y] = 1.0; | ||
74 | } | ||
75 | } | ||
76 | effect = new LowerSphere(); | ||
77 | |||
78 | effect.PaintEffect(map, allowMask, 128.0, 128.0, -1.0, 2, 6.0); | ||
79 | Assert.That(map[127, 128] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128)."); | ||
80 | Assert.That(map[127, 128] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128)."); | ||
81 | Assert.That(map[124, 128] < 1.0, "Lower brush should lowering value at this point (124,128)."); | ||
82 | Assert.That(map[123, 128] == 1.0, "Lower brush should not change value at this point (123,128)."); | ||
83 | Assert.That(map[128, 128] == 1.0, "Lower brush should not change value at this point (128,128)."); | ||
84 | Assert.That(map[0, 128] == 1.0, "Lower brush should not change value at this point (0,128)."); | ||
85 | } | ||
86 | |||
87 | [Test] | ||
88 | public void TerrainChannelTest() | ||
89 | { | ||
90 | TerrainChannel x = new TerrainChannel(256, 256); | ||
91 | Assert.That(x[0, 0] == 0.0, "Terrain not initialising correctly."); | ||
92 | |||
93 | x[0, 0] = 1.0; | ||
94 | Assert.That(x[0, 0] == 1.0, "Terrain not setting values correctly."); | ||
95 | |||
96 | x[0, 0] = 0; | ||
97 | x[0, 0] += 5.0; | ||
98 | x[0, 0] -= 1.0; | ||
99 | Assert.That(x[0, 0] == 4.0, "Terrain addition/subtraction error."); | ||
100 | |||
101 | x[0, 0] = Math.PI; | ||
102 | double[,] doublesExport = x.GetDoubles(); | ||
103 | Assert.That(doublesExport[0, 0] == Math.PI, "Export to double[,] array not working correctly."); | ||
104 | |||
105 | x[0, 0] = 1.0; | ||
106 | float[] floatsExport = x.GetFloatsSerialised(); | ||
107 | Assert.That(floatsExport[0] == 1.0f, "Export to float[] not working correctly."); | ||
108 | |||
109 | x[0, 0] = 1.0; | ||
110 | Assert.That(x.Tainted(0, 0), "Terrain channel tainting not working correctly."); | ||
111 | Assert.That(!x.Tainted(0, 0), "Terrain channel tainting not working correctly."); | ||
112 | |||
113 | TerrainChannel y = x.Copy(); | ||
114 | Assert.That(!ReferenceEquals(x, y), "Terrain copy not duplicating correctly."); | ||
115 | Assert.That(!ReferenceEquals(x.GetDoubles(), y.GetDoubles()), "Terrain array not duplicating correctly."); | ||
116 | } | ||
117 | } | ||
118 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Vegetation/VegetationModule.cs b/OpenSim/Region/CoreModules/World/Vegetation/VegetationModule.cs new file mode 100644 index 0000000..8b2bb1e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Vegetation/VegetationModule.cs | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Reflection; | ||
30 | using log4net; | ||
31 | using Nini.Config; | ||
32 | using OpenMetaverse; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Framework.Communications; | ||
35 | using OpenSim.Framework.Communications.Cache; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.Framework.Scenes; | ||
38 | |||
39 | namespace OpenSim.Region.CoreModules.Avatar.Vegetation | ||
40 | { | ||
41 | public class VegetationModule : IRegionModule, IVegetationModule | ||
42 | { | ||
43 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
44 | |||
45 | protected Scene m_scene; | ||
46 | |||
47 | protected static readonly PCode[] creationCapabilities = new PCode[] { PCode.Grass, PCode.NewTree, PCode.Tree }; | ||
48 | public PCode[] CreationCapabilities { get { return creationCapabilities; } } | ||
49 | |||
50 | public void Initialise(Scene scene, IConfigSource source) | ||
51 | { | ||
52 | m_scene = scene; | ||
53 | m_scene.RegisterModuleInterface<IVegetationModule>(this); | ||
54 | } | ||
55 | |||
56 | public void PostInitialise() {} | ||
57 | public void Close() {} | ||
58 | public string Name { get { return "Vegetation Module"; } } | ||
59 | public bool IsSharedModule { get { return false; } } | ||
60 | |||
61 | public SceneObjectGroup AddTree( | ||
62 | UUID uuid, UUID groupID, Vector3 scale, Quaternion rotation, Vector3 position, Tree treeType, bool newTree) | ||
63 | { | ||
64 | PrimitiveBaseShape treeShape = new PrimitiveBaseShape(); | ||
65 | treeShape.PathCurve = 16; | ||
66 | treeShape.PathEnd = 49900; | ||
67 | treeShape.PCode = newTree ? (byte)PCode.NewTree : (byte)PCode.Tree; | ||
68 | treeShape.Scale = scale; | ||
69 | treeShape.State = (byte)treeType; | ||
70 | |||
71 | return m_scene.AddNewPrim(uuid, groupID, position, rotation, treeShape); | ||
72 | } | ||
73 | |||
74 | public SceneObjectGroup CreateEntity( | ||
75 | UUID ownerID, UUID groupID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape) | ||
76 | { | ||
77 | if (Array.IndexOf(creationCapabilities, (PCode)shape.PCode) < 0) | ||
78 | { | ||
79 | m_log.DebugFormat("[VEGETATION]: PCode {0} not handled by {1}", shape.PCode, Name); | ||
80 | return null; | ||
81 | } | ||
82 | |||
83 | SceneObjectGroup sceneObject = new SceneObjectGroup(ownerID, pos, rot, shape); | ||
84 | SceneObjectPart rootPart = sceneObject.GetChildPart(sceneObject.UUID); | ||
85 | |||
86 | // if grass or tree, make phantom | ||
87 | //rootPart.TrimPermissions(); | ||
88 | rootPart.AddFlag(PrimFlags.Phantom); | ||
89 | if (rootPart.Shape.PCode != (byte)PCode.Grass) | ||
90 | AdaptTree(ref shape); | ||
91 | |||
92 | m_scene.AddNewSceneObject(sceneObject, true); | ||
93 | sceneObject.SetGroup(groupID, null); | ||
94 | |||
95 | return sceneObject; | ||
96 | } | ||
97 | |||
98 | protected void AdaptTree(ref PrimitiveBaseShape tree) | ||
99 | { | ||
100 | // Tree size has to be adapted depending on its type | ||
101 | switch ((Tree)tree.State) | ||
102 | { | ||
103 | case Tree.Cypress1: | ||
104 | case Tree.Cypress2: | ||
105 | tree.Scale = new Vector3(4, 4, 10); | ||
106 | break; | ||
107 | |||
108 | // case... other tree types | ||
109 | // tree.Scale = new Vector3(?, ?, ?); | ||
110 | // break; | ||
111 | |||
112 | default: | ||
113 | tree.Scale = new Vector3(4, 4, 4); | ||
114 | break; | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Wind/WindModule.cs b/OpenSim/Region/CoreModules/World/Wind/WindModule.cs new file mode 100644 index 0000000..b8bd605 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Wind/WindModule.cs | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using OpenMetaverse; | ||
31 | using Nini.Config; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.Framework.Interfaces; | ||
34 | using OpenSim.Region.Framework.Scenes; | ||
35 | |||
36 | namespace OpenSim.Region.CoreModules | ||
37 | { | ||
38 | public class WindModule : IWindModule | ||
39 | { | ||
40 | // private static readonly log4net.ILog m_log | ||
41 | // = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
42 | |||
43 | private int m_frame = 0; | ||
44 | private int m_frame_mod = 150; | ||
45 | private Random rndnums = new Random(System.Environment.TickCount); | ||
46 | private Scene m_scene = null; | ||
47 | private bool ready = false; | ||
48 | private Vector2[] windSpeeds = new Vector2[16 * 16]; | ||
49 | private Dictionary<UUID, ulong> m_rootAgents = new Dictionary<UUID, ulong>(); | ||
50 | |||
51 | public void Initialise(Scene scene, IConfigSource config) | ||
52 | { | ||
53 | m_scene = scene; | ||
54 | m_frame = 0; | ||
55 | |||
56 | scene.EventManager.OnFrame += WindUpdate; | ||
57 | scene.EventManager.OnMakeChildAgent += MakeChildAgent; | ||
58 | scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; | ||
59 | scene.EventManager.OnClientClosed += ClientLoggedOut; | ||
60 | scene.RegisterModuleInterface<IWindModule>(this); | ||
61 | |||
62 | GenWindPos(); | ||
63 | |||
64 | ready = true; | ||
65 | } | ||
66 | |||
67 | public void PostInitialise() | ||
68 | { | ||
69 | } | ||
70 | |||
71 | public void Close() | ||
72 | { | ||
73 | ready = false; | ||
74 | // Remove our hooks | ||
75 | m_scene.EventManager.OnFrame -= WindUpdate; | ||
76 | // m_scene.EventManager.OnNewClient -= SunToClient; | ||
77 | m_scene.EventManager.OnMakeChildAgent -= MakeChildAgent; | ||
78 | m_scene.EventManager.OnAvatarEnteringNewParcel -= AvatarEnteringParcel; | ||
79 | m_scene.EventManager.OnClientClosed -= ClientLoggedOut; | ||
80 | } | ||
81 | |||
82 | public string Name | ||
83 | { | ||
84 | get { return "WindModule"; } | ||
85 | } | ||
86 | |||
87 | public bool IsSharedModule | ||
88 | { | ||
89 | get { return false; } | ||
90 | } | ||
91 | |||
92 | public Vector2[] WindSpeeds | ||
93 | { | ||
94 | get { return windSpeeds; } | ||
95 | } | ||
96 | |||
97 | public void WindToClient(IClientAPI client) | ||
98 | { | ||
99 | if (ready) | ||
100 | { | ||
101 | //if (!sunFixed) | ||
102 | //GenWindPos(); // Generate shared values once | ||
103 | client.SendWindData(windSpeeds); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | public void WindUpdate() | ||
108 | { | ||
109 | if (((m_frame++ % m_frame_mod) != 0) || !ready) | ||
110 | { | ||
111 | return; | ||
112 | } | ||
113 | //m_log.Debug("[WIND]:Regenerating..."); | ||
114 | GenWindPos(); // Generate shared values once | ||
115 | |||
116 | //int spotxp = 0; | ||
117 | //int spotyp = 0; | ||
118 | //int spotxm = 0; | ||
119 | //int spotym = 0; | ||
120 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
121 | foreach (ScenePresence avatar in avatars) | ||
122 | { | ||
123 | if (!avatar.IsChildAgent) | ||
124 | { | ||
125 | avatar.ControllingClient.SendWindData(windSpeeds); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | // set estate settings for region access to sun position | ||
130 | //m_scene.RegionInfo.RegionSettings.SunVector = Position; | ||
131 | //m_scene.RegionInfo.EstateSettings.sunHour = GetLindenEstateHourFromCurrentTime(); | ||
132 | } | ||
133 | |||
134 | public void ForceWindUpdateToAllClients() | ||
135 | { | ||
136 | GenWindPos(); // Generate shared values once | ||
137 | |||
138 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
139 | foreach (ScenePresence avatar in avatars) | ||
140 | { | ||
141 | if (!avatar.IsChildAgent) | ||
142 | avatar.ControllingClient.SendWindData(windSpeeds); | ||
143 | } | ||
144 | |||
145 | // set estate settings for region access to sun position | ||
146 | //m_scene.RegionInfo.RegionSettings.SunVector = Position; | ||
147 | //m_scene.RegionInfo.RegionSettings.SunPosition = GetLindenEstateHourFromCurrentTime(); | ||
148 | } | ||
149 | /// <summary> | ||
150 | /// Calculate the sun's orbital position and its velocity. | ||
151 | /// </summary> | ||
152 | |||
153 | private void GenWindPos() | ||
154 | { | ||
155 | for (int y = 0; y < 16; y++) | ||
156 | { | ||
157 | for (int x = 0; x < 16; x++) | ||
158 | { | ||
159 | windSpeeds[y * 16 + x].X = (float)(rndnums.NextDouble() * 2d - 1d); | ||
160 | windSpeeds[y * 16 + x].Y = (float)(rndnums.NextDouble() * 2d - 1d); | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | private void ClientLoggedOut(UUID AgentId) | ||
166 | { | ||
167 | lock (m_rootAgents) | ||
168 | { | ||
169 | if (m_rootAgents.ContainsKey(AgentId)) | ||
170 | { | ||
171 | m_rootAgents.Remove(AgentId); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID) | ||
177 | { | ||
178 | lock (m_rootAgents) | ||
179 | { | ||
180 | if (m_rootAgents.ContainsKey(avatar.UUID)) | ||
181 | { | ||
182 | m_rootAgents[avatar.UUID] = avatar.RegionHandle; | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | m_rootAgents.Add(avatar.UUID, avatar.RegionHandle); | ||
187 | WindToClient(avatar.ControllingClient); | ||
188 | } | ||
189 | } | ||
190 | //m_log.Info("[FRIEND]: " + avatar.Name + " status:" + (!avatar.IsChildAgent).ToString()); | ||
191 | } | ||
192 | |||
193 | private void MakeChildAgent(ScenePresence avatar) | ||
194 | { | ||
195 | lock (m_rootAgents) | ||
196 | { | ||
197 | if (m_rootAgents.ContainsKey(avatar.UUID)) | ||
198 | { | ||
199 | if (m_rootAgents[avatar.UUID] == avatar.RegionHandle) | ||
200 | { | ||
201 | m_rootAgents.Remove(avatar.UUID); | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/IMapTileTerrainRenderer.cs b/OpenSim/Region/CoreModules/World/WorldMap/IMapTileTerrainRenderer.cs new file mode 100644 index 0000000..3684df0 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/WorldMap/IMapTileTerrainRenderer.cs | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System.Drawing; | ||
29 | using OpenSim.Region.Framework.Scenes; | ||
30 | using Nini.Config; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.WorldMap | ||
33 | { | ||
34 | public interface IMapTileTerrainRenderer | ||
35 | { | ||
36 | void Initialise(Scene scene, IConfigSource config); | ||
37 | void TerrainToBitmap(Bitmap mapbmp); | ||
38 | } | ||
39 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/MapImageModule.cs new file mode 100644 index 0000000..eea6cf6 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/WorldMap/MapImageModule.cs | |||
@@ -0,0 +1,586 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Drawing; | ||
32 | using System.Drawing.Drawing2D; | ||
33 | using System.Drawing.Imaging; | ||
34 | using System.Reflection; | ||
35 | using Nini.Config; | ||
36 | using OpenMetaverse.Imaging; | ||
37 | using log4net; | ||
38 | using OpenSim.Region.Framework.Interfaces; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenMetaverse; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.World.WorldMap | ||
43 | { | ||
44 | public enum DrawRoutine | ||
45 | { | ||
46 | Rectangle, | ||
47 | Polygon, | ||
48 | Ellipse | ||
49 | } | ||
50 | |||
51 | public struct face | ||
52 | { | ||
53 | public Point[] pts; | ||
54 | } | ||
55 | |||
56 | public struct DrawStruct | ||
57 | { | ||
58 | public DrawRoutine dr; | ||
59 | public Rectangle rect; | ||
60 | public SolidBrush brush; | ||
61 | public face[] trns; | ||
62 | } | ||
63 | |||
64 | public class MapImageModule : IMapImageGenerator, IRegionModule | ||
65 | { | ||
66 | private static readonly ILog m_log = | ||
67 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
68 | |||
69 | private Scene m_scene; | ||
70 | private IConfigSource m_config; | ||
71 | private IMapTileTerrainRenderer terrainRenderer; | ||
72 | |||
73 | #region IMapImageGenerator Members | ||
74 | |||
75 | public byte[] WriteJpeg2000Image(string gradientmap) | ||
76 | { | ||
77 | byte[] imageData = null; | ||
78 | |||
79 | bool drawPrimVolume = true; | ||
80 | bool textureTerrain = true; | ||
81 | |||
82 | try | ||
83 | { | ||
84 | IConfig startupConfig = m_config.Configs["Startup"]; | ||
85 | drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", drawPrimVolume); | ||
86 | textureTerrain = startupConfig.GetBoolean("TextureOnMapTile", textureTerrain); | ||
87 | } | ||
88 | catch | ||
89 | { | ||
90 | m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); | ||
91 | } | ||
92 | |||
93 | if (textureTerrain) | ||
94 | { | ||
95 | terrainRenderer = new TexturedMapTileRenderer(); | ||
96 | } | ||
97 | else | ||
98 | { | ||
99 | terrainRenderer = new ShadedMapTileRenderer(); | ||
100 | } | ||
101 | terrainRenderer.Initialise(m_scene, m_config); | ||
102 | |||
103 | Bitmap mapbmp = new Bitmap(256, 256); | ||
104 | //long t = System.Environment.TickCount; | ||
105 | //for (int i = 0; i < 10; ++i) { | ||
106 | terrainRenderer.TerrainToBitmap(mapbmp); | ||
107 | //} | ||
108 | //t = System.Environment.TickCount - t; | ||
109 | //m_log.InfoFormat("[MAPTILE] generation of 10 maptiles needed {0} ms", t); | ||
110 | |||
111 | |||
112 | if (drawPrimVolume) | ||
113 | { | ||
114 | DrawObjectVolume(m_scene, mapbmp); | ||
115 | } | ||
116 | |||
117 | try | ||
118 | { | ||
119 | imageData = OpenJPEG.EncodeFromImage(mapbmp, true); | ||
120 | } | ||
121 | catch (Exception e) // LEGIT: Catching problems caused by OpenJPEG p/invoke | ||
122 | { | ||
123 | Console.WriteLine("Failed generating terrain map: " + e); | ||
124 | } | ||
125 | |||
126 | return imageData; | ||
127 | } | ||
128 | |||
129 | #endregion | ||
130 | |||
131 | #region IRegionModule Members | ||
132 | |||
133 | public void Initialise(Scene scene, IConfigSource source) | ||
134 | { | ||
135 | m_scene = scene; | ||
136 | m_config = source; | ||
137 | |||
138 | IConfig startupConfig = m_config.Configs["Startup"]; | ||
139 | if (startupConfig.GetString("MapImageModule", "MapImageModule") != | ||
140 | "MapImageModule") | ||
141 | return; | ||
142 | |||
143 | m_scene.RegisterModuleInterface<IMapImageGenerator>(this); | ||
144 | } | ||
145 | |||
146 | public void PostInitialise() | ||
147 | { | ||
148 | } | ||
149 | |||
150 | public void Close() | ||
151 | { | ||
152 | } | ||
153 | |||
154 | public string Name | ||
155 | { | ||
156 | get { return "MapImageModule"; } | ||
157 | } | ||
158 | |||
159 | public bool IsSharedModule | ||
160 | { | ||
161 | get { return false; } | ||
162 | } | ||
163 | |||
164 | #endregion | ||
165 | |||
166 | // TODO: unused: | ||
167 | // private void ShadeBuildings(Bitmap map) | ||
168 | // { | ||
169 | // lock (map) | ||
170 | // { | ||
171 | // lock (m_scene.Entities) | ||
172 | // { | ||
173 | // foreach (EntityBase entity in m_scene.Entities.Values) | ||
174 | // { | ||
175 | // if (entity is SceneObjectGroup) | ||
176 | // { | ||
177 | // SceneObjectGroup sog = (SceneObjectGroup) entity; | ||
178 | // | ||
179 | // foreach (SceneObjectPart primitive in sog.Children.Values) | ||
180 | // { | ||
181 | // int x = (int) (primitive.AbsolutePosition.X - (primitive.Scale.X / 2)); | ||
182 | // int y = (int) (primitive.AbsolutePosition.Y - (primitive.Scale.Y / 2)); | ||
183 | // int w = (int) primitive.Scale.X; | ||
184 | // int h = (int) primitive.Scale.Y; | ||
185 | // | ||
186 | // int dx; | ||
187 | // for (dx = x; dx < x + w; dx++) | ||
188 | // { | ||
189 | // int dy; | ||
190 | // for (dy = y; dy < y + h; dy++) | ||
191 | // { | ||
192 | // if (x < 0 || y < 0) | ||
193 | // continue; | ||
194 | // if (x >= map.Width || y >= map.Height) | ||
195 | // continue; | ||
196 | // | ||
197 | // map.SetPixel(dx, dy, Color.DarkGray); | ||
198 | // } | ||
199 | // } | ||
200 | // } | ||
201 | // } | ||
202 | // } | ||
203 | // } | ||
204 | // } | ||
205 | // } | ||
206 | |||
207 | private Bitmap DrawObjectVolume(Scene whichScene, Bitmap mapbmp) | ||
208 | { | ||
209 | int tc = 0; | ||
210 | double[,] hm = whichScene.Heightmap.GetDoubles(); | ||
211 | tc = System.Environment.TickCount; | ||
212 | m_log.Info("[MAPTILE]: Generating Maptile Step 2: Object Volume Profile"); | ||
213 | List<EntityBase> objs = whichScene.GetEntities(); | ||
214 | Dictionary<uint, DrawStruct> z_sort = new Dictionary<uint, DrawStruct>(); | ||
215 | //SortedList<float, RectangleDrawStruct> z_sort = new SortedList<float, RectangleDrawStruct>(); | ||
216 | List<float> z_sortheights = new List<float>(); | ||
217 | List<uint> z_localIDs = new List<uint>(); | ||
218 | |||
219 | lock (objs) | ||
220 | { | ||
221 | foreach (EntityBase obj in objs) | ||
222 | { | ||
223 | // Only draw the contents of SceneObjectGroup | ||
224 | if (obj is SceneObjectGroup) | ||
225 | { | ||
226 | SceneObjectGroup mapdot = (SceneObjectGroup)obj; | ||
227 | Color mapdotspot = Color.Gray; // Default color when prim color is white | ||
228 | // Loop over prim in group | ||
229 | foreach (SceneObjectPart part in mapdot.Children.Values) | ||
230 | { | ||
231 | if (part == null) | ||
232 | continue; | ||
233 | |||
234 | // Draw if the object is at least 1 meter wide in any direction | ||
235 | if (part.Scale.X > 1f || part.Scale.Y > 1f || part.Scale.Z > 1f) | ||
236 | { | ||
237 | // Try to get the RGBA of the default texture entry.. | ||
238 | // | ||
239 | try | ||
240 | { | ||
241 | // get the null checks out of the way | ||
242 | // skip the ones that break | ||
243 | if (part == null) | ||
244 | continue; | ||
245 | |||
246 | if (part.Shape == null) | ||
247 | continue; | ||
248 | |||
249 | if (part.Shape.PCode == (byte)PCode.Tree || part.Shape.PCode == (byte)PCode.NewTree || part.Shape.PCode == (byte)PCode.Grass) | ||
250 | continue; // eliminates trees from this since we don't really have a good tree representation | ||
251 | // if you want tree blocks on the map comment the above line and uncomment the below line | ||
252 | //mapdotspot = Color.PaleGreen; | ||
253 | |||
254 | if (part.Shape.Textures == null) | ||
255 | continue; | ||
256 | |||
257 | if (part.Shape.Textures.DefaultTexture == null) | ||
258 | continue; | ||
259 | |||
260 | Color4 texcolor = part.Shape.Textures.DefaultTexture.RGBA; | ||
261 | |||
262 | // Not sure why some of these are null, oh well. | ||
263 | |||
264 | int colorr = 255 - (int)(texcolor.R * 255f); | ||
265 | int colorg = 255 - (int)(texcolor.G * 255f); | ||
266 | int colorb = 255 - (int)(texcolor.B * 255f); | ||
267 | |||
268 | if (!(colorr == 255 && colorg == 255 && colorb == 255)) | ||
269 | { | ||
270 | //Try to set the map spot color | ||
271 | try | ||
272 | { | ||
273 | // If the color gets goofy somehow, skip it *shakes fist at Color4 | ||
274 | mapdotspot = Color.FromArgb(colorr, colorg, colorb); | ||
275 | } | ||
276 | catch (ArgumentException) | ||
277 | { | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | catch (IndexOutOfRangeException) | ||
282 | { | ||
283 | // Windows Array | ||
284 | } | ||
285 | catch (ArgumentOutOfRangeException) | ||
286 | { | ||
287 | // Mono Array | ||
288 | } | ||
289 | |||
290 | Vector3 pos = part.GetWorldPosition(); | ||
291 | |||
292 | // skip prim outside of retion | ||
293 | if (pos.X < 0f || pos.X > 256f || pos.Y < 0f || pos.Y > 256f) | ||
294 | continue; | ||
295 | |||
296 | // skip prim in non-finite position | ||
297 | if (Single.IsNaN(pos.X) || Single.IsNaN(pos.Y) || | ||
298 | Single.IsInfinity(pos.X) || Single.IsInfinity(pos.Y)) | ||
299 | continue; | ||
300 | |||
301 | // Figure out if object is under 256m above the height of the terrain | ||
302 | bool isBelow256AboveTerrain = false; | ||
303 | |||
304 | try | ||
305 | { | ||
306 | isBelow256AboveTerrain = (pos.Z < ((float)hm[(int)pos.X, (int)pos.Y] + 256f)); | ||
307 | } | ||
308 | catch (Exception) | ||
309 | { | ||
310 | } | ||
311 | |||
312 | if (isBelow256AboveTerrain) | ||
313 | { | ||
314 | // Translate scale by rotation so scale is represented properly when object is rotated | ||
315 | Vector3 lscale = new Vector3(part.Shape.Scale.X, part.Shape.Scale.Y, part.Shape.Scale.Z); | ||
316 | Vector3 scale = new Vector3(); | ||
317 | Vector3 tScale = new Vector3(); | ||
318 | Vector3 axPos = new Vector3(pos.X,pos.Y,pos.Z); | ||
319 | |||
320 | Quaternion llrot = part.GetWorldRotation(); | ||
321 | Quaternion rot = new Quaternion(llrot.W, llrot.X, llrot.Y, llrot.Z); | ||
322 | scale = lscale * rot; | ||
323 | |||
324 | // negative scales don't work in this situation | ||
325 | scale.X = Math.Abs(scale.X); | ||
326 | scale.Y = Math.Abs(scale.Y); | ||
327 | scale.Z = Math.Abs(scale.Z); | ||
328 | |||
329 | // This scaling isn't very accurate and doesn't take into account the face rotation :P | ||
330 | int mapdrawstartX = (int)(pos.X - scale.X); | ||
331 | int mapdrawstartY = (int)(pos.Y - scale.Y); | ||
332 | int mapdrawendX = (int)(pos.X + scale.X); | ||
333 | int mapdrawendY = (int)(pos.Y + scale.Y); | ||
334 | |||
335 | // If object is beyond the edge of the map, don't draw it to avoid errors | ||
336 | if (mapdrawstartX < 0 || mapdrawstartX > 255 || mapdrawendX < 0 || mapdrawendX > 255 | ||
337 | || mapdrawstartY < 0 || mapdrawstartY > 255 || mapdrawendY < 0 | ||
338 | || mapdrawendY > 255) | ||
339 | continue; | ||
340 | |||
341 | #region obb face reconstruction part duex | ||
342 | Vector3[] vertexes = new Vector3[8]; | ||
343 | |||
344 | // float[] distance = new float[6]; | ||
345 | Vector3[] FaceA = new Vector3[6]; // vertex A for Facei | ||
346 | Vector3[] FaceB = new Vector3[6]; // vertex B for Facei | ||
347 | Vector3[] FaceC = new Vector3[6]; // vertex C for Facei | ||
348 | Vector3[] FaceD = new Vector3[6]; // vertex D for Facei | ||
349 | |||
350 | tScale = new Vector3(lscale.X, -lscale.Y, lscale.Z); | ||
351 | scale = ((tScale * rot)); | ||
352 | vertexes[0] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
353 | // vertexes[0].x = pos.X + vertexes[0].x; | ||
354 | //vertexes[0].y = pos.Y + vertexes[0].y; | ||
355 | //vertexes[0].z = pos.Z + vertexes[0].z; | ||
356 | |||
357 | FaceA[0] = vertexes[0]; | ||
358 | FaceB[3] = vertexes[0]; | ||
359 | FaceA[4] = vertexes[0]; | ||
360 | |||
361 | tScale = lscale; | ||
362 | scale = ((tScale * rot)); | ||
363 | vertexes[1] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
364 | |||
365 | // vertexes[1].x = pos.X + vertexes[1].x; | ||
366 | // vertexes[1].y = pos.Y + vertexes[1].y; | ||
367 | //vertexes[1].z = pos.Z + vertexes[1].z; | ||
368 | |||
369 | FaceB[0] = vertexes[1]; | ||
370 | FaceA[1] = vertexes[1]; | ||
371 | FaceC[4] = vertexes[1]; | ||
372 | |||
373 | tScale = new Vector3(lscale.X, -lscale.Y, -lscale.Z); | ||
374 | scale = ((tScale * rot)); | ||
375 | |||
376 | vertexes[2] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
377 | |||
378 | //vertexes[2].x = pos.X + vertexes[2].x; | ||
379 | //vertexes[2].y = pos.Y + vertexes[2].y; | ||
380 | //vertexes[2].z = pos.Z + vertexes[2].z; | ||
381 | |||
382 | FaceC[0] = vertexes[2]; | ||
383 | FaceD[3] = vertexes[2]; | ||
384 | FaceC[5] = vertexes[2]; | ||
385 | |||
386 | tScale = new Vector3(lscale.X, lscale.Y, -lscale.Z); | ||
387 | scale = ((tScale * rot)); | ||
388 | vertexes[3] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
389 | |||
390 | //vertexes[3].x = pos.X + vertexes[3].x; | ||
391 | // vertexes[3].y = pos.Y + vertexes[3].y; | ||
392 | // vertexes[3].z = pos.Z + vertexes[3].z; | ||
393 | |||
394 | FaceD[0] = vertexes[3]; | ||
395 | FaceC[1] = vertexes[3]; | ||
396 | FaceA[5] = vertexes[3]; | ||
397 | |||
398 | tScale = new Vector3(-lscale.X, lscale.Y, lscale.Z); | ||
399 | scale = ((tScale * rot)); | ||
400 | vertexes[4] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
401 | |||
402 | // vertexes[4].x = pos.X + vertexes[4].x; | ||
403 | // vertexes[4].y = pos.Y + vertexes[4].y; | ||
404 | // vertexes[4].z = pos.Z + vertexes[4].z; | ||
405 | |||
406 | FaceB[1] = vertexes[4]; | ||
407 | FaceA[2] = vertexes[4]; | ||
408 | FaceD[4] = vertexes[4]; | ||
409 | |||
410 | tScale = new Vector3(-lscale.X, lscale.Y, -lscale.Z); | ||
411 | scale = ((tScale * rot)); | ||
412 | vertexes[5] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
413 | |||
414 | // vertexes[5].x = pos.X + vertexes[5].x; | ||
415 | // vertexes[5].y = pos.Y + vertexes[5].y; | ||
416 | // vertexes[5].z = pos.Z + vertexes[5].z; | ||
417 | |||
418 | FaceD[1] = vertexes[5]; | ||
419 | FaceC[2] = vertexes[5]; | ||
420 | FaceB[5] = vertexes[5]; | ||
421 | |||
422 | tScale = new Vector3(-lscale.X, -lscale.Y, lscale.Z); | ||
423 | scale = ((tScale * rot)); | ||
424 | vertexes[6] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
425 | |||
426 | // vertexes[6].x = pos.X + vertexes[6].x; | ||
427 | // vertexes[6].y = pos.Y + vertexes[6].y; | ||
428 | // vertexes[6].z = pos.Z + vertexes[6].z; | ||
429 | |||
430 | FaceB[2] = vertexes[6]; | ||
431 | FaceA[3] = vertexes[6]; | ||
432 | FaceB[4] = vertexes[6]; | ||
433 | |||
434 | tScale = new Vector3(-lscale.X, -lscale.Y, -lscale.Z); | ||
435 | scale = ((tScale * rot)); | ||
436 | vertexes[7] = (new Vector3((pos.X + scale.X), (pos.Y + scale.Y), (pos.Z + scale.Z))); | ||
437 | |||
438 | // vertexes[7].x = pos.X + vertexes[7].x; | ||
439 | // vertexes[7].y = pos.Y + vertexes[7].y; | ||
440 | // vertexes[7].z = pos.Z + vertexes[7].z; | ||
441 | |||
442 | FaceD[2] = vertexes[7]; | ||
443 | FaceC[3] = vertexes[7]; | ||
444 | FaceD[5] = vertexes[7]; | ||
445 | #endregion | ||
446 | |||
447 | //int wy = 0; | ||
448 | |||
449 | //bool breakYN = false; // If we run into an error drawing, break out of the | ||
450 | // loop so we don't lag to death on error handling | ||
451 | DrawStruct ds = new DrawStruct(); | ||
452 | ds.brush = new SolidBrush(mapdotspot); | ||
453 | //ds.rect = new Rectangle(mapdrawstartX, (255 - mapdrawstartY), mapdrawendX - mapdrawstartX, mapdrawendY - mapdrawstartY); | ||
454 | |||
455 | ds.trns = new face[FaceA.Length]; | ||
456 | |||
457 | for (int i = 0; i < FaceA.Length; i++) | ||
458 | { | ||
459 | Point[] working = new Point[5]; | ||
460 | working[0] = project(FaceA[i], axPos); | ||
461 | working[1] = project(FaceB[i], axPos); | ||
462 | working[2] = project(FaceD[i], axPos); | ||
463 | working[3] = project(FaceC[i], axPos); | ||
464 | working[4] = project(FaceA[i], axPos); | ||
465 | |||
466 | face workingface = new face(); | ||
467 | workingface.pts = working; | ||
468 | |||
469 | ds.trns[i] = workingface; | ||
470 | } | ||
471 | |||
472 | z_sort.Add(part.LocalId, ds); | ||
473 | z_localIDs.Add(part.LocalId); | ||
474 | z_sortheights.Add(pos.Z); | ||
475 | |||
476 | //for (int wx = mapdrawstartX; wx < mapdrawendX; wx++) | ||
477 | //{ | ||
478 | //for (wy = mapdrawstartY; wy < mapdrawendY; wy++) | ||
479 | //{ | ||
480 | //m_log.InfoFormat("[MAPDEBUG]: {0},{1}({2})", wx, (255 - wy),wy); | ||
481 | //try | ||
482 | //{ | ||
483 | // Remember, flip the y! | ||
484 | // mapbmp.SetPixel(wx, (255 - wy), mapdotspot); | ||
485 | //} | ||
486 | //catch (ArgumentException) | ||
487 | //{ | ||
488 | // breakYN = true; | ||
489 | //} | ||
490 | |||
491 | //if (breakYN) | ||
492 | // break; | ||
493 | //} | ||
494 | |||
495 | //if (breakYN) | ||
496 | // break; | ||
497 | //} | ||
498 | } // Object is within 256m Z of terrain | ||
499 | } // object is at least a meter wide | ||
500 | } // loop over group children | ||
501 | } // entitybase is sceneobject group | ||
502 | } // foreach loop over entities | ||
503 | |||
504 | float[] sortedZHeights = z_sortheights.ToArray(); | ||
505 | uint[] sortedlocalIds = z_localIDs.ToArray(); | ||
506 | |||
507 | // Sort prim by Z position | ||
508 | Array.Sort(sortedZHeights, sortedlocalIds); | ||
509 | |||
510 | Graphics g = Graphics.FromImage(mapbmp); | ||
511 | |||
512 | for (int s = 0; s < sortedZHeights.Length; s++) | ||
513 | { | ||
514 | if (z_sort.ContainsKey(sortedlocalIds[s])) | ||
515 | { | ||
516 | DrawStruct rectDrawStruct = z_sort[sortedlocalIds[s]]; | ||
517 | for (int r = 0; r < rectDrawStruct.trns.Length; r++ ) | ||
518 | { | ||
519 | g.FillPolygon(rectDrawStruct.brush,rectDrawStruct.trns[r].pts); | ||
520 | } | ||
521 | //g.FillRectangle(rectDrawStruct.brush , rectDrawStruct.rect); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | g.Dispose(); | ||
526 | } // lock entities objs | ||
527 | |||
528 | m_log.Info("[MAPTILE]: Generating Maptile Step 2: Done in " + (System.Environment.TickCount - tc) + " ms"); | ||
529 | return mapbmp; | ||
530 | } | ||
531 | |||
532 | private Point project(Vector3 point3d, Vector3 originpos) | ||
533 | { | ||
534 | Point returnpt = new Point(); | ||
535 | //originpos = point3d; | ||
536 | //int d = (int)(256f / 1.5f); | ||
537 | |||
538 | //Vector3 topos = new Vector3(0, 0, 0); | ||
539 | // float z = -point3d.z - topos.z; | ||
540 | |||
541 | returnpt.X = (int)point3d.X;//(int)((topos.x - point3d.x) / z * d); | ||
542 | returnpt.Y = (int)(255 - point3d.Y);//(int)(255 - (((topos.y - point3d.y) / z * d))); | ||
543 | |||
544 | return returnpt; | ||
545 | } | ||
546 | |||
547 | // TODO: unused: | ||
548 | // #region Deprecated Maptile Generation. Adam may update this | ||
549 | // private Bitmap TerrainToBitmap(string gradientmap) | ||
550 | // { | ||
551 | // Bitmap gradientmapLd = new Bitmap(gradientmap); | ||
552 | // | ||
553 | // int pallete = gradientmapLd.Height; | ||
554 | // | ||
555 | // Bitmap bmp = new Bitmap(m_scene.Heightmap.Width, m_scene.Heightmap.Height); | ||
556 | // Color[] colours = new Color[pallete]; | ||
557 | // | ||
558 | // for (int i = 0; i < pallete; i++) | ||
559 | // { | ||
560 | // colours[i] = gradientmapLd.GetPixel(0, i); | ||
561 | // } | ||
562 | // | ||
563 | // lock (m_scene.Heightmap) | ||
564 | // { | ||
565 | // ITerrainChannel copy = m_scene.Heightmap; | ||
566 | // for (int y = 0; y < copy.Height; y++) | ||
567 | // { | ||
568 | // for (int x = 0; x < copy.Width; x++) | ||
569 | // { | ||
570 | // // 512 is the largest possible height before colours clamp | ||
571 | // int colorindex = (int) (Math.Max(Math.Min(1.0, copy[x, y] / 512.0), 0.0) * (pallete - 1)); | ||
572 | // | ||
573 | // // Handle error conditions | ||
574 | // if (colorindex > pallete - 1 || colorindex < 0) | ||
575 | // bmp.SetPixel(x, copy.Height - y - 1, Color.Red); | ||
576 | // else | ||
577 | // bmp.SetPixel(x, copy.Height - y - 1, colours[colorindex]); | ||
578 | // } | ||
579 | // } | ||
580 | // ShadeBuildings(bmp); | ||
581 | // return bmp; | ||
582 | // } | ||
583 | // } | ||
584 | // #endregion | ||
585 | } | ||
586 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs new file mode 100644 index 0000000..45a99a9 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | using System; | ||
28 | using System.Reflection; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Net; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenSim.Region.Framework.Interfaces; | ||
33 | using OpenSim.Region.Framework.Scenes; | ||
34 | using OpenSim.Region.Framework.Scenes.Hypergrid; | ||
35 | using OpenMetaverse; | ||
36 | using log4net; | ||
37 | using Nini.Config; | ||
38 | |||
39 | namespace OpenSim.Region.CoreModules.World.WorldMap | ||
40 | { | ||
41 | public class MapSearchModule : IRegionModule | ||
42 | { | ||
43 | private static readonly ILog m_log = | ||
44 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
45 | |||
46 | Scene m_scene = null; // only need one for communication with GridService | ||
47 | List<Scene> m_scenes = new List<Scene>(); | ||
48 | |||
49 | #region IRegionModule Members | ||
50 | public void Initialise(Scene scene, IConfigSource source) | ||
51 | { | ||
52 | if (m_scene == null) | ||
53 | { | ||
54 | m_scene = scene; | ||
55 | } | ||
56 | |||
57 | m_scenes.Add(scene); | ||
58 | scene.EventManager.OnNewClient += OnNewClient; | ||
59 | } | ||
60 | |||
61 | public void PostInitialise() | ||
62 | { | ||
63 | } | ||
64 | |||
65 | public void Close() | ||
66 | { | ||
67 | m_scene = null; | ||
68 | m_scenes.Clear(); | ||
69 | } | ||
70 | |||
71 | public string Name | ||
72 | { | ||
73 | get { return "MapSearchModule"; } | ||
74 | } | ||
75 | |||
76 | public bool IsSharedModule | ||
77 | { | ||
78 | get { return true; } | ||
79 | } | ||
80 | |||
81 | #endregion | ||
82 | |||
83 | private void OnNewClient(IClientAPI client) | ||
84 | { | ||
85 | client.OnMapNameRequest += OnMapNameRequest; | ||
86 | } | ||
87 | |||
88 | private void OnMapNameRequest(IClientAPI remoteClient, string mapName) | ||
89 | { | ||
90 | if (mapName.Length < 3) | ||
91 | { | ||
92 | remoteClient.SendAlertMessage("Use a search string with at least 3 characters"); | ||
93 | return; | ||
94 | } | ||
95 | |||
96 | // try to fetch from GridServer | ||
97 | List<RegionInfo> regionInfos = m_scene.SceneGridService.RequestNamedRegions(mapName, 20); | ||
98 | if (regionInfos == null) | ||
99 | { | ||
100 | m_log.Warn("[MAPSEARCHMODULE]: RequestNamedRegions returned null. Old gridserver?"); | ||
101 | // service wasn't available; maybe still an old GridServer. Try the old API, though it will return only one region | ||
102 | regionInfos = new List<RegionInfo>(); | ||
103 | RegionInfo info = m_scene.SceneGridService.RequestClosestRegion(mapName); | ||
104 | if (info != null) regionInfos.Add(info); | ||
105 | } | ||
106 | |||
107 | if ((regionInfos.Count == 0) && IsHypergridOn()) | ||
108 | { | ||
109 | // OK, we tried but there are no regions matching that name. | ||
110 | // Let's check quickly if this is a domain name, and if so link to it | ||
111 | if (mapName.Contains(".") && mapName.Contains(":")) | ||
112 | { | ||
113 | // It probably is a domain name. Try to link to it. | ||
114 | RegionInfo regInfo; | ||
115 | Scene cScene = GetClientScene(remoteClient); | ||
116 | regInfo = HGHyperlink.TryLinkRegion(cScene, remoteClient, mapName); | ||
117 | if (regInfo != null) | ||
118 | regionInfos.Add(regInfo); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | List<MapBlockData> blocks = new List<MapBlockData>(); | ||
123 | |||
124 | MapBlockData data; | ||
125 | if (regionInfos.Count > 0) | ||
126 | { | ||
127 | foreach (RegionInfo info in regionInfos) | ||
128 | { | ||
129 | data = new MapBlockData(); | ||
130 | data.Agents = 0; | ||
131 | data.Access = 21; // TODO what's this? | ||
132 | data.MapImageId = info.RegionSettings.TerrainImageID; | ||
133 | data.Name = info.RegionName; | ||
134 | data.RegionFlags = 0; // TODO not used? | ||
135 | data.WaterHeight = 0; // not used | ||
136 | data.X = (ushort)info.RegionLocX; | ||
137 | data.Y = (ushort)info.RegionLocY; | ||
138 | blocks.Add(data); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | // final block, closing the search result | ||
143 | data = new MapBlockData(); | ||
144 | data.Agents = 0; | ||
145 | data.Access = 255; | ||
146 | data.MapImageId = UUID.Zero; | ||
147 | data.Name = mapName; | ||
148 | data.RegionFlags = 0; | ||
149 | data.WaterHeight = 0; // not used | ||
150 | data.X = 0; | ||
151 | data.Y = 0; | ||
152 | blocks.Add(data); | ||
153 | |||
154 | remoteClient.SendMapBlock(blocks, 0); | ||
155 | } | ||
156 | |||
157 | private bool IsHypergridOn() | ||
158 | { | ||
159 | return (m_scene.SceneGridService is HGSceneCommunicationService); | ||
160 | } | ||
161 | |||
162 | private Scene GetClientScene(IClientAPI client) | ||
163 | { | ||
164 | foreach (Scene s in m_scenes) | ||
165 | { | ||
166 | if (client.Scene.RegionInfo.RegionHandle == s.RegionInfo.RegionHandle) | ||
167 | return s; | ||
168 | } | ||
169 | return m_scene; | ||
170 | } | ||
171 | } | ||
172 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/ShadedMapTileRenderer.cs b/OpenSim/Region/CoreModules/World/WorldMap/ShadedMapTileRenderer.cs new file mode 100644 index 0000000..b783d7c --- /dev/null +++ b/OpenSim/Region/CoreModules/World/WorldMap/ShadedMapTileRenderer.cs | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Drawing; | ||
32 | using System.Drawing.Drawing2D; | ||
33 | using System.Drawing.Imaging; | ||
34 | using System.Reflection; | ||
35 | using OpenMetaverse; | ||
36 | using OpenMetaverse.Imaging; | ||
37 | using Nini.Config; | ||
38 | using log4net; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.World.WorldMap | ||
43 | { | ||
44 | public class ShadedMapTileRenderer : IMapTileTerrainRenderer | ||
45 | { | ||
46 | private static readonly ILog m_log = | ||
47 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
48 | |||
49 | private Scene m_scene; | ||
50 | //private IConfigSource m_config; // not used currently | ||
51 | |||
52 | public void Initialise(Scene scene, IConfigSource config) | ||
53 | { | ||
54 | m_scene = scene; | ||
55 | // m_config = config; // not used currently | ||
56 | } | ||
57 | |||
58 | public void TerrainToBitmap(Bitmap mapbmp) | ||
59 | { | ||
60 | int tc = System.Environment.TickCount; | ||
61 | m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain"); | ||
62 | |||
63 | double[,] hm = m_scene.Heightmap.GetDoubles(); | ||
64 | bool ShadowDebugContinue = true; | ||
65 | |||
66 | bool terraincorruptedwarningsaid = false; | ||
67 | |||
68 | float low = 255; | ||
69 | float high = 0; | ||
70 | for (int x = 0; x < 256; x++) | ||
71 | { | ||
72 | for (int y = 0; y < 256; y++) | ||
73 | { | ||
74 | float hmval = (float)hm[x, y]; | ||
75 | if (hmval < low) | ||
76 | low = hmval; | ||
77 | if (hmval > high) | ||
78 | high = hmval; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; | ||
83 | |||
84 | for (int x = 0; x < 256; x++) | ||
85 | { | ||
86 | for (int y = 0; y < 256; y++) | ||
87 | { | ||
88 | // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left | ||
89 | int yr = 255 - y; | ||
90 | |||
91 | float heightvalue = (float)hm[x, y]; | ||
92 | |||
93 | if (heightvalue > waterHeight) | ||
94 | { | ||
95 | // scale height value | ||
96 | // No, that doesn't scale it: | ||
97 | // heightvalue = low + mid * (heightvalue - low) / mid; => low + (heightvalue - low) * mid / mid = low + (heightvalue - low) * 1 = low + heightvalue - low = heightvalue | ||
98 | |||
99 | if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) | ||
100 | heightvalue = 0; | ||
101 | else if (heightvalue > 255f) | ||
102 | heightvalue = 255f; | ||
103 | else if (heightvalue < 0f) | ||
104 | heightvalue = 0f; | ||
105 | |||
106 | Color color = Color.FromArgb((int)heightvalue, 100, (int)heightvalue); | ||
107 | |||
108 | mapbmp.SetPixel(x, yr, color); | ||
109 | |||
110 | try | ||
111 | { | ||
112 | //X | ||
113 | // . | ||
114 | // | ||
115 | // Shade the terrain for shadows | ||
116 | if (x < 255 && yr < 255) | ||
117 | { | ||
118 | float hfvalue = (float)hm[x, y]; | ||
119 | float hfvaluecompare = 0f; | ||
120 | |||
121 | if ((x + 1 < 256) && (y + 1 < 256)) | ||
122 | { | ||
123 | hfvaluecompare = (float)hm[x + 1, y + 1]; // light from north-east => look at land height there | ||
124 | } | ||
125 | if (Single.IsInfinity(hfvalue) || Single.IsNaN(hfvalue)) | ||
126 | hfvalue = 0f; | ||
127 | |||
128 | if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare)) | ||
129 | hfvaluecompare = 0f; | ||
130 | |||
131 | float hfdiff = hfvalue - hfvaluecompare; // => positive if NE is lower, negative if here is lower | ||
132 | |||
133 | int hfdiffi = 0; | ||
134 | int hfdiffihighlight = 0; | ||
135 | float highlightfactor = 0.18f; | ||
136 | |||
137 | try | ||
138 | { | ||
139 | // hfdiffi = Math.Abs((int)((hfdiff * 4) + (hfdiff * 0.5))) + 1; | ||
140 | hfdiffi = Math.Abs((int)(hfdiff * 4.5f)) + 1; | ||
141 | if (hfdiff % 1f != 0) | ||
142 | { | ||
143 | // hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1); | ||
144 | hfdiffi = hfdiffi + Math.Abs((int)((hfdiff % 1f) * 5f) - 1); | ||
145 | } | ||
146 | |||
147 | hfdiffihighlight = Math.Abs((int)((hfdiff * highlightfactor) * 4.5f)) + 1; | ||
148 | if (hfdiff % 1f != 0) | ||
149 | { | ||
150 | // hfdiffi = hfdiffi + Math.Abs((int)(((hfdiff % 1) * 0.5f) * 10f) - 1); | ||
151 | hfdiffihighlight = hfdiffihighlight + Math.Abs((int)(((hfdiff * highlightfactor) % 1f) * 5f) - 1); | ||
152 | } | ||
153 | } | ||
154 | catch (System.OverflowException) | ||
155 | { | ||
156 | m_log.Debug("[MAPTILE]: Shadow failed at value: " + hfdiff.ToString()); | ||
157 | ShadowDebugContinue = false; | ||
158 | } | ||
159 | |||
160 | if (hfdiff > 0.3f) | ||
161 | { | ||
162 | // NE is lower than here | ||
163 | // We have to desaturate and lighten the land at the same time | ||
164 | // we use floats, colors use bytes, so shrink are space down to | ||
165 | // 0-255 | ||
166 | |||
167 | if (ShadowDebugContinue) | ||
168 | { | ||
169 | int r = color.R; | ||
170 | int g = color.G; | ||
171 | int b = color.B; | ||
172 | color = Color.FromArgb((r + hfdiffihighlight < 255) ? r + hfdiffihighlight : 255, | ||
173 | (g + hfdiffihighlight < 255) ? g + hfdiffihighlight : 255, | ||
174 | (b + hfdiffihighlight < 255) ? b + hfdiffihighlight : 255); | ||
175 | } | ||
176 | } | ||
177 | else if (hfdiff < -0.3f) | ||
178 | { | ||
179 | // here is lower than NE: | ||
180 | // We have to desaturate and blacken the land at the same time | ||
181 | // we use floats, colors use bytes, so shrink are space down to | ||
182 | // 0-255 | ||
183 | |||
184 | if (ShadowDebugContinue) | ||
185 | { | ||
186 | if ((x - 1 > 0) && (yr + 1 < 256)) | ||
187 | { | ||
188 | color = mapbmp.GetPixel(x - 1, yr + 1); | ||
189 | int r = color.R; | ||
190 | int g = color.G; | ||
191 | int b = color.B; | ||
192 | color = Color.FromArgb((r - hfdiffi > 0) ? r - hfdiffi : 0, | ||
193 | (g - hfdiffi > 0) ? g - hfdiffi : 0, | ||
194 | (b - hfdiffi > 0) ? b - hfdiffi : 0); | ||
195 | |||
196 | mapbmp.SetPixel(x-1, yr+1, color); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | catch (System.ArgumentException) | ||
203 | { | ||
204 | if (!terraincorruptedwarningsaid) | ||
205 | { | ||
206 | m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", m_scene.RegionInfo.RegionName); | ||
207 | terraincorruptedwarningsaid = true; | ||
208 | } | ||
209 | color = Color.Black; | ||
210 | mapbmp.SetPixel(x, yr, color); | ||
211 | } | ||
212 | } | ||
213 | else | ||
214 | { | ||
215 | // We're under the water level with the terrain, so paint water instead of land | ||
216 | |||
217 | // Y flip the cordinates | ||
218 | heightvalue = waterHeight - heightvalue; | ||
219 | if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) | ||
220 | heightvalue = 0f; | ||
221 | else if (heightvalue > 19f) | ||
222 | heightvalue = 19f; | ||
223 | else if (heightvalue < 0f) | ||
224 | heightvalue = 0f; | ||
225 | |||
226 | heightvalue = 100f - (heightvalue * 100f) / 19f; | ||
227 | |||
228 | try | ||
229 | { | ||
230 | Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255); | ||
231 | mapbmp.SetPixel(x, yr, water); | ||
232 | } | ||
233 | catch (System.ArgumentException) | ||
234 | { | ||
235 | if (!terraincorruptedwarningsaid) | ||
236 | { | ||
237 | m_log.WarnFormat("[MAPIMAGE]: Your terrain is corrupted in region {0}, it might take a few minutes to generate the map image depending on the corruption level", m_scene.RegionInfo.RegionName); | ||
238 | terraincorruptedwarningsaid = true; | ||
239 | } | ||
240 | Color black = Color.Black; | ||
241 | mapbmp.SetPixel(x, (256 - y) - 1, black); | ||
242 | } | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms"); | ||
247 | } | ||
248 | } | ||
249 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/TexturedMapTileRenderer.cs b/OpenSim/Region/CoreModules/World/WorldMap/TexturedMapTileRenderer.cs new file mode 100644 index 0000000..537644a --- /dev/null +++ b/OpenSim/Region/CoreModules/World/WorldMap/TexturedMapTileRenderer.cs | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Drawing; | ||
32 | using System.Drawing.Drawing2D; | ||
33 | using System.Drawing.Imaging; | ||
34 | using System.Reflection; | ||
35 | using OpenMetaverse; | ||
36 | using Nini.Config; | ||
37 | using log4net; | ||
38 | using OpenMetaverse.Imaging; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.Framework.Interfaces; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | using OpenSim.Region.CoreModules.World.Terrain; | ||
43 | |||
44 | namespace OpenSim.Region.CoreModules.World.WorldMap | ||
45 | { | ||
46 | // Hue, Saturation, Value; used for color-interpolation | ||
47 | struct HSV { | ||
48 | public float h; | ||
49 | public float s; | ||
50 | public float v; | ||
51 | |||
52 | public HSV(float h, float s, float v) | ||
53 | { | ||
54 | this.h = h; | ||
55 | this.s = s; | ||
56 | this.v = v; | ||
57 | } | ||
58 | |||
59 | // (for info about algorithm, see http://en.wikipedia.org/wiki/HSL_and_HSV) | ||
60 | public HSV(Color c) | ||
61 | { | ||
62 | float r = c.R / 255f; | ||
63 | float g = c.G / 255f; | ||
64 | float b = c.B / 255f; | ||
65 | float max = Math.Max(Math.Max(r, g), b); | ||
66 | float min = Math.Min(Math.Min(r, g), b); | ||
67 | float diff = max - min; | ||
68 | |||
69 | if (max == min) h = 0f; | ||
70 | else if (max == r) h = (g - b) / diff * 60f; | ||
71 | else if (max == g) h = (b - r) / diff * 60f + 120f; | ||
72 | else h = (r - g) / diff * 60f + 240f; | ||
73 | if (h < 0f) h += 360f; | ||
74 | |||
75 | if (max == 0f) s = 0f; | ||
76 | else s = diff / max; | ||
77 | |||
78 | v = max; | ||
79 | } | ||
80 | |||
81 | // (for info about algorithm, see http://en.wikipedia.org/wiki/HSL_and_HSV) | ||
82 | public Color toColor() | ||
83 | { | ||
84 | if (s < 0f) Console.WriteLine("S < 0: " + s); | ||
85 | else if (s > 1f) Console.WriteLine("S > 1: " + s); | ||
86 | if (v < 0f) Console.WriteLine("V < 0: " + v); | ||
87 | else if (v > 1f) Console.WriteLine("V > 1: " + v); | ||
88 | |||
89 | float f = h / 60f; | ||
90 | int sector = (int)f % 6; | ||
91 | f = f - (int)f; | ||
92 | int pi = (int)(v * (1f - s) * 255f); | ||
93 | int qi = (int)(v * (1f - s * f) * 255f); | ||
94 | int ti = (int)(v * (1f - (1f - f) * s) * 255f); | ||
95 | int vi = (int)(v * 255f); | ||
96 | |||
97 | switch (sector) | ||
98 | { | ||
99 | case 0: | ||
100 | return Color.FromArgb(vi, ti, pi); | ||
101 | case 1: | ||
102 | return Color.FromArgb(qi, vi, pi); | ||
103 | case 2: | ||
104 | return Color.FromArgb(pi, vi, ti); | ||
105 | case 3: | ||
106 | return Color.FromArgb(pi, qi, vi); | ||
107 | case 4: | ||
108 | return Color.FromArgb(ti, pi, vi); | ||
109 | default: | ||
110 | return Color.FromArgb(vi, pi, qi); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | public class TexturedMapTileRenderer : IMapTileTerrainRenderer | ||
116 | { | ||
117 | #region Constants | ||
118 | |||
119 | private static readonly ILog m_log = | ||
120 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
121 | |||
122 | // some hardcoded terrain UUIDs that work with SL 1.20 (the four default textures and "Blank"). | ||
123 | // The color-values were choosen because they "look right" (at least to me) ;-) | ||
124 | private static readonly UUID defaultTerrainTexture1 = new UUID("0bc58228-74a0-7e83-89bc-5c23464bcec5"); | ||
125 | private static readonly Color defaultColor1 = Color.FromArgb(165, 137, 118); | ||
126 | private static readonly UUID defaultTerrainTexture2 = new UUID("63338ede-0037-c4fd-855b-015d77112fc8"); | ||
127 | private static readonly Color defaultColor2 = Color.FromArgb(69, 89, 49); | ||
128 | private static readonly UUID defaultTerrainTexture3 = new UUID("303cd381-8560-7579-23f1-f0a880799740"); | ||
129 | private static readonly Color defaultColor3 = Color.FromArgb(162, 154, 141); | ||
130 | private static readonly UUID defaultTerrainTexture4 = new UUID("53a2f406-4895-1d13-d541-d2e3b86bc19c"); | ||
131 | private static readonly Color defaultColor4 = Color.FromArgb(200, 200, 200); | ||
132 | |||
133 | #endregion | ||
134 | |||
135 | |||
136 | private Scene m_scene; | ||
137 | // private IConfigSource m_config; // not used currently | ||
138 | |||
139 | // mapping from texture UUIDs to averaged color. This will contain 5-9 values, in general; new values are only | ||
140 | // added when the terrain textures are changed in the estate dialog and a new map is generated (and will stay in | ||
141 | // that map until the region-server restarts. This could be considered a memory-leak, but it's a *very* small one. | ||
142 | // TODO does it make sense to use a "real" cache and regenerate missing entries on fetch? | ||
143 | private Dictionary<UUID, Color> m_mapping; | ||
144 | |||
145 | |||
146 | public void Initialise(Scene scene, IConfigSource source) | ||
147 | { | ||
148 | m_scene = scene; | ||
149 | // m_config = source; // not used currently | ||
150 | m_mapping = new Dictionary<UUID,Color>(); | ||
151 | m_mapping.Add(defaultTerrainTexture1, defaultColor1); | ||
152 | m_mapping.Add(defaultTerrainTexture2, defaultColor2); | ||
153 | m_mapping.Add(defaultTerrainTexture3, defaultColor3); | ||
154 | m_mapping.Add(defaultTerrainTexture4, defaultColor4); | ||
155 | m_mapping.Add(Util.BLANK_TEXTURE_UUID, Color.White); | ||
156 | } | ||
157 | |||
158 | #region Helpers | ||
159 | // This fetches the texture from the asset server synchroneously. That should be ok, as we | ||
160 | // call map-creation only in those places: | ||
161 | // - on start: We can wait here until the asset server returns the texture | ||
162 | // TODO (- on "map" command: We are in the command-line thread, we will wait for completion anyway) | ||
163 | // TODO (- on "automatic" update after some change: We are called from the mapUpdateTimer here and | ||
164 | // will wait anyway) | ||
165 | private Bitmap fetchTexture(UUID id) | ||
166 | { | ||
167 | AssetBase asset = m_scene.AssetCache.GetAsset(id, true); | ||
168 | m_log.DebugFormat("Fetched texture {0}, found: {1}", id, asset != null); | ||
169 | if (asset == null) return null; | ||
170 | |||
171 | ManagedImage managedImage; | ||
172 | Image image; | ||
173 | |||
174 | try | ||
175 | { | ||
176 | if (OpenJPEG.DecodeToImage(asset.Data, out managedImage, out image)) | ||
177 | return new Bitmap(image); | ||
178 | else | ||
179 | return null; | ||
180 | } | ||
181 | catch (DllNotFoundException) | ||
182 | { | ||
183 | m_log.ErrorFormat("[TexturedMapTileRenderer]: OpenJpeg is not installed correctly on this system. Asset Data is emtpy for {0}", id); | ||
184 | |||
185 | } | ||
186 | catch (IndexOutOfRangeException) | ||
187 | { | ||
188 | m_log.ErrorFormat("[TexturedMapTileRenderer]: OpenJpeg was unable to encode this. Asset Data is emtpy for {0}", id); | ||
189 | |||
190 | } | ||
191 | catch (Exception) | ||
192 | { | ||
193 | m_log.ErrorFormat("[TexturedMapTileRenderer]: OpenJpeg was unable to encode this. Asset Data is emtpy for {0}", id); | ||
194 | |||
195 | } | ||
196 | return null; | ||
197 | |||
198 | } | ||
199 | |||
200 | // Compute the average color of a texture. | ||
201 | private Color computeAverageColor(Bitmap bmp) | ||
202 | { | ||
203 | // we have 256 x 256 pixel, each with 256 possible color-values per | ||
204 | // color-channel, so 2^24 is the maximum value we can get, adding everything. | ||
205 | // int is be big enough for that. | ||
206 | int r = 0, g = 0, b = 0; | ||
207 | for (int y = 0; y < bmp.Height; ++y) | ||
208 | { | ||
209 | for (int x = 0; x < bmp.Width; ++x) | ||
210 | { | ||
211 | Color c = bmp.GetPixel(x, y); | ||
212 | r += (int)c.R & 0xff; | ||
213 | g += (int)c.G & 0xff; | ||
214 | b += (int)c.B & 0xff; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | int pixels = bmp.Width * bmp.Height; | ||
219 | return Color.FromArgb(r / pixels, g / pixels, b / pixels); | ||
220 | } | ||
221 | |||
222 | // return either the average color of the texture, or the defaultColor if the texturID is invalid | ||
223 | // or the texture couldn't be found | ||
224 | private Color computeAverageColor(UUID textureID, Color defaultColor) { | ||
225 | if (textureID == UUID.Zero) return defaultColor; // not set | ||
226 | if (m_mapping.ContainsKey(textureID)) return m_mapping[textureID]; // one of the predefined textures | ||
227 | |||
228 | Bitmap bmp = fetchTexture(textureID); | ||
229 | Color color = bmp == null ? defaultColor : computeAverageColor(bmp); | ||
230 | // store it for future reference | ||
231 | m_mapping[textureID] = color; | ||
232 | |||
233 | return color; | ||
234 | } | ||
235 | |||
236 | // S-curve: f(x) = 3x² - 2x³: | ||
237 | // f(0) = 0, f(0.5) = 0.5, f(1) = 1, | ||
238 | // f'(x) = 0 at x = 0 and x = 1; f'(0.5) = 1.5, | ||
239 | // f''(0.5) = 0, f''(x) != 0 for x != 0.5 | ||
240 | private float S(float v) { | ||
241 | return (v * v * (3f - 2f * v)); | ||
242 | } | ||
243 | |||
244 | // interpolate two colors in HSV space and return the resulting color | ||
245 | private HSV interpolateHSV(ref HSV c1, ref HSV c2, float ratio) { | ||
246 | if (ratio <= 0f) return c1; | ||
247 | if (ratio >= 1f) return c2; | ||
248 | |||
249 | // make sure we are on the same side on the hue-circle for interpolation | ||
250 | // We change the hue of the parameters here, but we don't change the color | ||
251 | // represented by that value | ||
252 | if (c1.h - c2.h > 180f) c1.h -= 360f; | ||
253 | else if (c2.h - c1.h > 180f) c1.h += 360f; | ||
254 | |||
255 | return new HSV(c1.h * (1f - ratio) + c2.h * ratio, | ||
256 | c1.s * (1f - ratio) + c2.s * ratio, | ||
257 | c1.v * (1f - ratio) + c2.v * ratio); | ||
258 | } | ||
259 | |||
260 | // the heigthfield might have some jumps in values. Rendered land is smooth, though, | ||
261 | // as a slope is rendered at that place. So average 4 neighbour values to emulate that. | ||
262 | private float getHeight(double[,] hm, int x, int y) { | ||
263 | if (x < 255 && y < 255) | ||
264 | return (float)(hm[x, y] * .444 + (hm[x + 1, y] + hm[x, y + 1]) * .222 + hm[x + 1, y +1] * .112); | ||
265 | else | ||
266 | return (float)hm[x, y]; | ||
267 | } | ||
268 | #endregion | ||
269 | |||
270 | public void TerrainToBitmap(Bitmap mapbmp) | ||
271 | { | ||
272 | int tc = System.Environment.TickCount; | ||
273 | m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain"); | ||
274 | |||
275 | // These textures should be in the AssetCache anyway, as every client conneting to this | ||
276 | // region needs them. Except on start, when the map is recreated (before anyone connected), | ||
277 | // and on change of the estate settings (textures and terrain values), when the map should | ||
278 | // be recreated. | ||
279 | RegionSettings settings = m_scene.RegionInfo.RegionSettings; | ||
280 | |||
281 | // the four terrain colors as HSVs for interpolation | ||
282 | HSV hsv1 = new HSV(computeAverageColor(settings.TerrainTexture1, defaultColor1)); | ||
283 | HSV hsv2 = new HSV(computeAverageColor(settings.TerrainTexture2, defaultColor2)); | ||
284 | HSV hsv3 = new HSV(computeAverageColor(settings.TerrainTexture3, defaultColor3)); | ||
285 | HSV hsv4 = new HSV(computeAverageColor(settings.TerrainTexture4, defaultColor4)); | ||
286 | |||
287 | float levelNElow = (float)settings.Elevation1NE; | ||
288 | float levelNEhigh = (float)settings.Elevation2NE; | ||
289 | |||
290 | float levelNWlow = (float)settings.Elevation1NW; | ||
291 | float levelNWhigh = (float)settings.Elevation2NW; | ||
292 | |||
293 | float levelSElow = (float)settings.Elevation1SE; | ||
294 | float levelSEhigh = (float)settings.Elevation2SE; | ||
295 | |||
296 | float levelSWlow = (float)settings.Elevation1SW; | ||
297 | float levelSWhigh = (float)settings.Elevation2SW; | ||
298 | |||
299 | float waterHeight = (float)settings.WaterHeight; | ||
300 | |||
301 | double[,] hm = m_scene.Heightmap.GetDoubles(); | ||
302 | |||
303 | for (int x = 0; x < 256; x++) | ||
304 | { | ||
305 | float columnRatio = x / 255f; // 0 - 1, for interpolation | ||
306 | for (int y = 0; y < 256; y++) | ||
307 | { | ||
308 | float rowRatio = y / 255f; // 0 - 1, for interpolation | ||
309 | |||
310 | // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left | ||
311 | int yr = 255 - y; | ||
312 | |||
313 | float heightvalue = getHeight(hm, x, y); | ||
314 | if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) | ||
315 | heightvalue = 0; | ||
316 | |||
317 | if (heightvalue > waterHeight) | ||
318 | { | ||
319 | // add a bit noise for breaking up those flat colors: | ||
320 | // - a large-scale noise, for the "patches" (using an doubled s-curve for sharper contrast) | ||
321 | // - a small-scale noise, for bringing in some small scale variation | ||
322 | //float bigNoise = (float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f; // map to 0.0 - 1.0 | ||
323 | //float smallNoise = (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * .5f + .5f; | ||
324 | //float hmod = heightvalue + smallNoise * 3f + S(S(bigNoise)) * 10f; | ||
325 | float hmod = | ||
326 | heightvalue + | ||
327 | (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * 1.5f + 1.5f + // 0 - 3 | ||
328 | S(S((float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f)) * 10f; // 0 - 10 | ||
329 | |||
330 | // find the low/high values for this point (interpolated bilinearily) | ||
331 | // (and remember, x=0,y=0 is SW) | ||
332 | float low = levelSWlow * (1f - rowRatio) * (1f - columnRatio) + | ||
333 | levelSElow * (1f - rowRatio) * columnRatio + | ||
334 | levelNWlow * rowRatio * (1f - columnRatio) + | ||
335 | levelNElow * rowRatio * columnRatio; | ||
336 | float high = levelSWhigh * (1f - rowRatio) * (1f - columnRatio) + | ||
337 | levelSEhigh * (1f - rowRatio) * columnRatio + | ||
338 | levelNWhigh * rowRatio * (1f - columnRatio) + | ||
339 | levelNEhigh * rowRatio * columnRatio; | ||
340 | if (high < low) | ||
341 | { | ||
342 | // someone tried to fool us. High value should be higher than low every time | ||
343 | float tmp = high; | ||
344 | high = low; | ||
345 | low = tmp; | ||
346 | } | ||
347 | |||
348 | HSV hsv; | ||
349 | if (hmod <= low) hsv = hsv1; // too low | ||
350 | else if (hmod >= high) hsv = hsv4; // too high | ||
351 | else | ||
352 | { | ||
353 | // HSV-interpolate along the colors | ||
354 | // first, rescale h to 0.0 - 1.0 | ||
355 | hmod = (hmod - low) / (high - low); | ||
356 | // now we have to split: 0.00 => color1, 0.33 => color2, 0.67 => color3, 1.00 => color4 | ||
357 | if (hmod < 1f/3f) hsv = interpolateHSV(ref hsv1, ref hsv2, hmod * 3f); | ||
358 | else if (hmod < 2f/3f) hsv = interpolateHSV(ref hsv2, ref hsv3, (hmod * 3f) - 1f); | ||
359 | else hsv = interpolateHSV(ref hsv3, ref hsv4, (hmod * 3f) - 2f); | ||
360 | } | ||
361 | |||
362 | // Shade the terrain for shadows | ||
363 | if (x < 255 && y < 255) | ||
364 | { | ||
365 | float hfvaluecompare = getHeight(hm, x + 1, y + 1); // light from north-east => look at land height there | ||
366 | if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare)) | ||
367 | hfvaluecompare = 0f; | ||
368 | |||
369 | float hfdiff = heightvalue - hfvaluecompare; // => positive if NE is lower, negative if here is lower | ||
370 | hfdiff *= 0.06f; // some random factor so "it looks good" | ||
371 | if (hfdiff > 0.02f) | ||
372 | { | ||
373 | float highlightfactor = 0.18f; | ||
374 | // NE is lower than here | ||
375 | // We have to desaturate and lighten the land at the same time | ||
376 | hsv.s = (hsv.s - (hfdiff * highlightfactor) > 0f) ? hsv.s - (hfdiff * highlightfactor) : 0f; | ||
377 | hsv.v = (hsv.v + (hfdiff * highlightfactor) < 1f) ? hsv.v + (hfdiff * highlightfactor) : 1f; | ||
378 | } | ||
379 | else if (hfdiff < -0.02f) | ||
380 | { | ||
381 | // here is lower than NE: | ||
382 | // We have to desaturate and blacken the land at the same time | ||
383 | hsv.s = (hsv.s + hfdiff > 0f) ? hsv.s + hfdiff : 0f; | ||
384 | hsv.v = (hsv.v + hfdiff > 0f) ? hsv.v + hfdiff : 0f; | ||
385 | } | ||
386 | } | ||
387 | mapbmp.SetPixel(x, yr, hsv.toColor()); | ||
388 | } | ||
389 | else | ||
390 | { | ||
391 | // We're under the water level with the terrain, so paint water instead of land | ||
392 | |||
393 | heightvalue = waterHeight - heightvalue; | ||
394 | if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue)) | ||
395 | heightvalue = 0f; | ||
396 | else if (heightvalue > 19f) | ||
397 | heightvalue = 19f; | ||
398 | else if (heightvalue < 0f) | ||
399 | heightvalue = 0f; | ||
400 | |||
401 | heightvalue = 100f - (heightvalue * 100f) / 19f; // 0 - 19 => 100 - 0 | ||
402 | |||
403 | Color water = Color.FromArgb((int)heightvalue, (int)heightvalue, 255); | ||
404 | mapbmp.SetPixel(x, yr, water); | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (System.Environment.TickCount - tc) + " ms"); | ||
409 | } | ||
410 | } | ||
411 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs new file mode 100644 index 0000000..376e365 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | |||
@@ -0,0 +1,905 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Drawing; | ||
32 | using System.Drawing.Imaging; | ||
33 | using System.IO; | ||
34 | using System.Net; | ||
35 | using System.Reflection; | ||
36 | using System.Threading; | ||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.Imaging; | ||
39 | using OpenMetaverse.StructuredData; | ||
40 | using log4net; | ||
41 | using Nini.Config; | ||
42 | using OpenSim.Framework; | ||
43 | using OpenSim.Framework.Communications.Cache; | ||
44 | using OpenSim.Framework.Communications.Capabilities; | ||
45 | using OpenSim.Framework.Servers; | ||
46 | using OpenSim.Region.Framework.Interfaces; | ||
47 | using OpenSim.Region.Framework.Scenes; | ||
48 | using OpenSim.Region.Framework.Scenes.Types; | ||
49 | using Caps = OpenSim.Framework.Communications.Capabilities.Caps; | ||
50 | |||
51 | using OSD = OpenMetaverse.StructuredData.OSD; | ||
52 | using OSDMap = OpenMetaverse.StructuredData.OSDMap; | ||
53 | using OSDArray = OpenMetaverse.StructuredData.OSDArray; | ||
54 | |||
55 | namespace OpenSim.Region.CoreModules.World.WorldMap | ||
56 | { | ||
57 | public class WorldMapModule : IRegionModule | ||
58 | { | ||
59 | private static readonly ILog m_log = | ||
60 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
61 | |||
62 | private static readonly string m_mapLayerPath = "0001/"; | ||
63 | |||
64 | private OpenSim.Framework.BlockingQueue<MapRequestState> requests = new OpenSim.Framework.BlockingQueue<MapRequestState>(); | ||
65 | |||
66 | //private IConfig m_config; | ||
67 | protected Scene m_scene; | ||
68 | private List<MapBlockData> cachedMapBlocks = new List<MapBlockData>(); | ||
69 | private int cachedTime = 0; | ||
70 | private byte[] myMapImageJPEG; | ||
71 | protected bool m_Enabled = false; | ||
72 | private Dictionary<UUID, MapRequestState> m_openRequests = new Dictionary<UUID, MapRequestState>(); | ||
73 | private Dictionary<string, int> m_blacklistedurls = new Dictionary<string, int>(); | ||
74 | private Dictionary<ulong, int> m_blacklistedregions = new Dictionary<ulong, int>(); | ||
75 | private Dictionary<ulong, string> m_cachedRegionMapItemsAddress = new Dictionary<ulong, string>(); | ||
76 | private List<UUID> m_rootAgents = new List<UUID>(); | ||
77 | private Thread mapItemReqThread; | ||
78 | private volatile bool threadrunning = false; | ||
79 | |||
80 | //private int CacheRegionsDistance = 256; | ||
81 | |||
82 | #region IRegionModule Members | ||
83 | |||
84 | public virtual void Initialise(Scene scene, IConfigSource config) | ||
85 | { | ||
86 | IConfig startupConfig = config.Configs["Startup"]; | ||
87 | if (startupConfig.GetString("WorldMapModule", "WorldMap") == | ||
88 | "WorldMap") | ||
89 | m_Enabled = true; | ||
90 | |||
91 | if (!m_Enabled) | ||
92 | return; | ||
93 | |||
94 | m_scene = scene; | ||
95 | } | ||
96 | |||
97 | public virtual void PostInitialise() | ||
98 | { | ||
99 | if (m_Enabled) | ||
100 | AddHandlers(); | ||
101 | } | ||
102 | |||
103 | public virtual void Close() | ||
104 | { | ||
105 | } | ||
106 | |||
107 | public virtual string Name | ||
108 | { | ||
109 | get { return "WorldMapModule"; } | ||
110 | } | ||
111 | |||
112 | public bool IsSharedModule | ||
113 | { | ||
114 | get { return false; } | ||
115 | } | ||
116 | |||
117 | #endregion | ||
118 | |||
119 | protected virtual void AddHandlers() | ||
120 | { | ||
121 | myMapImageJPEG = new byte[0]; | ||
122 | |||
123 | string regionimage = "regionImage" + m_scene.RegionInfo.RegionID.ToString(); | ||
124 | regionimage = regionimage.Replace("-", ""); | ||
125 | m_log.Info("[WORLD MAP]: JPEG Map location: http://" + m_scene.RegionInfo.ExternalEndPoint.Address.ToString() + ":" + m_scene.RegionInfo.HttpPort.ToString() + "/index.php?method=" + regionimage); | ||
126 | |||
127 | m_scene.CommsManager.HttpServer.AddHTTPHandler(regionimage, OnHTTPGetMapImage); | ||
128 | m_scene.CommsManager.HttpServer.AddLLSDHandler( | ||
129 | "/MAP/MapItems/" + m_scene.RegionInfo.RegionHandle.ToString(), HandleRemoteMapItemRequest); | ||
130 | |||
131 | m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; | ||
132 | m_scene.EventManager.OnNewClient += OnNewClient; | ||
133 | m_scene.EventManager.OnClientClosed += ClientLoggedOut; | ||
134 | m_scene.EventManager.OnMakeChildAgent += MakeChildAgent; | ||
135 | m_scene.EventManager.OnMakeRootAgent += MakeRootAgent; | ||
136 | } | ||
137 | |||
138 | public void OnRegisterCaps(UUID agentID, Caps caps) | ||
139 | { | ||
140 | //m_log.DebugFormat("[WORLD MAP]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps); | ||
141 | string capsBase = "/CAPS/" + caps.CapsObjectPath; | ||
142 | caps.RegisterHandler("MapLayer", | ||
143 | new RestStreamHandler("POST", capsBase + m_mapLayerPath, | ||
144 | delegate(string request, string path, string param, | ||
145 | OSHttpRequest httpRequest, OSHttpResponse httpResponse) | ||
146 | { | ||
147 | return MapLayerRequest(request, path, param, | ||
148 | agentID, caps); | ||
149 | })); | ||
150 | } | ||
151 | |||
152 | /// <summary> | ||
153 | /// Callback for a map layer request | ||
154 | /// </summary> | ||
155 | /// <param name="request"></param> | ||
156 | /// <param name="path"></param> | ||
157 | /// <param name="param"></param> | ||
158 | /// <param name="agentID"></param> | ||
159 | /// <param name="caps"></param> | ||
160 | /// <returns></returns> | ||
161 | public string MapLayerRequest(string request, string path, string param, | ||
162 | UUID agentID, Caps caps) | ||
163 | { | ||
164 | //try | ||
165 | //{ | ||
166 | //m_log.DebugFormat("[MAPLAYER]: request: {0}, path: {1}, param: {2}, agent:{3}", | ||
167 | //request, path, param,agentID.ToString()); | ||
168 | |||
169 | // this is here because CAPS map requests work even beyond the 10,000 limit. | ||
170 | ScenePresence avatarPresence = null; | ||
171 | |||
172 | m_scene.TryGetAvatar(agentID, out avatarPresence); | ||
173 | |||
174 | if (avatarPresence != null) | ||
175 | { | ||
176 | bool lookup = false; | ||
177 | |||
178 | lock (cachedMapBlocks) | ||
179 | { | ||
180 | if (cachedMapBlocks.Count > 0 && ((cachedTime + 1800) > Util.UnixTimeSinceEpoch())) | ||
181 | { | ||
182 | List<MapBlockData> mapBlocks; | ||
183 | |||
184 | mapBlocks = cachedMapBlocks; | ||
185 | avatarPresence.ControllingClient.SendMapBlock(mapBlocks, 0); | ||
186 | } | ||
187 | else | ||
188 | { | ||
189 | lookup = true; | ||
190 | } | ||
191 | } | ||
192 | if (lookup) | ||
193 | { | ||
194 | List<MapBlockData> mapBlocks; | ||
195 | |||
196 | mapBlocks = m_scene.SceneGridService.RequestNeighbourMapBlocks((int)m_scene.RegionInfo.RegionLocX - 8, (int)m_scene.RegionInfo.RegionLocY - 8, (int)m_scene.RegionInfo.RegionLocX + 8, (int)m_scene.RegionInfo.RegionLocY + 8); | ||
197 | avatarPresence.ControllingClient.SendMapBlock(mapBlocks,0); | ||
198 | |||
199 | lock (cachedMapBlocks) | ||
200 | cachedMapBlocks = mapBlocks; | ||
201 | |||
202 | cachedTime = Util.UnixTimeSinceEpoch(); | ||
203 | } | ||
204 | } | ||
205 | LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); | ||
206 | mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); | ||
207 | return mapResponse.ToString(); | ||
208 | } | ||
209 | |||
210 | /// <summary> | ||
211 | /// | ||
212 | /// </summary> | ||
213 | /// <param name="mapReq"></param> | ||
214 | /// <returns></returns> | ||
215 | public LLSDMapLayerResponse GetMapLayer(LLSDMapRequest mapReq) | ||
216 | { | ||
217 | m_log.Debug("[WORLD MAP]: MapLayer Request in region: " + m_scene.RegionInfo.RegionName); | ||
218 | LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); | ||
219 | mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); | ||
220 | return mapResponse; | ||
221 | } | ||
222 | |||
223 | /// <summary> | ||
224 | /// | ||
225 | /// </summary> | ||
226 | /// <returns></returns> | ||
227 | protected static OSDMapLayer GetOSDMapLayerResponse() | ||
228 | { | ||
229 | OSDMapLayer mapLayer = new OSDMapLayer(); | ||
230 | mapLayer.Right = 5000; | ||
231 | mapLayer.Top = 5000; | ||
232 | mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); | ||
233 | |||
234 | return mapLayer; | ||
235 | } | ||
236 | #region EventHandlers | ||
237 | |||
238 | /// <summary> | ||
239 | /// Registered for event | ||
240 | /// </summary> | ||
241 | /// <param name="client"></param> | ||
242 | private void OnNewClient(IClientAPI client) | ||
243 | { | ||
244 | client.OnRequestMapBlocks += RequestMapBlocks; | ||
245 | client.OnMapItemRequest += HandleMapItemRequest; | ||
246 | } | ||
247 | |||
248 | /// <summary> | ||
249 | /// Client logged out, check to see if there are any more root agents in the simulator | ||
250 | /// If not, stop the mapItemRequest Thread | ||
251 | /// Event handler | ||
252 | /// </summary> | ||
253 | /// <param name="AgentId">AgentID that logged out</param> | ||
254 | private void ClientLoggedOut(UUID AgentId) | ||
255 | { | ||
256 | List<ScenePresence> presences = m_scene.GetAvatars(); | ||
257 | int rootcount = 0; | ||
258 | for (int i=0;i<presences.Count;i++) | ||
259 | { | ||
260 | if (presences[i] != null) | ||
261 | { | ||
262 | if (!presences[i].IsChildAgent) | ||
263 | rootcount++; | ||
264 | } | ||
265 | } | ||
266 | if (rootcount <= 1) | ||
267 | StopThread(); | ||
268 | |||
269 | lock (m_rootAgents) | ||
270 | { | ||
271 | if (m_rootAgents.Contains(AgentId)) | ||
272 | { | ||
273 | m_rootAgents.Remove(AgentId); | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | #endregion | ||
278 | |||
279 | /// <summary> | ||
280 | /// Starts the MapItemRequest Thread | ||
281 | /// Note that this only gets started when there are actually agents in the region | ||
282 | /// Additionally, it gets stopped when there are none. | ||
283 | /// </summary> | ||
284 | /// <param name="o"></param> | ||
285 | private void StartThread(object o) | ||
286 | { | ||
287 | if (threadrunning) return; | ||
288 | threadrunning = true; | ||
289 | m_log.Debug("[WORLD MAP]: Starting remote MapItem request thread"); | ||
290 | mapItemReqThread = new Thread(new ThreadStart(process)); | ||
291 | mapItemReqThread.IsBackground = true; | ||
292 | mapItemReqThread.Name = "MapItemRequestThread"; | ||
293 | mapItemReqThread.Priority = ThreadPriority.BelowNormal; | ||
294 | mapItemReqThread.SetApartmentState(ApartmentState.MTA); | ||
295 | mapItemReqThread.Start(); | ||
296 | ThreadTracker.Add(mapItemReqThread); | ||
297 | } | ||
298 | |||
299 | /// <summary> | ||
300 | /// Enqueues a 'stop thread' MapRequestState. Causes the MapItemRequest thread to end | ||
301 | /// </summary> | ||
302 | private void StopThread() | ||
303 | { | ||
304 | MapRequestState st = new MapRequestState(); | ||
305 | st.agentID=UUID.Zero; | ||
306 | st.EstateID=0; | ||
307 | st.flags=0; | ||
308 | st.godlike=false; | ||
309 | st.itemtype=0; | ||
310 | st.regionhandle=0; | ||
311 | |||
312 | requests.Enqueue(st); | ||
313 | } | ||
314 | |||
315 | public virtual void HandleMapItemRequest(IClientAPI remoteClient, uint flags, | ||
316 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) | ||
317 | { | ||
318 | lock (m_rootAgents) | ||
319 | { | ||
320 | if (!m_rootAgents.Contains(remoteClient.AgentId)) | ||
321 | return; | ||
322 | } | ||
323 | uint xstart = 0; | ||
324 | uint ystart = 0; | ||
325 | Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); | ||
326 | if (itemtype == 6) // we only sevice 6 right now (avatar green dots) | ||
327 | { | ||
328 | if (regionhandle == 0 || regionhandle == m_scene.RegionInfo.RegionHandle) | ||
329 | { | ||
330 | // Local Map Item Request | ||
331 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
332 | int tc = System.Environment.TickCount; | ||
333 | List<mapItemReply> mapitems = new List<mapItemReply>(); | ||
334 | mapItemReply mapitem = new mapItemReply(); | ||
335 | if (avatars.Count == 0 || avatars.Count == 1) | ||
336 | { | ||
337 | mapitem = new mapItemReply(); | ||
338 | mapitem.x = (uint)(xstart + 1); | ||
339 | mapitem.y = (uint)(ystart + 1); | ||
340 | mapitem.id = UUID.Zero; | ||
341 | mapitem.name = Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()); | ||
342 | mapitem.Extra = 0; | ||
343 | mapitem.Extra2 = 0; | ||
344 | mapitems.Add(mapitem); | ||
345 | } | ||
346 | else | ||
347 | { | ||
348 | foreach (ScenePresence av in avatars) | ||
349 | { | ||
350 | // Don't send a green dot for yourself | ||
351 | if (av.UUID != remoteClient.AgentId) | ||
352 | { | ||
353 | mapitem = new mapItemReply(); | ||
354 | mapitem.x = (uint)(xstart + av.AbsolutePosition.X); | ||
355 | mapitem.y = (uint)(ystart + av.AbsolutePosition.Y); | ||
356 | mapitem.id = UUID.Zero; | ||
357 | mapitem.name = Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()); | ||
358 | mapitem.Extra = 1; | ||
359 | mapitem.Extra2 = 0; | ||
360 | mapitems.Add(mapitem); | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | ||
365 | } | ||
366 | else | ||
367 | { | ||
368 | // Remote Map Item Request | ||
369 | |||
370 | // ensures that the blockingqueue doesn't get borked if the GetAgents() timing changes. | ||
371 | // Note that we only start up a remote mapItem Request thread if there's users who could | ||
372 | // be making requests | ||
373 | if (!threadrunning) | ||
374 | { | ||
375 | m_log.Warn("[WORLD MAP]: Starting new remote request thread manually. This means that AvatarEnteringParcel never fired! This needs to be fixed! Don't Mantis this, as the developers can see it in this message"); | ||
376 | StartThread(new object()); | ||
377 | } | ||
378 | |||
379 | RequestMapItems("",remoteClient.AgentId,flags,EstateID,godlike,itemtype,regionhandle); | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | /// <summary> | ||
385 | /// Processing thread main() loop for doing remote mapitem requests | ||
386 | /// </summary> | ||
387 | public void process() | ||
388 | { | ||
389 | try | ||
390 | { | ||
391 | while (true) | ||
392 | { | ||
393 | MapRequestState st = requests.Dequeue(); | ||
394 | |||
395 | // end gracefully | ||
396 | if (st.agentID == UUID.Zero) | ||
397 | { | ||
398 | ThreadTracker.Remove(mapItemReqThread); | ||
399 | break; | ||
400 | } | ||
401 | |||
402 | bool dorequest = true; | ||
403 | lock (m_rootAgents) | ||
404 | { | ||
405 | if (!m_rootAgents.Contains(st.agentID)) | ||
406 | dorequest = false; | ||
407 | } | ||
408 | |||
409 | if (dorequest) | ||
410 | { | ||
411 | OSDMap response = RequestMapItemsAsync("", st.agentID, st.flags, st.EstateID, st.godlike, st.itemtype, st.regionhandle); | ||
412 | RequestMapItemsCompleted(response); | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | catch (Exception e) | ||
417 | { | ||
418 | m_log.ErrorFormat("[WORLD MAP]: Map item request thread terminated abnormally with exception {0}", e); | ||
419 | } | ||
420 | |||
421 | threadrunning = false; | ||
422 | } | ||
423 | |||
424 | /// <summary> | ||
425 | /// Enqueues the map item request into the processing thread | ||
426 | /// </summary> | ||
427 | /// <param name="state"></param> | ||
428 | public void EnqueueMapItemRequest(MapRequestState state) | ||
429 | { | ||
430 | requests.Enqueue(state); | ||
431 | } | ||
432 | |||
433 | /// <summary> | ||
434 | /// Sends the mapitem response to the IClientAPI | ||
435 | /// </summary> | ||
436 | /// <param name="response">The OSDMap Response for the mapitem</param> | ||
437 | private void RequestMapItemsCompleted(OSDMap response) | ||
438 | { | ||
439 | UUID requestID = response["requestID"].AsUUID(); | ||
440 | |||
441 | if (requestID != UUID.Zero) | ||
442 | { | ||
443 | MapRequestState mrs = new MapRequestState(); | ||
444 | mrs.agentID = UUID.Zero; | ||
445 | lock (m_openRequests) | ||
446 | { | ||
447 | if (m_openRequests.ContainsKey(requestID)) | ||
448 | { | ||
449 | mrs = m_openRequests[requestID]; | ||
450 | m_openRequests.Remove(requestID); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | if (mrs.agentID != UUID.Zero) | ||
455 | { | ||
456 | ScenePresence av = null; | ||
457 | m_scene.TryGetAvatar(mrs.agentID, out av); | ||
458 | if (av != null) | ||
459 | { | ||
460 | if (response.ContainsKey(mrs.itemtype.ToString())) | ||
461 | { | ||
462 | List<mapItemReply> returnitems = new List<mapItemReply>(); | ||
463 | OSDArray itemarray = (OSDArray)response[mrs.itemtype.ToString()]; | ||
464 | for (int i = 0; i < itemarray.Count; i++) | ||
465 | { | ||
466 | OSDMap mapitem = (OSDMap)itemarray[i]; | ||
467 | mapItemReply mi = new mapItemReply(); | ||
468 | mi.x = (uint)mapitem["X"].AsInteger(); | ||
469 | mi.y = (uint)mapitem["Y"].AsInteger(); | ||
470 | mi.id = mapitem["ID"].AsUUID(); | ||
471 | mi.Extra = mapitem["Extra"].AsInteger(); | ||
472 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | ||
473 | mi.name = mapitem["Name"].AsString(); | ||
474 | returnitems.Add(mi); | ||
475 | } | ||
476 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), mrs.itemtype, mrs.flags); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | } | ||
482 | |||
483 | /// <summary> | ||
484 | /// Enqueue the MapItem request for remote processing | ||
485 | /// </summary> | ||
486 | /// <param name="httpserver">blank string, we discover this in the process</param> | ||
487 | /// <param name="id">Agent ID that we are making this request on behalf</param> | ||
488 | /// <param name="flags">passed in from packet</param> | ||
489 | /// <param name="EstateID">passed in from packet</param> | ||
490 | /// <param name="godlike">passed in from packet</param> | ||
491 | /// <param name="itemtype">passed in from packet</param> | ||
492 | /// <param name="regionhandle">Region we're looking up</param> | ||
493 | public void RequestMapItems(string httpserver, UUID id, uint flags, | ||
494 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) | ||
495 | { | ||
496 | MapRequestState st = new MapRequestState(); | ||
497 | st.agentID = id; | ||
498 | st.flags = flags; | ||
499 | st.EstateID = EstateID; | ||
500 | st.godlike = godlike; | ||
501 | st.itemtype = itemtype; | ||
502 | st.regionhandle = regionhandle; | ||
503 | EnqueueMapItemRequest(st); | ||
504 | } | ||
505 | |||
506 | /// <summary> | ||
507 | /// Does the actual remote mapitem request | ||
508 | /// This should be called from an asynchronous thread | ||
509 | /// Request failures get blacklisted until region restart so we don't | ||
510 | /// continue to spend resources trying to contact regions that are down. | ||
511 | /// </summary> | ||
512 | /// <param name="httpserver">blank string, we discover this in the process</param> | ||
513 | /// <param name="id">Agent ID that we are making this request on behalf</param> | ||
514 | /// <param name="flags">passed in from packet</param> | ||
515 | /// <param name="EstateID">passed in from packet</param> | ||
516 | /// <param name="godlike">passed in from packet</param> | ||
517 | /// <param name="itemtype">passed in from packet</param> | ||
518 | /// <param name="regionhandle">Region we're looking up</param> | ||
519 | /// <returns></returns> | ||
520 | private OSDMap RequestMapItemsAsync(string httpserver, UUID id, uint flags, | ||
521 | uint EstateID, bool godlike, uint itemtype, ulong regionhandle) | ||
522 | { | ||
523 | bool blacklisted = false; | ||
524 | lock (m_blacklistedregions) | ||
525 | { | ||
526 | if (m_blacklistedregions.ContainsKey(regionhandle)) | ||
527 | blacklisted = true; | ||
528 | } | ||
529 | |||
530 | if (blacklisted) | ||
531 | return new OSDMap(); | ||
532 | |||
533 | UUID requestID = UUID.Random(); | ||
534 | lock (m_cachedRegionMapItemsAddress) | ||
535 | { | ||
536 | if (m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | ||
537 | httpserver = m_cachedRegionMapItemsAddress[regionhandle]; | ||
538 | } | ||
539 | if (httpserver.Length == 0) | ||
540 | { | ||
541 | RegionInfo mreg = m_scene.SceneGridService.RequestNeighbouringRegionInfo(regionhandle); | ||
542 | |||
543 | if (mreg != null) | ||
544 | { | ||
545 | httpserver = "http://" + mreg.ExternalEndPoint.Address.ToString() + ":" + mreg.HttpPort + "/MAP/MapItems/" + regionhandle.ToString(); | ||
546 | lock (m_cachedRegionMapItemsAddress) | ||
547 | { | ||
548 | if (!m_cachedRegionMapItemsAddress.ContainsKey(regionhandle)) | ||
549 | m_cachedRegionMapItemsAddress.Add(regionhandle, httpserver); | ||
550 | } | ||
551 | } | ||
552 | else | ||
553 | { | ||
554 | lock (m_blacklistedregions) | ||
555 | { | ||
556 | if (!m_blacklistedregions.ContainsKey(regionhandle)) | ||
557 | m_blacklistedregions.Add(regionhandle, System.Environment.TickCount); | ||
558 | } | ||
559 | m_log.InfoFormat("[WORLD MAP]: Blacklisted region {0}", regionhandle.ToString()); | ||
560 | } | ||
561 | } | ||
562 | |||
563 | blacklisted = false; | ||
564 | lock (m_blacklistedurls) | ||
565 | { | ||
566 | if (m_blacklistedurls.ContainsKey(httpserver)) | ||
567 | blacklisted = true; | ||
568 | } | ||
569 | |||
570 | // Can't find the http server | ||
571 | if (httpserver.Length == 0 || blacklisted) | ||
572 | return new OSDMap(); | ||
573 | |||
574 | MapRequestState mrs = new MapRequestState(); | ||
575 | mrs.agentID = id; | ||
576 | mrs.EstateID = EstateID; | ||
577 | mrs.flags = flags; | ||
578 | mrs.godlike = godlike; | ||
579 | mrs.itemtype=itemtype; | ||
580 | mrs.regionhandle = regionhandle; | ||
581 | |||
582 | lock (m_openRequests) | ||
583 | m_openRequests.Add(requestID, mrs); | ||
584 | |||
585 | WebRequest mapitemsrequest = WebRequest.Create(httpserver); | ||
586 | mapitemsrequest.Method = "POST"; | ||
587 | mapitemsrequest.ContentType = "application/xml+llsd"; | ||
588 | OSDMap RAMap = new OSDMap(); | ||
589 | |||
590 | // string RAMapString = RAMap.ToString(); | ||
591 | OSD LLSDofRAMap = RAMap; // RENAME if this works | ||
592 | |||
593 | byte[] buffer = OSDParser.SerializeLLSDXmlBytes(LLSDofRAMap); | ||
594 | OSDMap responseMap = new OSDMap(); | ||
595 | responseMap["requestID"] = OSD.FromUUID(requestID); | ||
596 | |||
597 | Stream os = null; | ||
598 | try | ||
599 | { // send the Post | ||
600 | mapitemsrequest.ContentLength = buffer.Length; //Count bytes to send | ||
601 | os = mapitemsrequest.GetRequestStream(); | ||
602 | os.Write(buffer, 0, buffer.Length); //Send it | ||
603 | os.Close(); | ||
604 | //m_log.DebugFormat("[WORLD MAP]: Getting MapItems from Sim {0}", httpserver); | ||
605 | } | ||
606 | catch (WebException ex) | ||
607 | { | ||
608 | m_log.WarnFormat("[WORLD MAP]: Bad send on GetMapItems {0}", ex.Message); | ||
609 | responseMap["connect"] = OSD.FromBoolean(false); | ||
610 | lock (m_blacklistedurls) | ||
611 | { | ||
612 | if (!m_blacklistedurls.ContainsKey(httpserver)) | ||
613 | m_blacklistedurls.Add(httpserver, System.Environment.TickCount); | ||
614 | } | ||
615 | |||
616 | m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver); | ||
617 | |||
618 | return responseMap; | ||
619 | } | ||
620 | |||
621 | string response_mapItems_reply = null; | ||
622 | { // get the response | ||
623 | try | ||
624 | { | ||
625 | WebResponse webResponse = mapitemsrequest.GetResponse(); | ||
626 | if (webResponse != null) | ||
627 | { | ||
628 | StreamReader sr = new StreamReader(webResponse.GetResponseStream()); | ||
629 | response_mapItems_reply = sr.ReadToEnd().Trim(); | ||
630 | } | ||
631 | else | ||
632 | { | ||
633 | return new OSDMap(); | ||
634 | } | ||
635 | } | ||
636 | catch (WebException) | ||
637 | { | ||
638 | responseMap["connect"] = OSD.FromBoolean(false); | ||
639 | lock (m_blacklistedurls) | ||
640 | { | ||
641 | if (!m_blacklistedurls.ContainsKey(httpserver)) | ||
642 | m_blacklistedurls.Add(httpserver, System.Environment.TickCount); | ||
643 | } | ||
644 | |||
645 | m_log.WarnFormat("[WORLD MAP]: Blacklisted {0}", httpserver); | ||
646 | |||
647 | return responseMap; | ||
648 | } | ||
649 | OSD rezResponse = null; | ||
650 | try | ||
651 | { | ||
652 | rezResponse = OSDParser.DeserializeLLSDXml(response_mapItems_reply); | ||
653 | |||
654 | responseMap = (OSDMap)rezResponse; | ||
655 | responseMap["requestID"] = OSD.FromUUID(requestID); | ||
656 | } | ||
657 | catch (Exception) | ||
658 | { | ||
659 | //m_log.InfoFormat("[OGP]: exception on parse of rez reply {0}", ex.Message); | ||
660 | responseMap["connect"] = OSD.FromBoolean(false); | ||
661 | |||
662 | return responseMap; | ||
663 | } | ||
664 | } | ||
665 | return responseMap; | ||
666 | } | ||
667 | |||
668 | /// <summary> | ||
669 | /// Requests map blocks in area of minX, maxX, minY, MaxY in world cordinates | ||
670 | /// </summary> | ||
671 | /// <param name="minX"></param> | ||
672 | /// <param name="minY"></param> | ||
673 | /// <param name="maxX"></param> | ||
674 | /// <param name="maxY"></param> | ||
675 | public virtual void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag) | ||
676 | { | ||
677 | List<MapBlockData> mapBlocks; | ||
678 | if ((flag & 0x10000) != 0) // user clicked on the map a tile that isn't visible | ||
679 | { | ||
680 | List<MapBlockData> response = new List<MapBlockData>(); | ||
681 | |||
682 | // this should return one mapblock at most. But make sure: Look whether the one we requested is in there | ||
683 | mapBlocks = m_scene.SceneGridService.RequestNeighbourMapBlocks(minX, minY, maxX, maxY); | ||
684 | if (mapBlocks != null) | ||
685 | { | ||
686 | foreach (MapBlockData block in mapBlocks) | ||
687 | { | ||
688 | if (block.X == minX && block.Y == minY) | ||
689 | { | ||
690 | // found it => add it to response | ||
691 | response.Add(block); | ||
692 | break; | ||
693 | } | ||
694 | } | ||
695 | } | ||
696 | |||
697 | if (response.Count == 0) | ||
698 | { | ||
699 | // response still empty => couldn't find the map-tile the user clicked on => tell the client | ||
700 | MapBlockData block = new MapBlockData(); | ||
701 | block.X = (ushort)minX; | ||
702 | block.Y = (ushort)minY; | ||
703 | block.Access = 254; // == not there | ||
704 | response.Add(block); | ||
705 | } | ||
706 | remoteClient.SendMapBlock(response, 0); | ||
707 | } | ||
708 | else | ||
709 | { | ||
710 | // normal mapblock request. Use the provided values | ||
711 | mapBlocks = m_scene.SceneGridService.RequestNeighbourMapBlocks(minX - 4, minY - 4, maxX + 4, maxY + 4); | ||
712 | remoteClient.SendMapBlock(mapBlocks, flag); | ||
713 | } | ||
714 | } | ||
715 | |||
716 | public Hashtable OnHTTPGetMapImage(Hashtable keysvals) | ||
717 | { | ||
718 | m_log.Debug("[WORLD MAP]: Sending map image jpeg"); | ||
719 | Hashtable reply = new Hashtable(); | ||
720 | int statuscode = 200; | ||
721 | byte[] jpeg = new byte[0]; | ||
722 | |||
723 | if (myMapImageJPEG.Length == 0) | ||
724 | { | ||
725 | MemoryStream imgstream = new MemoryStream(); | ||
726 | Bitmap mapTexture = new Bitmap(1,1); | ||
727 | ManagedImage managedImage; | ||
728 | Image image = (Image)mapTexture; | ||
729 | |||
730 | try | ||
731 | { | ||
732 | // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data | ||
733 | |||
734 | imgstream = new MemoryStream(); | ||
735 | |||
736 | // non-async because we know we have the asset immediately. | ||
737 | AssetBase mapasset = m_scene.AssetCache.GetAsset(m_scene.RegionInfo.lastMapUUID, true); | ||
738 | |||
739 | // Decode image to System.Drawing.Image | ||
740 | if (OpenJPEG.DecodeToImage(mapasset.Data, out managedImage, out image)) | ||
741 | { | ||
742 | // Save to bitmap | ||
743 | mapTexture = new Bitmap(image); | ||
744 | |||
745 | EncoderParameters myEncoderParameters = new EncoderParameters(); | ||
746 | myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L); | ||
747 | |||
748 | // Save bitmap to stream | ||
749 | mapTexture.Save(imgstream, GetEncoderInfo("image/jpeg"), myEncoderParameters); | ||
750 | |||
751 | // Write the stream to a byte array for output | ||
752 | jpeg = imgstream.ToArray(); | ||
753 | myMapImageJPEG = jpeg; | ||
754 | } | ||
755 | } | ||
756 | catch (Exception) | ||
757 | { | ||
758 | // Dummy! | ||
759 | m_log.Warn("[WORLD MAP]: Unable to generate Map image"); | ||
760 | } | ||
761 | finally | ||
762 | { | ||
763 | // Reclaim memory, these are unmanaged resources | ||
764 | mapTexture.Dispose(); | ||
765 | image.Dispose(); | ||
766 | imgstream.Close(); | ||
767 | imgstream.Dispose(); | ||
768 | } | ||
769 | } | ||
770 | else | ||
771 | { | ||
772 | // Use cached version so we don't have to loose our mind | ||
773 | jpeg = myMapImageJPEG; | ||
774 | } | ||
775 | |||
776 | reply["str_response_string"] = Convert.ToBase64String(jpeg); | ||
777 | reply["int_response_code"] = statuscode; | ||
778 | reply["content_type"] = "image/jpeg"; | ||
779 | |||
780 | return reply; | ||
781 | } | ||
782 | |||
783 | // From msdn | ||
784 | private static ImageCodecInfo GetEncoderInfo(String mimeType) | ||
785 | { | ||
786 | ImageCodecInfo[] encoders; | ||
787 | encoders = ImageCodecInfo.GetImageEncoders(); | ||
788 | for (int j = 0; j < encoders.Length; ++j) | ||
789 | { | ||
790 | if (encoders[j].MimeType == mimeType) | ||
791 | return encoders[j]; | ||
792 | } | ||
793 | return null; | ||
794 | } | ||
795 | |||
796 | public OSD HandleRemoteMapItemRequest(string path, OSD request, string endpoint) | ||
797 | { | ||
798 | uint xstart = 0; | ||
799 | uint ystart = 0; | ||
800 | |||
801 | Utils.LongToUInts(m_scene.RegionInfo.RegionHandle,out xstart,out ystart); | ||
802 | |||
803 | OSDMap responsemap = new OSDMap(); | ||
804 | List<ScenePresence> avatars = m_scene.GetAvatars(); | ||
805 | OSDArray responsearr = new OSDArray(avatars.Count); | ||
806 | OSDMap responsemapdata = new OSDMap(); | ||
807 | int tc = System.Environment.TickCount; | ||
808 | /* | ||
809 | foreach (ScenePresence av in avatars) | ||
810 | { | ||
811 | responsemapdata = new OSDMap(); | ||
812 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + av.AbsolutePosition.X)); | ||
813 | responsemapdata["Y"] = OSD.FromInteger((int)(ystart + av.AbsolutePosition.Y)); | ||
814 | responsemapdata["ID"] = OSD.FromUUID(UUID.Zero); | ||
815 | responsemapdata["Name"] = OSD.FromString("TH"); | ||
816 | responsemapdata["Extra"] = OSD.FromInteger(0); | ||
817 | responsemapdata["Extra2"] = OSD.FromInteger(0); | ||
818 | responsearr.Add(responsemapdata); | ||
819 | } | ||
820 | responsemap["1"] = responsearr; | ||
821 | */ | ||
822 | if (avatars.Count == 0) | ||
823 | { | ||
824 | responsemapdata = new OSDMap(); | ||
825 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + 1)); | ||
826 | responsemapdata["Y"] = OSD.FromInteger((int)(ystart + 1)); | ||
827 | responsemapdata["ID"] = OSD.FromUUID(UUID.Zero); | ||
828 | responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString())); | ||
829 | responsemapdata["Extra"] = OSD.FromInteger(0); | ||
830 | responsemapdata["Extra2"] = OSD.FromInteger(0); | ||
831 | responsearr.Add(responsemapdata); | ||
832 | |||
833 | responsemap["6"] = responsearr; | ||
834 | } | ||
835 | else | ||
836 | { | ||
837 | responsearr = new OSDArray(avatars.Count); | ||
838 | foreach (ScenePresence av in avatars) | ||
839 | { | ||
840 | responsemapdata = new OSDMap(); | ||
841 | responsemapdata["X"] = OSD.FromInteger((int)(xstart + av.AbsolutePosition.X)); | ||
842 | responsemapdata["Y"] = OSD.FromInteger((int)(ystart + av.AbsolutePosition.Y)); | ||
843 | responsemapdata["ID"] = OSD.FromUUID(UUID.Zero); | ||
844 | responsemapdata["Name"] = OSD.FromString(Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString())); | ||
845 | responsemapdata["Extra"] = OSD.FromInteger(1); | ||
846 | responsemapdata["Extra2"] = OSD.FromInteger(0); | ||
847 | responsearr.Add(responsemapdata); | ||
848 | } | ||
849 | responsemap["6"] = responsearr; | ||
850 | } | ||
851 | return responsemap; | ||
852 | } | ||
853 | |||
854 | private void MakeRootAgent(ScenePresence avatar) | ||
855 | { | ||
856 | // You may ask, why this is in a threadpool to start with.. | ||
857 | // The reason is so we don't cause the thread to freeze waiting | ||
858 | // for the 1 second it costs to start a thread manually. | ||
859 | if (!threadrunning) | ||
860 | ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartThread)); | ||
861 | |||
862 | lock (m_rootAgents) | ||
863 | { | ||
864 | if (!m_rootAgents.Contains(avatar.UUID)) | ||
865 | { | ||
866 | m_rootAgents.Add(avatar.UUID); | ||
867 | } | ||
868 | } | ||
869 | } | ||
870 | |||
871 | private void MakeChildAgent(ScenePresence avatar) | ||
872 | { | ||
873 | List<ScenePresence> presences = m_scene.GetAvatars(); | ||
874 | int rootcount = 0; | ||
875 | for (int i = 0; i < presences.Count; i++) | ||
876 | { | ||
877 | if (presences[i] != null) | ||
878 | { | ||
879 | if (!presences[i].IsChildAgent) | ||
880 | rootcount++; | ||
881 | } | ||
882 | } | ||
883 | if (rootcount <= 1) | ||
884 | StopThread(); | ||
885 | |||
886 | lock (m_rootAgents) | ||
887 | { | ||
888 | if (m_rootAgents.Contains(avatar.UUID)) | ||
889 | { | ||
890 | m_rootAgents.Remove(avatar.UUID); | ||
891 | } | ||
892 | } | ||
893 | } | ||
894 | } | ||
895 | |||
896 | public struct MapRequestState | ||
897 | { | ||
898 | public UUID agentID; | ||
899 | public uint flags; | ||
900 | public uint EstateID; | ||
901 | public bool godlike; | ||
902 | public uint itemtype; | ||
903 | public ulong regionhandle; | ||
904 | } | ||
905 | } | ||