GridService.cs 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  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. using System;
  28. using System.Collections.Generic;
  29. using System.Net;
  30. using System.Reflection;
  31. using Nini.Config;
  32. using log4net;
  33. using OpenSim.Framework;
  34. using OpenSim.Framework.Console;
  35. using OpenSim.Data;
  36. using OpenSim.Server.Base;
  37. using OpenSim.Services.Interfaces;
  38. using GridRegion = OpenSim.Services.Interfaces.GridRegion;
  39. using OpenMetaverse;
  40. namespace OpenSim.Services.GridService
  41. {
  42. public class GridService : GridServiceBase, IGridService
  43. {
  44. private static readonly ILog m_log =
  45. LogManager.GetLogger(
  46. MethodBase.GetCurrentMethod().DeclaringType);
  47. private string LogHeader = "[GRID SERVICE]";
  48. private bool m_DeleteOnUnregister = true;
  49. private static GridService m_RootInstance = null;
  50. protected IConfigSource m_config;
  51. protected static HypergridLinker m_HypergridLinker;
  52. protected IAuthenticationService m_AuthenticationService = null;
  53. protected bool m_AllowDuplicateNames = false;
  54. protected bool m_AllowHypergridMapSearch = false;
  55. protected bool m_SuppressVarregionOverlapCheckOnRegistration = false;
  56. private static Dictionary<string,object> m_ExtraFeatures = new Dictionary<string, object>();
  57. public GridService(IConfigSource config)
  58. : base(config)
  59. {
  60. m_log.DebugFormat("[GRID SERVICE]: Starting...");
  61. m_config = config;
  62. IConfig gridConfig = config.Configs["GridService"];
  63. bool suppressConsoleCommands = false;
  64. if (gridConfig != null)
  65. {
  66. m_DeleteOnUnregister = gridConfig.GetBoolean("DeleteOnUnregister", true);
  67. string authService = gridConfig.GetString("AuthenticationService", String.Empty);
  68. if (authService != String.Empty)
  69. {
  70. Object[] args = new Object[] { config };
  71. m_AuthenticationService = ServerUtils.LoadPlugin<IAuthenticationService>(authService, args);
  72. }
  73. m_AllowDuplicateNames = gridConfig.GetBoolean("AllowDuplicateNames", m_AllowDuplicateNames);
  74. m_AllowHypergridMapSearch = gridConfig.GetBoolean("AllowHypergridMapSearch", m_AllowHypergridMapSearch);
  75. m_SuppressVarregionOverlapCheckOnRegistration = gridConfig.GetBoolean("SuppressVarregionOverlapCheckOnRegistration", m_SuppressVarregionOverlapCheckOnRegistration);
  76. // This service is also used locally by a simulator running in grid mode. This switches prevents
  77. // inappropriate console commands from being registered
  78. suppressConsoleCommands = gridConfig.GetBoolean("SuppressConsoleCommands", suppressConsoleCommands);
  79. }
  80. if (m_RootInstance == null)
  81. {
  82. m_RootInstance = this;
  83. if (!suppressConsoleCommands && MainConsole.Instance != null)
  84. {
  85. MainConsole.Instance.Commands.AddCommand("Regions", true,
  86. "deregister region id",
  87. "deregister region id <region-id>+",
  88. "Deregister a region manually.",
  89. String.Empty,
  90. HandleDeregisterRegion);
  91. MainConsole.Instance.Commands.AddCommand("Regions", true,
  92. "show regions",
  93. "show regions",
  94. "Show details on all regions",
  95. String.Empty,
  96. HandleShowRegions);
  97. MainConsole.Instance.Commands.AddCommand("Regions", true,
  98. "show region name",
  99. "show region name <Region name>",
  100. "Show details on a region",
  101. String.Empty,
  102. HandleShowRegion);
  103. MainConsole.Instance.Commands.AddCommand("Regions", true,
  104. "show region at",
  105. "show region at <x-coord> <y-coord>",
  106. "Show details on a region at the given co-ordinate.",
  107. "For example, show region at 1000 1000",
  108. HandleShowRegionAt);
  109. MainConsole.Instance.Commands.AddCommand("General", true,
  110. "show grid size",
  111. "show grid size",
  112. "Show the current grid size (excluding hyperlink references)",
  113. String.Empty,
  114. HandleShowGridSize);
  115. MainConsole.Instance.Commands.AddCommand("Regions", true,
  116. "set region flags",
  117. "set region flags <Region name> <flags>",
  118. "Set database flags for region",
  119. String.Empty,
  120. HandleSetFlags);
  121. }
  122. if (!suppressConsoleCommands)
  123. SetExtraServiceURLs(config);
  124. m_HypergridLinker = new HypergridLinker(m_config, this, m_Database);
  125. }
  126. }
  127. private void SetExtraServiceURLs(IConfigSource config)
  128. {
  129. IConfig loginConfig = config.Configs["LoginService"];
  130. IConfig gridConfig = config.Configs["GridService"];
  131. if (loginConfig == null || gridConfig == null)
  132. return;
  133. string configVal;
  134. configVal = loginConfig.GetString("SearchURL", string.Empty);
  135. if (!string.IsNullOrEmpty(configVal))
  136. m_ExtraFeatures["search-server-url"] = configVal;
  137. configVal = loginConfig.GetString("MapTileURL", string.Empty);
  138. if (!string.IsNullOrEmpty(configVal))
  139. {
  140. // This URL must end with '/', the viewer doesn't check
  141. configVal = configVal.Trim();
  142. if (!configVal.EndsWith("/"))
  143. configVal = configVal + "/";
  144. m_ExtraFeatures["map-server-url"] = configVal;
  145. }
  146. configVal = loginConfig.GetString("DestinationGuide", string.Empty);
  147. if (!string.IsNullOrEmpty(configVal))
  148. m_ExtraFeatures["destination-guide-url"] = configVal;
  149. configVal = Util.GetConfigVarFromSections<string>(
  150. config, "GatekeeperURI", new string[] { "Startup", "Hypergrid" }, String.Empty);
  151. if (!string.IsNullOrEmpty(configVal))
  152. m_ExtraFeatures["GridURL"] = configVal;
  153. configVal = Util.GetConfigVarFromSections<string>(
  154. config, "GridName", new string[] { "Const", "Hypergrid" }, String.Empty);
  155. if (string.IsNullOrEmpty(configVal))
  156. configVal = Util.GetConfigVarFromSections<string>(
  157. config, "gridname", new string[] { "GridInfo" }, String.Empty);
  158. if (!string.IsNullOrEmpty(configVal))
  159. m_ExtraFeatures["GridName"] = configVal;
  160. m_ExtraFeatures["ExportSupported"] = gridConfig.GetString("ExportSupported", "true");
  161. }
  162. #region IGridService
  163. public string RegisterRegion(UUID scopeID, GridRegion regionInfos)
  164. {
  165. IConfig gridConfig = m_config.Configs["GridService"];
  166. if (regionInfos.RegionID == UUID.Zero)
  167. return "Invalid RegionID - cannot be zero UUID";
  168. String reason = "Region overlaps another region";
  169. // we should not need to check for overlaps
  170. RegionData region = m_Database.Get(regionInfos.RegionLocX, regionInfos.RegionLocY, scopeID);
  171. if ((region != null) && (region.RegionID != regionInfos.RegionID))
  172. {
  173. // If not same ID and same coordinates, this new region has conflicts and can't be registered.
  174. m_log.WarnFormat("{0} Register region conflict in scope {1}. {2}", LogHeader, scopeID, reason);
  175. return reason;
  176. }
  177. if (region != null)
  178. {
  179. // There is a preexisting record
  180. //
  181. // Get it's flags
  182. //
  183. OpenSim.Framework.RegionFlags rflags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(region.Data["flags"]);
  184. // Is this a reservation?
  185. //
  186. if ((rflags & OpenSim.Framework.RegionFlags.Reservation) != 0)
  187. {
  188. // Regions reserved for the null key cannot be taken.
  189. if ((string)region.Data["PrincipalID"] == UUID.Zero.ToString())
  190. return "Region location is reserved";
  191. // Treat it as an auth request
  192. //
  193. // NOTE: Fudging the flags value here, so these flags
  194. // should not be used elsewhere. Don't optimize
  195. // this with the later retrieval of the same flags!
  196. rflags |= OpenSim.Framework.RegionFlags.Authenticate;
  197. }
  198. if ((rflags & OpenSim.Framework.RegionFlags.Authenticate) != 0)
  199. {
  200. // Can we authenticate at all?
  201. //
  202. if (m_AuthenticationService == null)
  203. return "No authentication possible";
  204. if (!m_AuthenticationService.Verify(new UUID(region.Data["PrincipalID"].ToString()), regionInfos.Token, 30))
  205. return "Bad authentication";
  206. }
  207. }
  208. // If we get here, the destination is clear. Now for the real check.
  209. if (!m_AllowDuplicateNames)
  210. {
  211. List<RegionData> dupe = m_Database.Get(Util.EscapeForLike(regionInfos.RegionName), scopeID);
  212. if (dupe != null && dupe.Count > 0)
  213. {
  214. foreach (RegionData d in dupe)
  215. {
  216. if (d.RegionID != regionInfos.RegionID)
  217. {
  218. m_log.WarnFormat("[GRID SERVICE]: Region tried to register using a duplicate name. New region: {0} ({1}), existing region: {2} ({3}).",
  219. regionInfos.RegionName, regionInfos.RegionID, d.RegionName, d.RegionID);
  220. return "Duplicate region name";
  221. }
  222. }
  223. }
  224. }
  225. // If there is an old record for us, delete it if it is elsewhere.
  226. region = m_Database.Get(regionInfos.RegionID, scopeID);
  227. if ((region != null) && (region.RegionID == regionInfos.RegionID) &&
  228. ((region.posX != regionInfos.RegionLocX) || (region.posY != regionInfos.RegionLocY)))
  229. {
  230. if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.NoMove) != 0)
  231. return "Can't move this region";
  232. if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.LockedOut) != 0)
  233. return "Region locked out";
  234. // Region reregistering in other coordinates. Delete the old entry
  235. m_log.DebugFormat("[GRID SERVICE]: Region {0} ({1}) was previously registered at {2}-{3}. Deleting old entry.",
  236. regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionLocX, regionInfos.RegionLocY);
  237. try
  238. {
  239. m_Database.Delete(regionInfos.RegionID);
  240. }
  241. catch (Exception e)
  242. {
  243. m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e);
  244. }
  245. }
  246. // Everything is ok, let's register
  247. RegionData rdata = RegionInfo2RegionData(regionInfos);
  248. rdata.ScopeID = scopeID;
  249. if (region != null)
  250. {
  251. int oldFlags = Convert.ToInt32(region.Data["flags"]);
  252. oldFlags &= ~(int)OpenSim.Framework.RegionFlags.Reservation;
  253. rdata.Data["flags"] = oldFlags.ToString(); // Preserve flags
  254. }
  255. else
  256. {
  257. rdata.Data["flags"] = "0";
  258. if ((gridConfig != null) && rdata.RegionName != string.Empty)
  259. {
  260. int newFlags = 0;
  261. string regionName = rdata.RegionName.Trim().Replace(' ', '_');
  262. newFlags = ParseFlags(newFlags, gridConfig.GetString("DefaultRegionFlags", String.Empty));
  263. newFlags = ParseFlags(newFlags, gridConfig.GetString("Region_" + regionName, String.Empty));
  264. newFlags = ParseFlags(newFlags, gridConfig.GetString("Region_" + rdata.RegionID.ToString(), String.Empty));
  265. rdata.Data["flags"] = newFlags.ToString();
  266. }
  267. }
  268. int flags = Convert.ToInt32(rdata.Data["flags"]);
  269. flags |= (int)OpenSim.Framework.RegionFlags.RegionOnline;
  270. rdata.Data["flags"] = flags.ToString();
  271. try
  272. {
  273. rdata.Data["last_seen"] = Util.UnixTimeSinceEpoch();
  274. m_Database.Store(rdata);
  275. }
  276. catch (Exception e)
  277. {
  278. m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e);
  279. }
  280. m_log.DebugFormat
  281. ("[GRID SERVICE]: Region {0} ({1}, {2}x{3}) registered at {4},{5} with flags {6}",
  282. regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionSizeX, regionInfos.RegionSizeY,
  283. regionInfos.RegionCoordX, regionInfos.RegionCoordY,
  284. (OpenSim.Framework.RegionFlags)flags);
  285. return String.Empty;
  286. }
  287. /// <summary>
  288. /// Search the region map for regions conflicting with this region.
  289. /// The region to be added is passed and we look for any existing regions that are
  290. /// in the requested location, that are large varregions that overlap this region, or
  291. /// are previously defined regions that would lie under this new region.
  292. /// </summary>
  293. /// <param name="regionInfos">Information on region requested to be added to the world map</param>
  294. /// <param name="scopeID">Grid id for region</param>
  295. /// <param name="reason">The reason the returned region conflicts with passed region</param>
  296. /// <returns></returns>
  297. private RegionData FindAnyConflictingRegion(GridRegion regionInfos, UUID scopeID, out string reason)
  298. {
  299. reason = "Reregistration";
  300. // First see if there is an existing region right where this region is trying to go
  301. // (We keep this result so it can be returned if suppressing errors)
  302. RegionData noErrorRegion = m_Database.Get(regionInfos.RegionLocX, regionInfos.RegionLocY, scopeID);
  303. RegionData region = noErrorRegion;
  304. if (region != null
  305. && region.RegionID == regionInfos.RegionID
  306. && region.sizeX == regionInfos.RegionSizeX
  307. && region.sizeY == regionInfos.RegionSizeY)
  308. {
  309. // If this seems to be exactly the same region, return this as it could be
  310. // a re-registration (permissions checked by calling routine).
  311. m_log.DebugFormat("{0} FindAnyConflictingRegion: re-register of {1}",
  312. LogHeader, RegionString(regionInfos));
  313. return region;
  314. }
  315. // No region exactly there or we're resizing an existing region.
  316. // Fetch regions that could be varregions overlapping requested location.
  317. int xmin = regionInfos.RegionLocX - (int)Constants.MaximumRegionSize + 10;
  318. int xmax = regionInfos.RegionLocX;
  319. int ymin = regionInfos.RegionLocY - (int)Constants.MaximumRegionSize + 10;
  320. int ymax = regionInfos.RegionLocY;
  321. List<RegionData> rdatas = m_Database.Get(xmin, ymin, xmax, ymax, scopeID);
  322. foreach (RegionData rdata in rdatas)
  323. {
  324. // m_log.DebugFormat("{0} FindAnyConflictingRegion: find existing. Checking {1}", LogHeader, RegionString(rdata) );
  325. if ( (rdata.posX + rdata.sizeX > regionInfos.RegionLocX)
  326. && (rdata.posY + rdata.sizeY > regionInfos.RegionLocY) )
  327. {
  328. region = rdata;
  329. m_log.WarnFormat("{0} FindAnyConflictingRegion: conflict of {1} by existing varregion {2}",
  330. LogHeader, RegionString(regionInfos), RegionString(region));
  331. reason = String.Format("Region location is overlapped by existing varregion {0}",
  332. RegionString(region));
  333. if (m_SuppressVarregionOverlapCheckOnRegistration)
  334. region = noErrorRegion;
  335. return region;
  336. }
  337. }
  338. // There isn't a region that overlaps this potential region.
  339. // See if this potential region overlaps an existing region.
  340. // First, a shortcut of not looking for overlap if new region is legacy region sized
  341. // and connot overlap anything.
  342. if (regionInfos.RegionSizeX != Constants.RegionSize
  343. || regionInfos.RegionSizeY != Constants.RegionSize)
  344. {
  345. // trim range looked for so we don't pick up neighbor regions just off the edges
  346. xmin = regionInfos.RegionLocX;
  347. xmax = regionInfos.RegionLocX + regionInfos.RegionSizeX - 10;
  348. ymin = regionInfos.RegionLocY;
  349. ymax = regionInfos.RegionLocY + regionInfos.RegionSizeY - 10;
  350. rdatas = m_Database.Get(xmin, ymin, xmax, ymax, scopeID);
  351. // If the region is being resized, the found region could be ourself.
  352. foreach (RegionData rdata in rdatas)
  353. {
  354. // m_log.DebugFormat("{0} FindAnyConflictingRegion: see if overlap. Checking {1}", LogHeader, RegionString(rdata) );
  355. if (region == null || region.RegionID != regionInfos.RegionID)
  356. {
  357. region = rdata;
  358. m_log.WarnFormat("{0} FindAnyConflictingRegion: conflict of varregion {1} overlaps existing region {2}",
  359. LogHeader, RegionString(regionInfos), RegionString(region));
  360. reason = String.Format("Region {0} would overlap existing region {1}",
  361. RegionString(regionInfos), RegionString(region));
  362. if (m_SuppressVarregionOverlapCheckOnRegistration)
  363. region = noErrorRegion;
  364. return region;
  365. }
  366. }
  367. }
  368. // If we get here, region is either null (nothing found here) or
  369. // is the non-conflicting region found at the location being requested.
  370. return region;
  371. }
  372. // String describing name and region location of passed region
  373. private String RegionString(RegionData reg)
  374. {
  375. return String.Format("{0}/{1} at <{2},{3}>",
  376. reg.RegionName, reg.RegionID, reg.coordX, reg.coordY);
  377. }
  378. // String describing name and region location of passed region
  379. private String RegionString(GridRegion reg)
  380. {
  381. return String.Format("{0}/{1} at <{2},{3}>",
  382. reg.RegionName, reg.RegionID, reg.RegionCoordX, reg.RegionCoordY);
  383. }
  384. public bool DeregisterRegion(UUID regionID)
  385. {
  386. RegionData region = m_Database.Get(regionID, UUID.Zero);
  387. if (region == null)
  388. return false;
  389. m_log.DebugFormat(
  390. "[GRID SERVICE]: Deregistering region {0} ({1}) at {2}-{3}",
  391. region.RegionName, region.RegionID, region.coordX, region.coordY);
  392. int flags = Convert.ToInt32(region.Data["flags"]);
  393. if ((!m_DeleteOnUnregister) || ((flags & (int)OpenSim.Framework.RegionFlags.Persistent) != 0))
  394. {
  395. flags &= ~(int)OpenSim.Framework.RegionFlags.RegionOnline;
  396. region.Data["flags"] = flags.ToString();
  397. region.Data["last_seen"] = Util.UnixTimeSinceEpoch();
  398. try
  399. {
  400. m_Database.Store(region);
  401. }
  402. catch (Exception e)
  403. {
  404. m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e);
  405. }
  406. return true;
  407. }
  408. return m_Database.Delete(regionID);
  409. }
  410. public List<GridRegion> GetNeighbours(UUID scopeID, UUID regionID)
  411. {
  412. List<GridRegion> rinfos = new List<GridRegion>();
  413. RegionData region = m_Database.Get(regionID, scopeID);
  414. if (region != null)
  415. {
  416. List<RegionData> rdatas = m_Database.Get(
  417. region.posX - 1, region.posY - 1,
  418. region.posX + region.sizeX + 1, region.posY + region.sizeY + 1, scopeID);
  419. foreach (RegionData rdata in rdatas)
  420. {
  421. if (rdata.RegionID != regionID)
  422. {
  423. int flags = Convert.ToInt32(rdata.Data["flags"]);
  424. if ((flags & (int)Framework.RegionFlags.Hyperlink) == 0) // no hyperlinks as neighbours
  425. rinfos.Add(RegionData2RegionInfo(rdata));
  426. }
  427. }
  428. // string rNames = "";
  429. // foreach (GridRegion gr in rinfos)
  430. // rNames += gr.RegionName + ",";
  431. // m_log.DebugFormat("{0} region {1} has {2} neighbours ({3})",
  432. // LogHeader, region.RegionName, rinfos.Count, rNames);
  433. }
  434. else
  435. {
  436. m_log.WarnFormat(
  437. "[GRID SERVICE]: GetNeighbours() called for scope {0}, region {1} but no such region found",
  438. scopeID, regionID);
  439. }
  440. return rinfos;
  441. }
  442. public GridRegion GetRegionByUUID(UUID scopeID, UUID regionID)
  443. {
  444. RegionData rdata = m_Database.Get(regionID, scopeID);
  445. if (rdata != null)
  446. return RegionData2RegionInfo(rdata);
  447. return null;
  448. }
  449. // Get a region given its base coordinates.
  450. // NOTE: this is NOT 'get a region by some point in the region'. The coordinate MUST
  451. // be the base coordinate of the region.
  452. // The snapping is technically unnecessary but is harmless because regions are always
  453. // multiples of the legacy region size (256).
  454. public GridRegion GetRegionByPosition(UUID scopeID, int x, int y)
  455. {
  456. uint regionX = Util.WorldToRegionLoc((uint)x);
  457. uint regionY = Util.WorldToRegionLoc((uint)y);
  458. int snapX = (int)Util.RegionToWorldLoc(regionX);
  459. int snapY = (int)Util.RegionToWorldLoc(regionY);
  460. RegionData rdata = m_Database.Get(snapX, snapY, scopeID);
  461. if (rdata != null)
  462. {
  463. m_log.DebugFormat("{0} GetRegionByPosition. Found region {1} in database. Pos=<{2},{3}>",
  464. LogHeader, rdata.RegionName, regionX, regionY);
  465. return RegionData2RegionInfo(rdata);
  466. }
  467. else
  468. {
  469. m_log.DebugFormat("{0} GetRegionByPosition. Did not find region in database. Pos=<{1},{2}>",
  470. LogHeader, regionX, regionY);
  471. return null;
  472. }
  473. }
  474. public GridRegion GetRegionByName(UUID scopeID, string name)
  475. {
  476. List<RegionData> rdatas = m_Database.Get(Util.EscapeForLike(name), scopeID);
  477. if ((rdatas != null) && (rdatas.Count > 0))
  478. return RegionData2RegionInfo(rdatas[0]); // get the first
  479. if (m_AllowHypergridMapSearch)
  480. {
  481. GridRegion r = GetHypergridRegionByName(scopeID, name);
  482. if (r != null)
  483. return r;
  484. }
  485. return null;
  486. }
  487. public List<GridRegion> GetRegionsByName(UUID scopeID, string name, int maxNumber)
  488. {
  489. // m_log.DebugFormat("[GRID SERVICE]: GetRegionsByName {0}", name);
  490. List<RegionData> rdatas = m_Database.Get(Util.EscapeForLike(name) + "%", scopeID);
  491. int count = 0;
  492. List<GridRegion> rinfos = new List<GridRegion>();
  493. if (rdatas != null)
  494. {
  495. // m_log.DebugFormat("[GRID SERVICE]: Found {0} regions", rdatas.Count);
  496. foreach (RegionData rdata in rdatas)
  497. {
  498. if (count++ < maxNumber)
  499. rinfos.Add(RegionData2RegionInfo(rdata));
  500. }
  501. }
  502. if (m_AllowHypergridMapSearch && (rdatas == null || (rdatas != null && rdatas.Count == 0)))
  503. {
  504. GridRegion r = GetHypergridRegionByName(scopeID, name);
  505. if (r != null)
  506. rinfos.Add(r);
  507. }
  508. return rinfos;
  509. }
  510. /// <summary>
  511. /// Get a hypergrid region.
  512. /// </summary>
  513. /// <param name="scopeID"></param>
  514. /// <param name="name"></param>
  515. /// <returns>null if no hypergrid region could be found.</returns>
  516. protected GridRegion GetHypergridRegionByName(UUID scopeID, string name)
  517. {
  518. if (name.Contains("."))
  519. return m_HypergridLinker.LinkRegion(scopeID, name);
  520. else
  521. return null;
  522. }
  523. public List<GridRegion> GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax)
  524. {
  525. int xminSnap = (int)(xmin / Constants.RegionSize) * (int)Constants.RegionSize;
  526. int xmaxSnap = (int)(xmax / Constants.RegionSize) * (int)Constants.RegionSize;
  527. int yminSnap = (int)(ymin / Constants.RegionSize) * (int)Constants.RegionSize;
  528. int ymaxSnap = (int)(ymax / Constants.RegionSize) * (int)Constants.RegionSize;
  529. List<RegionData> rdatas = m_Database.Get(xminSnap, yminSnap, xmaxSnap, ymaxSnap, scopeID);
  530. List<GridRegion> rinfos = new List<GridRegion>();
  531. foreach (RegionData rdata in rdatas)
  532. rinfos.Add(RegionData2RegionInfo(rdata));
  533. return rinfos;
  534. }
  535. #endregion
  536. #region Data structure conversions
  537. public RegionData RegionInfo2RegionData(GridRegion rinfo)
  538. {
  539. RegionData rdata = new RegionData();
  540. rdata.posX = (int)rinfo.RegionLocX;
  541. rdata.posY = (int)rinfo.RegionLocY;
  542. rdata.sizeX = rinfo.RegionSizeX;
  543. rdata.sizeY = rinfo.RegionSizeY;
  544. rdata.RegionID = rinfo.RegionID;
  545. rdata.RegionName = rinfo.RegionName;
  546. rdata.Data = rinfo.ToKeyValuePairs();
  547. rdata.Data["regionHandle"] = Utils.UIntsToLong((uint)rdata.posX, (uint)rdata.posY);
  548. rdata.Data["owner_uuid"] = rinfo.EstateOwner.ToString();
  549. return rdata;
  550. }
  551. public GridRegion RegionData2RegionInfo(RegionData rdata)
  552. {
  553. GridRegion rinfo = new GridRegion(rdata.Data);
  554. rinfo.RegionLocX = rdata.posX;
  555. rinfo.RegionLocY = rdata.posY;
  556. rinfo.RegionSizeX = rdata.sizeX;
  557. rinfo.RegionSizeY = rdata.sizeY;
  558. rinfo.RegionID = rdata.RegionID;
  559. rinfo.RegionName = rdata.RegionName;
  560. rinfo.ScopeID = rdata.ScopeID;
  561. return rinfo;
  562. }
  563. #endregion
  564. public List<GridRegion> GetDefaultRegions(UUID scopeID)
  565. {
  566. List<GridRegion> ret = new List<GridRegion>();
  567. List<RegionData> regions = m_Database.GetDefaultRegions(scopeID);
  568. foreach (RegionData r in regions)
  569. {
  570. if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0)
  571. ret.Add(RegionData2RegionInfo(r));
  572. }
  573. m_log.DebugFormat("[GRID SERVICE]: GetDefaultRegions returning {0} regions", ret.Count);
  574. return ret;
  575. }
  576. public List<GridRegion> GetDefaultHypergridRegions(UUID scopeID)
  577. {
  578. List<GridRegion> ret = new List<GridRegion>();
  579. List<RegionData> regions = m_Database.GetDefaultHypergridRegions(scopeID);
  580. foreach (RegionData r in regions)
  581. {
  582. if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0)
  583. ret.Add(RegionData2RegionInfo(r));
  584. }
  585. int hgDefaultRegionsFoundOnline = regions.Count;
  586. // For now, hypergrid default regions will always be given precedence but we will also return simple default
  587. // regions in case no specific hypergrid regions are specified.
  588. ret.AddRange(GetDefaultRegions(scopeID));
  589. int normalDefaultRegionsFoundOnline = ret.Count - hgDefaultRegionsFoundOnline;
  590. m_log.DebugFormat(
  591. "[GRID SERVICE]: GetDefaultHypergridRegions returning {0} hypergrid default and {1} normal default regions",
  592. hgDefaultRegionsFoundOnline, normalDefaultRegionsFoundOnline);
  593. return ret;
  594. }
  595. public List<GridRegion> GetFallbackRegions(UUID scopeID, int x, int y)
  596. {
  597. List<GridRegion> ret = new List<GridRegion>();
  598. List<RegionData> regions = m_Database.GetFallbackRegions(scopeID, x, y);
  599. foreach (RegionData r in regions)
  600. {
  601. if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0)
  602. ret.Add(RegionData2RegionInfo(r));
  603. }
  604. m_log.DebugFormat("[GRID SERVICE]: Fallback returned {0} regions", ret.Count);
  605. return ret;
  606. }
  607. public List<GridRegion> GetHyperlinks(UUID scopeID)
  608. {
  609. List<GridRegion> ret = new List<GridRegion>();
  610. List<RegionData> regions = m_Database.GetHyperlinks(scopeID);
  611. foreach (RegionData r in regions)
  612. {
  613. if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0)
  614. ret.Add(RegionData2RegionInfo(r));
  615. }
  616. m_log.DebugFormat("[GRID SERVICE]: Hyperlinks returned {0} regions", ret.Count);
  617. return ret;
  618. }
  619. public int GetRegionFlags(UUID scopeID, UUID regionID)
  620. {
  621. RegionData region = m_Database.Get(regionID, scopeID);
  622. if (region != null)
  623. {
  624. int flags = Convert.ToInt32(region.Data["flags"]);
  625. //m_log.DebugFormat("[GRID SERVICE]: Request for flags of {0}: {1}", regionID, flags);
  626. return flags;
  627. }
  628. else
  629. return -1;
  630. }
  631. private void HandleDeregisterRegion(string module, string[] cmd)
  632. {
  633. if (cmd.Length < 4)
  634. {
  635. MainConsole.Instance.Output("Usage: degregister region id <region-id>+");
  636. return;
  637. }
  638. for (int i = 3; i < cmd.Length; i++)
  639. {
  640. string rawRegionUuid = cmd[i];
  641. UUID regionUuid;
  642. if (!UUID.TryParse(rawRegionUuid, out regionUuid))
  643. {
  644. MainConsole.Instance.OutputFormat("{0} is not a valid region uuid", rawRegionUuid);
  645. return;
  646. }
  647. GridRegion region = GetRegionByUUID(UUID.Zero, regionUuid);
  648. if (region == null)
  649. {
  650. MainConsole.Instance.OutputFormat("No region with UUID {0}", regionUuid);
  651. return;
  652. }
  653. if (DeregisterRegion(regionUuid))
  654. {
  655. MainConsole.Instance.OutputFormat("Deregistered {0} {1}", region.RegionName, regionUuid);
  656. }
  657. else
  658. {
  659. // I don't think this can ever occur if we know that the region exists.
  660. MainConsole.Instance.OutputFormat("Error deregistering {0} {1}", region.RegionName, regionUuid);
  661. }
  662. }
  663. }
  664. private void HandleShowRegions(string module, string[] cmd)
  665. {
  666. if (cmd.Length != 2)
  667. {
  668. MainConsole.Instance.Output("Syntax: show regions");
  669. return;
  670. }
  671. List<RegionData> regions = m_Database.Get(int.MinValue, int.MinValue, int.MaxValue, int.MaxValue, UUID.Zero);
  672. OutputRegionsToConsoleSummary(regions);
  673. }
  674. private void HandleShowGridSize(string module, string[] cmd)
  675. {
  676. List<RegionData> regions = m_Database.Get(int.MinValue, int.MinValue, int.MaxValue, int.MaxValue, UUID.Zero);
  677. double size = 0;
  678. foreach (RegionData region in regions)
  679. {
  680. int flags = Convert.ToInt32(region.Data["flags"]);
  681. if ((flags & (int)Framework.RegionFlags.Hyperlink) == 0)
  682. size += region.sizeX * region.sizeY;
  683. }
  684. MainConsole.Instance.Output("This is a very rough approximation.");
  685. MainConsole.Instance.Output("Although it will not count regions that are actually links to others over the Hypergrid, ");
  686. MainConsole.Instance.Output("it will count regions that are inactive but were not deregistered from the grid service");
  687. MainConsole.Instance.Output("(e.g. simulator crashed rather than shutting down cleanly).\n");
  688. MainConsole.Instance.OutputFormat("Grid size: {0} km squared.", size / 1000000);
  689. }
  690. private void HandleShowRegion(string module, string[] cmd)
  691. {
  692. if (cmd.Length != 4)
  693. {
  694. MainConsole.Instance.Output("Syntax: show region name <region name>");
  695. return;
  696. }
  697. string regionName = cmd[3];
  698. List<RegionData> regions = m_Database.Get(Util.EscapeForLike(regionName), UUID.Zero);
  699. if (regions == null || regions.Count < 1)
  700. {
  701. MainConsole.Instance.Output("No region with name {0} found", regionName);
  702. return;
  703. }
  704. OutputRegionsToConsole(regions);
  705. }
  706. private void HandleShowRegionAt(string module, string[] cmd)
  707. {
  708. if (cmd.Length != 5)
  709. {
  710. MainConsole.Instance.Output("Syntax: show region at <x-coord> <y-coord>");
  711. return;
  712. }
  713. uint x, y;
  714. if (!uint.TryParse(cmd[3], out x))
  715. {
  716. MainConsole.Instance.Output("x-coord must be an integer");
  717. return;
  718. }
  719. if (!uint.TryParse(cmd[4], out y))
  720. {
  721. MainConsole.Instance.Output("y-coord must be an integer");
  722. return;
  723. }
  724. RegionData region = m_Database.Get((int)Util.RegionToWorldLoc(x), (int)Util.RegionToWorldLoc(y), UUID.Zero);
  725. if (region == null)
  726. {
  727. MainConsole.Instance.OutputFormat("No region found at {0},{1}", x, y);
  728. return;
  729. }
  730. OutputRegionToConsole(region);
  731. }
  732. private void OutputRegionToConsole(RegionData r)
  733. {
  734. OpenSim.Framework.RegionFlags flags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(r.Data["flags"]);
  735. ConsoleDisplayList dispList = new ConsoleDisplayList();
  736. dispList.AddRow("Region Name", r.RegionName);
  737. dispList.AddRow("Region ID", r.RegionID);
  738. dispList.AddRow("Location", string.Format("{0},{1}", r.coordX, r.coordY));
  739. dispList.AddRow("Size", string.Format("{0}x{1}", r.sizeX, r.sizeY));
  740. dispList.AddRow("URI", r.Data["serverURI"]);
  741. dispList.AddRow("Owner ID", r.Data["owner_uuid"]);
  742. dispList.AddRow("Flags", flags);
  743. MainConsole.Instance.Output(dispList.ToString());
  744. }
  745. private void OutputRegionsToConsole(List<RegionData> regions)
  746. {
  747. foreach (RegionData r in regions)
  748. OutputRegionToConsole(r);
  749. }
  750. private void OutputRegionsToConsoleSummary(List<RegionData> regions)
  751. {
  752. ConsoleDisplayTable dispTable = new ConsoleDisplayTable();
  753. dispTable.AddColumn("Name", 44);
  754. dispTable.AddColumn("ID", 36);
  755. dispTable.AddColumn("Position", 11);
  756. dispTable.AddColumn("Size", 11);
  757. dispTable.AddColumn("Flags", 60);
  758. foreach (RegionData r in regions)
  759. {
  760. OpenSim.Framework.RegionFlags flags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(r.Data["flags"]);
  761. dispTable.AddRow(
  762. r.RegionName,
  763. r.RegionID.ToString(),
  764. string.Format("{0},{1}", r.coordX, r.coordY),
  765. string.Format("{0}x{1}", r.sizeX, r.sizeY),
  766. flags.ToString());
  767. }
  768. MainConsole.Instance.Output(dispTable.ToString());
  769. }
  770. private int ParseFlags(int prev, string flags)
  771. {
  772. OpenSim.Framework.RegionFlags f = (OpenSim.Framework.RegionFlags)prev;
  773. string[] parts = flags.Split(new char[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
  774. foreach (string p in parts)
  775. {
  776. int val;
  777. try
  778. {
  779. if (p.StartsWith("+"))
  780. {
  781. val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p.Substring(1));
  782. f |= (OpenSim.Framework.RegionFlags)val;
  783. }
  784. else if (p.StartsWith("-"))
  785. {
  786. val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p.Substring(1));
  787. f &= ~(OpenSim.Framework.RegionFlags)val;
  788. }
  789. else
  790. {
  791. val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p);
  792. f |= (OpenSim.Framework.RegionFlags)val;
  793. }
  794. }
  795. catch (Exception)
  796. {
  797. MainConsole.Instance.Output("Error in flag specification: " + p);
  798. }
  799. }
  800. return (int)f;
  801. }
  802. private void HandleSetFlags(string module, string[] cmd)
  803. {
  804. if (cmd.Length < 5)
  805. {
  806. MainConsole.Instance.Output("Syntax: set region flags <region name> <flags>");
  807. return;
  808. }
  809. List<RegionData> regions = m_Database.Get(Util.EscapeForLike(cmd[3]), UUID.Zero);
  810. if (regions == null || regions.Count < 1)
  811. {
  812. MainConsole.Instance.Output("Region not found");
  813. return;
  814. }
  815. foreach (RegionData r in regions)
  816. {
  817. int flags = Convert.ToInt32(r.Data["flags"]);
  818. flags = ParseFlags(flags, cmd[4]);
  819. r.Data["flags"] = flags.ToString();
  820. OpenSim.Framework.RegionFlags f = (OpenSim.Framework.RegionFlags)flags;
  821. MainConsole.Instance.Output(String.Format("Set region {0} to {1}", r.RegionName, f));
  822. m_Database.Store(r);
  823. }
  824. }
  825. /// <summary>
  826. /// Gets the grid extra service URls we wish for the region to send in OpenSimExtras to dynamically refresh
  827. /// parameters in the viewer used to access services like map, search and destination guides.
  828. /// <para>see "SimulatorFeaturesModule" </para>
  829. /// </summary>
  830. /// <returns>
  831. /// The grid extra service URls.
  832. /// </returns>
  833. public Dictionary<string,object> GetExtraFeatures()
  834. {
  835. return m_ExtraFeatures;
  836. }
  837. }
  838. }