PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenSimSearch/Modules/SearchModule/OpenSearch.cs

https://bitbucket.org/VirtualReality/optional-modules
C# | 671 lines | 514 code | 104 blank | 53 comment | 43 complexity | 1ede1c50a7cb05807584549458fc5418 MD5 | raw file
  1. /*
  2. * Copyright (c) Contributors, http://aurora-sim.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 Aurora-Sim 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;
  29. using System.Collections.Generic;
  30. using System.Globalization;
  31. using System.Net;
  32. using System.Net.Sockets;
  33. using System.Reflection;
  34. using System.Xml;
  35. using OpenMetaverse;
  36. using log4net;
  37. using Nini.Config;
  38. using Nwc.XmlRpc;
  39. using Aurora.Framework;
  40. using OpenSim.Region.Framework.Interfaces;
  41. using OpenSim.Region.Framework.Scenes;
  42. using OpenSim.Services.Interfaces;
  43. namespace OpenSimSearch.Modules.OpenSearch
  44. {
  45. public class OpenSearchModule : ISharedRegionModule
  46. {
  47. //
  48. // Log module
  49. //
  50. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  51. //
  52. // Module vars
  53. //
  54. private IConfigSource m_gConfig;
  55. private List<IScene> m_Scenes = new List<IScene>();
  56. private string m_SearchServer = "";
  57. private bool m_Enabled = true;
  58. public void Initialise(IConfigSource source)
  59. {
  60. m_gConfig = source;
  61. IConfig searchConfig = source.Configs["Search"];
  62. if (searchConfig == null)
  63. {
  64. m_log.Info("[SEARCH] Not configured, disabling");
  65. m_Enabled = false;
  66. return;
  67. }
  68. m_SearchServer = searchConfig.GetString("SearchURL", "");
  69. if (m_SearchServer == "")
  70. {
  71. //m_log.Debug("[SEARCH] No search server, disabling search");
  72. m_Enabled = false;
  73. return;
  74. }
  75. else
  76. {
  77. m_log.Info("[SEARCH] Search module is activated");
  78. m_Enabled = true;
  79. }
  80. }
  81. public void PostInitialise()
  82. {
  83. }
  84. public void AddRegion(IScene scene)
  85. {
  86. if (!m_Enabled)
  87. return;
  88. m_Scenes.Add(scene);
  89. // Hook up events
  90. scene.EventManager.OnNewClient += OnNewClient;
  91. }
  92. public void RemoveRegion(IScene scene)
  93. {
  94. m_Scenes.Remove(scene);
  95. // Hook up events
  96. scene.EventManager.OnNewClient -= OnNewClient;
  97. }
  98. public void RegionLoaded(IScene scene)
  99. {
  100. }
  101. public void Close()
  102. {
  103. }
  104. public Type ReplaceableInterface
  105. {
  106. get { return null; }
  107. }
  108. public string Name
  109. {
  110. get { return "SearchModule"; }
  111. }
  112. /// New Client Event Handler
  113. private void OnNewClient(IClientAPI client)
  114. {
  115. // Subscribe to messages
  116. client.OnDirPlacesQuery += DirPlacesQuery;
  117. client.OnDirFindQuery += DirFindQuery;
  118. client.OnDirPopularQuery += DirPopularQuery;
  119. client.OnDirLandQuery += DirLandQuery;
  120. client.OnDirClassifiedQuery += DirClassifiedQuery;
  121. // Response after Directory Queries
  122. client.OnEventInfoRequest += EventInfoRequest;
  123. client.OnClassifiedInfoRequest += ClassifiedInfoRequest;
  124. client.OnMapItemRequest += HandleMapItemRequest;
  125. }
  126. //
  127. // Make external XMLRPC request
  128. //
  129. private Hashtable GenericXMLRPCRequest(Hashtable ReqParams, string method)
  130. {
  131. ArrayList SendParams = new ArrayList();
  132. SendParams.Add(ReqParams);
  133. // Send Request
  134. XmlRpcResponse Resp;
  135. try
  136. {
  137. XmlRpcRequest Req = new XmlRpcRequest(method, SendParams);
  138. Resp = Req.Send(m_SearchServer, 30000);
  139. }
  140. catch (WebException ex)
  141. {
  142. m_log.ErrorFormat("[SEARCH]: Unable to connect to Search " +
  143. "Server {0}. Exception {1}", m_SearchServer, ex);
  144. Hashtable ErrorHash = new Hashtable();
  145. ErrorHash["success"] = false;
  146. ErrorHash["errorMessage"] = "Unable to search at this time. ";
  147. ErrorHash["errorURI"] = "";
  148. return ErrorHash;
  149. }
  150. catch (SocketException ex)
  151. {
  152. m_log.ErrorFormat(
  153. "[SEARCH]: Unable to connect to Search Server {0}. " +
  154. "Exception {1}", m_SearchServer, ex);
  155. Hashtable ErrorHash = new Hashtable();
  156. ErrorHash["success"] = false;
  157. ErrorHash["errorMessage"] = "Unable to search at this time. ";
  158. ErrorHash["errorURI"] = "";
  159. return ErrorHash;
  160. }
  161. catch (XmlException ex)
  162. {
  163. m_log.ErrorFormat(
  164. "[SEARCH]: Unable to connect to Search Server {0}. " +
  165. "Exception {1}", m_SearchServer, ex);
  166. Hashtable ErrorHash = new Hashtable();
  167. ErrorHash["success"] = false;
  168. ErrorHash["errorMessage"] = "Unable to search at this time. ";
  169. ErrorHash["errorURI"] = "";
  170. return ErrorHash;
  171. }
  172. if (Resp.IsFault)
  173. {
  174. Hashtable ErrorHash = new Hashtable();
  175. ErrorHash["success"] = false;
  176. ErrorHash["errorMessage"] = "Unable to search at this time. ";
  177. ErrorHash["errorURI"] = "";
  178. return ErrorHash;
  179. }
  180. Hashtable RespData = (Hashtable)Resp.Value;
  181. return RespData;
  182. }
  183. protected void DirPlacesQuery(IClientAPI remoteClient, UUID queryID,
  184. string queryText, int queryFlags, int category, string simName,
  185. int queryStart)
  186. {
  187. Hashtable ReqHash = new Hashtable();
  188. ReqHash["text"] = queryText;
  189. ReqHash["flags"] = queryFlags.ToString();
  190. ReqHash["category"] = category.ToString();
  191. ReqHash["sim_name"] = simName;
  192. ReqHash["query_start"] = queryStart.ToString();
  193. Hashtable result = GenericXMLRPCRequest(ReqHash,
  194. "dir_places_query");
  195. if (!Convert.ToBoolean(result["success"]))
  196. {
  197. remoteClient.SendAgentAlertMessage(
  198. result["errorMessage"].ToString(), false);
  199. return;
  200. }
  201. ArrayList dataArray = (ArrayList)result["data"];
  202. int count = dataArray.Count;
  203. if (count > 100)
  204. count = 101;
  205. DirPlacesReplyData[] data = new DirPlacesReplyData[count];
  206. int i = 0;
  207. foreach (Object o in dataArray)
  208. {
  209. Hashtable d = (Hashtable)o;
  210. data[i] = new DirPlacesReplyData();
  211. data[i].parcelID = new UUID(d["parcel_id"].ToString());
  212. data[i].name = d["name"].ToString();
  213. data[i].forSale = Convert.ToBoolean(d["for_sale"]);
  214. data[i].auction = Convert.ToBoolean(d["auction"]);
  215. data[i].dwell = Convert.ToSingle(d["dwell"]);
  216. i++;
  217. if (i >= count)
  218. break;
  219. }
  220. remoteClient.SendDirPlacesReply(queryID, data);
  221. }
  222. public void DirPopularQuery(IClientAPI remoteClient, UUID queryID, uint queryFlags)
  223. {
  224. Hashtable ReqHash = new Hashtable();
  225. ReqHash["flags"] = queryFlags.ToString();
  226. Hashtable result = GenericXMLRPCRequest(ReqHash,
  227. "dir_popular_query");
  228. if (!Convert.ToBoolean(result["success"]))
  229. {
  230. remoteClient.SendAgentAlertMessage(
  231. result["errorMessage"].ToString(), false);
  232. return;
  233. }
  234. ArrayList dataArray = (ArrayList)result["data"];
  235. int count = dataArray.Count;
  236. if (count > 100)
  237. count = 101;
  238. DirPopularReplyData[] data = new DirPopularReplyData[count];
  239. int i = 0;
  240. foreach (Object o in dataArray)
  241. {
  242. Hashtable d = (Hashtable)o;
  243. data[i] = new DirPopularReplyData();
  244. data[i].ParcelID = new UUID(d["parcel_id"].ToString());
  245. data[i].Name = d["name"].ToString();
  246. data[i].Dwell = Convert.ToSingle(d["dwell"]);
  247. i++;
  248. if (i >= count)
  249. break;
  250. }
  251. remoteClient.SendDirPopularReply(queryID, data);
  252. }
  253. public void DirLandQuery(IClientAPI remoteClient, UUID queryID, uint queryFlags, uint searchType, uint price, uint area, int queryStart)
  254. {
  255. Hashtable ReqHash = new Hashtable();
  256. ReqHash["flags"] = queryFlags.ToString();
  257. ReqHash["type"] = searchType.ToString();
  258. ReqHash["price"] = price.ToString();
  259. ReqHash["area"] = area.ToString();
  260. ReqHash["query_start"] = queryStart.ToString();
  261. Hashtable result = GenericXMLRPCRequest(ReqHash,
  262. "dir_land_query");
  263. if (!Convert.ToBoolean(result["success"]))
  264. {
  265. remoteClient.SendAgentAlertMessage(
  266. result["errorMessage"].ToString(), false);
  267. return;
  268. }
  269. ArrayList dataArray = (ArrayList)result["data"];
  270. int count = dataArray.Count;
  271. if (count > 100)
  272. count = 101;
  273. DirLandReplyData[] data = new DirLandReplyData[count];
  274. int i = 0;
  275. foreach (Object o in dataArray)
  276. {
  277. Hashtable d = (Hashtable)o;
  278. if (d["name"] == null)
  279. continue;
  280. data[i] = new DirLandReplyData();
  281. data[i].parcelID = new UUID(d["parcel_id"].ToString());
  282. data[i].name = d["name"].ToString();
  283. data[i].auction = Convert.ToBoolean(d["auction"]);
  284. data[i].forSale = Convert.ToBoolean(d["for_sale"]);
  285. data[i].salePrice = Convert.ToInt32(d["sale_price"]);
  286. data[i].actualArea = Convert.ToInt32(d["area"]);
  287. i++;
  288. if (i >= count)
  289. break;
  290. }
  291. remoteClient.SendDirLandReply(queryID, data);
  292. }
  293. public void DirFindQuery(IClientAPI remoteClient, UUID queryID,
  294. string queryText, uint queryFlags, int queryStart)
  295. {
  296. if ((queryFlags & 1) != 0) //People (1 << 0)
  297. {
  298. DirPeopleQuery(remoteClient, queryID, queryText, queryFlags,
  299. queryStart);
  300. return;
  301. }
  302. else if ((queryFlags & 32) != 0) //DateEvents (1 << 5)
  303. {
  304. DirEventsQuery(remoteClient, queryID, queryText, queryFlags,
  305. queryStart);
  306. return;
  307. }
  308. }
  309. public void DirPeopleQuery(IClientAPI remoteClient, UUID queryID,
  310. string queryText, uint queryFlags, int queryStart)
  311. {
  312. List<UserAccount> accounts = m_Scenes[0].UserAccountService.GetUserAccounts(null, queryText);
  313. DirPeopleReplyData[] data =
  314. new DirPeopleReplyData[accounts.Count];
  315. int i = 0;
  316. foreach (UserAccount item in accounts)
  317. {
  318. data[i] = new DirPeopleReplyData();
  319. data[i].agentID = item.PrincipalID;
  320. data[i].firstName = item.FirstName;
  321. data[i].lastName = item.LastName;
  322. data[i].group = "";
  323. data[i].online = false;
  324. data[i].reputation = 0;
  325. i++;
  326. }
  327. remoteClient.SendDirPeopleReply(queryID, data);
  328. }
  329. public void DirEventsQuery(IClientAPI remoteClient, UUID queryID,
  330. string queryText, uint queryFlags, int queryStart)
  331. {
  332. Hashtable ReqHash = new Hashtable();
  333. ReqHash["text"] = queryText;
  334. ReqHash["flags"] = queryFlags.ToString();
  335. ReqHash["query_start"] = queryStart.ToString();
  336. Hashtable result = GenericXMLRPCRequest(ReqHash,
  337. "dir_events_query");
  338. if (!Convert.ToBoolean(result["success"]))
  339. {
  340. remoteClient.SendAgentAlertMessage(
  341. result["errorMessage"].ToString(), false);
  342. return;
  343. }
  344. ArrayList dataArray = (ArrayList)result["data"];
  345. int count = dataArray.Count;
  346. if (count > 100)
  347. count = 101;
  348. DirEventsReplyData[] data = new DirEventsReplyData[count];
  349. int i = 0;
  350. foreach (Object o in dataArray)
  351. {
  352. Hashtable d = (Hashtable)o;
  353. data[i] = new DirEventsReplyData();
  354. data[i].ownerID = new UUID(d["owner_id"].ToString());
  355. data[i].name = d["name"].ToString();
  356. data[i].eventID = Convert.ToUInt32(d["event_id"]);
  357. data[i].date = d["date"].ToString();
  358. data[i].unixTime = Convert.ToUInt32(d["unix_time"]);
  359. data[i].eventFlags = Convert.ToUInt32(d["event_flags"]);
  360. i++;
  361. if (i >= count)
  362. break;
  363. }
  364. remoteClient.SendDirEventsReply(queryID, data);
  365. }
  366. public void DirClassifiedQuery(IClientAPI remoteClient, UUID queryID,
  367. string queryText, uint queryFlags, uint category,
  368. int queryStart)
  369. {
  370. Hashtable ReqHash = new Hashtable();
  371. ReqHash["text"] = queryText;
  372. ReqHash["flags"] = queryFlags.ToString();
  373. ReqHash["category"] = category.ToString();
  374. ReqHash["query_start"] = queryStart.ToString();
  375. Hashtable result = GenericXMLRPCRequest(ReqHash,
  376. "dir_classified_query");
  377. if (!Convert.ToBoolean(result["success"]))
  378. {
  379. remoteClient.SendAgentAlertMessage(
  380. result["errorMessage"].ToString(), false);
  381. return;
  382. }
  383. ArrayList dataArray = (ArrayList)result["data"];
  384. int count = dataArray.Count;
  385. if (count > 100)
  386. count = 101;
  387. DirClassifiedReplyData[] data = new DirClassifiedReplyData[count];
  388. int i = 0;
  389. foreach (Object o in dataArray)
  390. {
  391. Hashtable d = (Hashtable)o;
  392. data[i] = new DirClassifiedReplyData();
  393. data[i].classifiedID = new UUID(d["classifiedid"].ToString());
  394. data[i].name = d["name"].ToString();
  395. data[i].classifiedFlags = Convert.ToByte(d["classifiedflags"]);
  396. data[i].creationDate = Convert.ToUInt32(d["creation_date"]);
  397. data[i].expirationDate = Convert.ToUInt32(d["expiration_date"]);
  398. data[i].price = Convert.ToInt32(d["priceforlisting"]);
  399. i++;
  400. if (i >= count)
  401. break;
  402. }
  403. remoteClient.SendDirClassifiedReply(queryID, data);
  404. }
  405. public void EventInfoRequest(IClientAPI remoteClient, uint queryEventID)
  406. {
  407. Hashtable ReqHash = new Hashtable();
  408. ReqHash["eventID"] = queryEventID.ToString();
  409. Hashtable result = GenericXMLRPCRequest(ReqHash,
  410. "event_info_query");
  411. if (!Convert.ToBoolean(result["success"]))
  412. {
  413. remoteClient.SendAgentAlertMessage(
  414. result["errorMessage"].ToString(), false);
  415. return;
  416. }
  417. ArrayList dataArray = (ArrayList)result["data"];
  418. if (dataArray.Count == 0)
  419. {
  420. // something bad happened here, if we could return an
  421. // event after the search,
  422. // we should be able to find it here
  423. // TODO do some (more) sensible error-handling here
  424. remoteClient.SendAgentAlertMessage("Couldn't find this event.",
  425. false);
  426. return;
  427. }
  428. Hashtable d = (Hashtable)dataArray[0];
  429. EventData data = new EventData();
  430. data.eventID = Convert.ToUInt32(d["event_id"]);
  431. data.creator = d["creator"].ToString();
  432. data.name = d["name"].ToString();
  433. data.category = d["category"].ToString();
  434. data.description = d["description"].ToString();
  435. data.date = d["date"].ToString();
  436. data.dateUTC = Convert.ToUInt32(d["dateUTC"]);
  437. data.duration = Convert.ToUInt32(d["duration"]);
  438. data.cover = Convert.ToUInt32(d["covercharge"]);
  439. data.amount = Convert.ToUInt32(d["coveramount"]);
  440. data.simName = d["simname"].ToString();
  441. Vector3.TryParse(d["globalposition"].ToString(), out data.globalPos);
  442. data.eventFlags = Convert.ToUInt32(d["eventflags"]);
  443. remoteClient.SendEventInfoReply(data);
  444. }
  445. public void ClassifiedInfoRequest(UUID queryClassifiedID, IClientAPI remoteClient)
  446. {
  447. Hashtable ReqHash = new Hashtable();
  448. ReqHash["classifiedID"] = queryClassifiedID.ToString();
  449. Hashtable result = GenericXMLRPCRequest(ReqHash,
  450. "classifieds_info_query");
  451. if (!Convert.ToBoolean(result["success"]))
  452. {
  453. remoteClient.SendAgentAlertMessage(
  454. result["errorMessage"].ToString(), false);
  455. return;
  456. }
  457. ArrayList dataArray = (ArrayList)result["data"];
  458. if (dataArray.Count == 0)
  459. {
  460. // something bad happened here, if we could return an
  461. // event after the search,
  462. // we should be able to find it here
  463. // TODO do some (more) sensible error-handling here
  464. remoteClient.SendAgentAlertMessage("Couldn't find this classifieds.",
  465. false);
  466. return;
  467. }
  468. Hashtable d = (Hashtable)dataArray[0];
  469. Vector3 globalPos = new Vector3();
  470. Vector3.TryParse(d["posglobal"].ToString(), out globalPos);
  471. remoteClient.SendClassifiedInfoReply(
  472. new UUID(d["classifieduuid"].ToString()),
  473. new UUID(d["creatoruuid"].ToString()),
  474. Convert.ToUInt32(d["creationdate"]),
  475. Convert.ToUInt32(d["expirationdate"]),
  476. Convert.ToUInt32(d["category"]),
  477. d["name"].ToString(),
  478. d["description"].ToString(),
  479. new UUID(d["parceluuid"].ToString()),
  480. Convert.ToUInt32(d["parentestate"]),
  481. new UUID(d["snapshotuuid"].ToString()),
  482. d["simname"].ToString(),
  483. globalPos,
  484. d["parcelname"].ToString(),
  485. Convert.ToByte(d["classifiedflags"]),
  486. Convert.ToInt32(d["priceforlisting"]));
  487. }
  488. public void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
  489. uint EstateID, bool godlike, uint itemtype, ulong regionhandle)
  490. {
  491. //The following constant appears to be from GridLayerType enum
  492. //defined in OpenMetaverse/GridManager.cs of libopenmetaverse.
  493. if (itemtype == 7) //(land sales)
  494. {
  495. int tc = Environment.TickCount;
  496. Hashtable ReqHash = new Hashtable();
  497. //The flags are: SortAsc (1 << 15), PerMeterSort (1 << 17)
  498. ReqHash["flags"] = "163840";
  499. ReqHash["type"] = "4294967295"; //This is -1 in 32 bits
  500. ReqHash["price"] = "0";
  501. ReqHash["area"] = "0";
  502. ReqHash["query_start"] = "0";
  503. Hashtable result = GenericXMLRPCRequest(ReqHash,
  504. "dir_land_query");
  505. if (!Convert.ToBoolean(result["success"]))
  506. {
  507. remoteClient.SendAgentAlertMessage(
  508. result["errorMessage"].ToString(), false);
  509. return;
  510. }
  511. ArrayList dataArray = (ArrayList)result["data"];
  512. int count = dataArray.Count;
  513. if (count > 100)
  514. count = 101;
  515. DirLandReplyData[] Landdata = new DirLandReplyData[count];
  516. int i = 0;
  517. string[] ParcelLandingPoint = new string[count];
  518. string[] ParcelRegionUUID = new string[count];
  519. foreach (Object o in dataArray)
  520. {
  521. Hashtable d = (Hashtable)o;
  522. if (d["name"] == null)
  523. continue;
  524. Landdata[i] = new DirLandReplyData();
  525. Landdata[i].parcelID = new UUID(d["parcel_id"].ToString());
  526. Landdata[i].name = d["name"].ToString();
  527. Landdata[i].auction = Convert.ToBoolean(d["auction"]);
  528. Landdata[i].forSale = Convert.ToBoolean(d["for_sale"]);
  529. Landdata[i].salePrice = Convert.ToInt32(d["sale_price"]);
  530. Landdata[i].actualArea = Convert.ToInt32(d["area"]);
  531. ParcelLandingPoint[i] = d["landing_point"].ToString();
  532. ParcelRegionUUID[i] = d["region_UUID"].ToString();
  533. i++;
  534. if (i >= count)
  535. break;
  536. }
  537. i = 0;
  538. int locX = 0;
  539. int locY = 0;
  540. List<mapItemReply> mapitems = new List<mapItemReply>();
  541. foreach (DirLandReplyData landDir in Landdata)
  542. {
  543. foreach(IScene scene in m_Scenes)
  544. {
  545. if(scene.RegionInfo.RegionID.ToString() == ParcelRegionUUID[i])
  546. {
  547. locX = scene.RegionInfo.RegionLocX;
  548. locY = scene.RegionInfo.RegionLocY;
  549. }
  550. }
  551. string[] landingpoint = ParcelLandingPoint[i].Split('/');
  552. mapItemReply mapitem = new mapItemReply();
  553. mapitem.x = (uint)((locX * 256 ) + Convert.ToDecimal(landingpoint[0]));
  554. mapitem.y = (uint)((locY * 256 ) + Convert.ToDecimal(landingpoint[1]));
  555. mapitem.id = landDir.parcelID;
  556. mapitem.name = landDir.name;
  557. mapitem.Extra = landDir.actualArea;
  558. mapitem.Extra2 = landDir.salePrice;
  559. mapitems.Add(mapitem);
  560. i++;
  561. }
  562. remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
  563. mapitems.Clear();
  564. }
  565. }
  566. }
  567. }