clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 OpenSimulator 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.Net;
32 using System.IO;
33 using System.Timers;
34 using System.Drawing;
35 using System.Drawing.Imaging;
36  
37 using log4net;
38 using Mono.Addins;
39 using Nini.Config;
40 using OpenSim.Framework;
41 using OpenSim.Region.Framework.Interfaces;
42 using OpenSim.Region.Framework.Scenes;
43 using OpenSim.Services.Interfaces;
44 using OpenSim.Server.Base;
45 using OpenMetaverse;
46 using OpenMetaverse.StructuredData;
47  
48 namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage
49 {
50 /// <summary>
51 /// </summary>
52 /// <remarks>
53 /// </remarks>
54  
55 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MapImageServiceModule")]
56 public class MapImageServiceModule : ISharedRegionModule
57 {
58 private static readonly ILog m_log =
59 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
60 private static string LogHeader = "[MAP IMAGE SERVICE MODULE]";
61  
62 private bool m_enabled = false;
63 private IMapImageService m_MapService;
64  
65 private Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>();
66  
67 private int m_refreshtime = 0;
68 private int m_lastrefresh = 0;
69 private System.Timers.Timer m_refreshTimer;
70  
71 #region ISharedRegionModule
72  
73 public Type ReplaceableInterface { get { return null; } }
74 public string Name { get { return "MapImageServiceModule"; } }
75 public void RegionLoaded(Scene scene) { }
76 public void Close() { }
77 public void PostInitialise() { }
78  
79 ///<summary>
80 ///
81 ///</summary>
82 public void Initialise(IConfigSource source)
83 {
84 IConfig moduleConfig = source.Configs["Modules"];
85 if (moduleConfig != null)
86 {
87 string name = moduleConfig.GetString("MapImageService", "");
88 if (name != Name)
89 return;
90 }
91  
92 IConfig config = source.Configs["MapImageService"];
93 if (config == null)
94 return;
95  
96 int refreshminutes = Convert.ToInt32(config.GetString("RefreshTime"));
97  
98 // if refresh is less than zero, disable the module
99 if (refreshminutes < 0)
100 {
101 m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: Negative refresh time given in config. Module disabled.");
102 return;
103 }
104  
105 string service = config.GetString("LocalServiceModule", string.Empty);
106 if (service == string.Empty)
107 {
108 m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: No service dll given in config. Unable to proceed.");
109 return;
110 }
111  
112 Object[] args = new Object[] { source };
113 m_MapService = ServerUtils.LoadPlugin<IMapImageService>(service, args);
114 if (m_MapService == null)
115 {
116 m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: Unable to load LocalServiceModule from {0}. MapService module disabled. Please fix the configuration.", service);
117 return;
118 }
119  
120 // we don't want the timer if the interval is zero, but we still want this module enables
121 if(refreshminutes > 0)
122 {
123 m_refreshtime = refreshminutes * 60 * 1000; // convert from minutes to ms
124  
125 m_refreshTimer = new System.Timers.Timer();
126 m_refreshTimer.Enabled = true;
127 m_refreshTimer.AutoReset = true;
128 m_refreshTimer.Interval = m_refreshtime;
129 m_refreshTimer.Elapsed += new ElapsedEventHandler(HandleMaptileRefresh);
130  
131 m_log.InfoFormat("[MAP IMAGE SERVICE MODULE]: enabled with refresh time {0} min and service object {1}",
132 refreshminutes, service);
133 }
134 else
135 {
136 m_log.InfoFormat("[MAP IMAGE SERVICE MODULE]: enabled with no refresh and service object {0}", service);
137 }
138 m_enabled = true;
139 }
140  
141 ///<summary>
142 ///
143 ///</summary>
144 public void AddRegion(Scene scene)
145 {
146 if (!m_enabled)
147 return;
148  
149 // Every shared region module has to maintain an indepedent list of
150 // currently running regions
151 lock (m_scenes)
152 m_scenes[scene.RegionInfo.RegionID] = scene;
153  
154 scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); };
155 }
156  
157 ///<summary>
158 ///
159 ///</summary>
160 public void RemoveRegion(Scene scene)
161 {
162 if (! m_enabled)
163 return;
164  
165 lock (m_scenes)
166 m_scenes.Remove(scene.RegionInfo.RegionID);
167 }
168  
169 #endregion ISharedRegionModule
170  
171 ///<summary>
172 ///
173 ///</summary>
174 private void HandleMaptileRefresh(object sender, EventArgs ea)
175 {
176 // this approach is a bit convoluted becase we want to wait for the
177 // first upload to happen on startup but after all the objects are
178 // loaded and initialized
179 if (m_lastrefresh > 0 && Util.EnvironmentTickCountSubtract(m_lastrefresh) < m_refreshtime)
180 return;
181  
182 m_log.DebugFormat("[MAP IMAGE SERVICE MODULE]: map refresh!");
183 lock (m_scenes)
184 {
185 foreach (IScene scene in m_scenes.Values)
186 {
187 try
188 {
189 UploadMapTile(scene);
190 }
191 catch (Exception ex)
192 {
193 m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: something bad happened {0}", ex.Message);
194 }
195 }
196 }
197  
198 m_lastrefresh = Util.EnvironmentTickCount();
199 }
200  
201 ///<summary>
202 ///
203 ///</summary>
204 private void UploadMapTile(IScene scene)
205 {
206 m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.RegionInfo.RegionName);
207 string regionName = scene.RegionInfo.RegionName;
208  
209 // Create a JPG map tile and upload it to the AddMapTile API
210 IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
211 if (tileGenerator == null)
212 {
213 m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader);
214 return;
215 }
216 using (Bitmap mapTile = tileGenerator.CreateMapTile())
217 {
218 if (mapTile != null)
219 {
220 // mapTile.Save( // DEBUG DEBUG
221 // String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
222 // ImageFormat.Jpeg);
223 // If the region/maptile is legacy sized, just upload the one tile like it has always been done
224 if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
225 {
226 ConvertAndUploadMaptile(mapTile,
227 scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY,
228 scene.RegionInfo.RegionName);
229 }
230 else
231 {
232 // For larger regions (varregion) we must cut the region image into legacy sized
233 // pieces since that is how the maptile system works.
234 // Note the assumption that varregions are always a multiple of legacy size.
235 for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
236 {
237 for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
238 {
239 // Images are addressed from the upper left corner so have to do funny
240 // math to pick out the sub-tile since regions are numbered from
241 // the lower left.
242 Rectangle rect = new Rectangle(
243 (int)xx,
244 mapTile.Height - (int)yy - (int)Constants.RegionSize,
245 (int)Constants.RegionSize, (int)Constants.RegionSize);
246 using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
247 {
248 ConvertAndUploadMaptile(subMapTile,
249 scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
250 scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
251 regionName);
252 }
253 }
254 }
255 }
256 }
257 else
258 {
259 m_log.WarnFormat("{0} Tile image generation failed", LogHeader);
260 }
261 }
262 }
263  
264 private void ConvertAndUploadMaptile(Image tileImage, uint locX, uint locY, string regionName)
265 {
266 byte[] jpgData = Utils.EmptyBytes;
267  
268 using (MemoryStream stream = new MemoryStream())
269 {
270 tileImage.Save(stream, ImageFormat.Jpeg);
271 jpgData = stream.ToArray();
272 }
273 if (jpgData != Utils.EmptyBytes)
274 {
275 string reason = string.Empty;
276 if (!m_MapService.AddMapTile((int)locX, (int)locY, jpgData, out reason))
277 {
278 m_log.DebugFormat("{0} Unable to upload tile image for {1} at {2}-{3}: {4}", LogHeader,
279 regionName, locX, locY, reason);
280 }
281 }
282 else
283 {
284 m_log.WarnFormat("{0} Tile image generation failed for region {1}", LogHeader, regionName);
285 }
286 }
287 }
288 }