OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
MapImageServiceModule.cs
Go to the documentation of this file.
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 {
54 
55  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MapImageServiceModule")]
56 
58  {
59  private static readonly ILog m_log =
60  LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
61  private static string LogHeader = "[MAP IMAGE SERVICE MODULE]:";
62 
63  private bool m_enabled = false;
64  private IMapImageService m_MapService;
65 
66  private Dictionary<UUID, Scene> m_scenes = new Dictionary<UUID, Scene>();
67 
68  private int m_refreshtime = 0;
69  private int m_lastrefresh = 0;
70  private System.Timers.Timer m_refreshTimer;
71 
72  #region ISharedRegionModule
73 
74  public Type ReplaceableInterface { get { return null; } }
75  public string Name { get { return "MapImageServiceModule"; } }
76  public void RegionLoaded(Scene scene) { }
77  public void Close() { }
78  public void PostInitialise() { }
79 
83  public void Initialise(IConfigSource source)
84  {
85  IConfig moduleConfig = source.Configs["Modules"];
86  if (moduleConfig != null)
87  {
88  string name = moduleConfig.GetString("MapImageService", "");
89  if (name != Name)
90  return;
91  }
92 
93  IConfig config = source.Configs["MapImageService"];
94  if (config == null)
95  return;
96 
97  int refreshminutes = Convert.ToInt32(config.GetString("RefreshTime"));
98  if (refreshminutes < 0)
99  {
100  m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: Negative refresh time given in config. Module disabled.");
101  return;
102  }
103 
104  string service = config.GetString("LocalServiceModule", string.Empty);
105  if (service == string.Empty)
106  {
107  m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: No service dll given in config. Unable to proceed.");
108  return;
109  }
110 
111  Object[] args = new Object[] { source };
112  m_MapService = ServerUtils.LoadPlugin<IMapImageService>(service, args);
113  if (m_MapService == null)
114  {
115  m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: Unable to load LocalServiceModule from {0}. MapService module disabled. Please fix the configuration.", service);
116  return;
117  }
118 
119  // we don't want the timer if the interval is zero, but we still want this module enables
120  if(refreshminutes > 0)
121  {
122  m_refreshtime = refreshminutes * 60 * 1000; // convert from minutes to ms
123 
124  m_refreshTimer = new System.Timers.Timer();
125  m_refreshTimer.Enabled = true;
126  m_refreshTimer.AutoReset = true;
127  m_refreshTimer.Interval = m_refreshtime;
128  m_refreshTimer.Elapsed += new ElapsedEventHandler(HandleMaptileRefresh);
129 
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 
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  // v2 Map generation on startup is now handled by scene to allow bmp to be shared with
155  // v1 service and not generate map tiles twice as was previous behavior
156  //scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); };
157 
158  scene.RegisterModuleInterface<IMapImageUploadModule>(this);
159  }
160 
164  public void RemoveRegion(Scene scene)
165  {
166  if (! m_enabled)
167  return;
168 
169  lock (m_scenes)
170  m_scenes.Remove(scene.RegionInfo.RegionID);
171  }
172 
173  #endregion ISharedRegionModule
174 
178  private void HandleMaptileRefresh(object sender, EventArgs ea)
179  {
180  // this approach is a bit convoluted becase we want to wait for the
181  // first upload to happen on startup but after all the objects are
182  // loaded and initialized
183  if (m_lastrefresh > 0 && Util.EnvironmentTickCountSubtract(m_lastrefresh) < m_refreshtime)
184  return;
185 
186  m_log.DebugFormat("[MAP IMAGE SERVICE MODULE]: map refresh!");
187  lock (m_scenes)
188  {
189  foreach (IScene scene in m_scenes.Values)
190  {
191  try
192  {
193  UploadMapTile(scene);
194  }
195  catch (Exception ex)
196  {
197  m_log.WarnFormat("[MAP IMAGE SERVICE MODULE]: something bad happened {0}", ex.Message);
198  }
199  }
200  }
201 
202  m_lastrefresh = Util.EnvironmentTickCount();
203  }
204 
205  public void UploadMapTile(IScene scene, Bitmap mapTile)
206  {
207  if (mapTile == null)
208  {
209  m_log.WarnFormat("{0} Cannot upload null image", LogHeader);
210  return;
211  }
212 
213  m_log.DebugFormat("{0} Upload maptile for {1}", LogHeader, scene.Name);
214 
215  // mapTile.Save( // DEBUG DEBUG
216  // String.Format("maptiles/raw-{0}-{1}-{2}.jpg", regionName, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY),
217  // ImageFormat.Jpeg);
218  // If the region/maptile is legacy sized, just upload the one tile like it has always been done
219  if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize)
220  {
221  ConvertAndUploadMaptile(scene, mapTile,
223  scene.RegionInfo.RegionName);
224  }
225  else
226  {
227  // For larger regions (varregion) we must cut the region image into legacy sized
228  // pieces since that is how the maptile system works.
229  // Note the assumption that varregions are always a multiple of legacy size.
230  for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize)
231  {
232  for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize)
233  {
234  // Images are addressed from the upper left corner so have to do funny
235  // math to pick out the sub-tile since regions are numbered from
236  // the lower left.
237  Rectangle rect = new Rectangle(
238  (int)xx,
239  mapTile.Height - (int)yy - (int)Constants.RegionSize,
241  using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat))
242  {
243  ConvertAndUploadMaptile(scene, subMapTile,
244  scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize),
245  scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize),
246  scene.Name);
247  }
248  }
249  }
250  }
251  }
252 
256  public void UploadMapTile(IScene scene)
257  {
258  m_log.DebugFormat("{0}: upload maptile for {1}", LogHeader, scene.RegionInfo.RegionName);
259 
260  // Create a JPG map tile and upload it to the AddMapTile API
261  IMapImageGenerator tileGenerator = scene.RequestModuleInterface<IMapImageGenerator>();
262  if (tileGenerator == null)
263  {
264  m_log.WarnFormat("{0} Cannot upload map tile without an ImageGenerator", LogHeader);
265  return;
266  }
267 
268  using (Bitmap mapTile = tileGenerator.CreateMapTile())
269  {
270  // XXX: The MapImageModule will return a null if the user has chosen not to create map tiles and there
271  // is no static map tile.
272  if (mapTile == null)
273  return;
274 
275  UploadMapTile(scene, mapTile);
276  }
277  }
278 
279  private void ConvertAndUploadMaptile(IScene scene, Image tileImage, uint locX, uint locY, string regionName)
280  {
281  byte[] jpgData = Utils.EmptyBytes;
282 
283  using (MemoryStream stream = new MemoryStream())
284  {
285  tileImage.Save(stream, ImageFormat.Jpeg);
286  jpgData = stream.ToArray();
287  }
288  if (jpgData != Utils.EmptyBytes)
289  {
290  string reason = string.Empty;
291  if (!m_MapService.AddMapTile((int)locX, (int)locY, jpgData, scene.RegionInfo.ScopeID, out reason))
292  {
293  m_log.DebugFormat("{0} Unable to upload tile image for {1} at {2}-{3}: {4}", LogHeader,
294  regionName, locX, locY, reason);
295  }
296  }
297  else
298  {
299  m_log.WarnFormat("{0} Tile image generation failed for region {1}", LogHeader, regionName);
300  }
301  }
302  }
303 }
RegionInfo RegionInfo
Definition: IScene.cs:64
OpenSim.Server.Handlers.Simulation.Utils Utils
string Name
The name of this scene.
Definition: IScene.cs:62
Interactive OpenSim region server
Definition: OpenSim.cs:55
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
uint RegionLocY
The y co-ordinate of this region in map tiles (e.g. 1000). Coordinate is scaled as world coordinates ...
Definition: RegionInfo.cs:496
uint RegionLocX
The x co-ordinate of this region in map tiles (e.g. 1000). Coordinate is scaled as world coordinates ...
Definition: RegionInfo.cs:485