aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Data/MySQL/MySQLRegionData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Data/MySQL/MySQLRegionData.cs')
-rw-r--r--OpenSim/Data/MySQL/MySQLRegionData.cs2360
1 files changed, 2360 insertions, 0 deletions
diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs
new file mode 100644
index 0000000..70b6d3c
--- /dev/null
+++ b/OpenSim/Data/MySQL/MySQLRegionData.cs
@@ -0,0 +1,2360 @@
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
28using System;
29using System.Collections.Generic;
30using System.Data;
31using System.IO;
32using System.Reflection;
33using System.Threading;
34using libsecondlife;
35using log4net;
36using MySql.Data.MySqlClient;
37using OpenSim.Framework;
38using OpenSim.Region.Environment.Interfaces;
39using OpenSim.Region.Environment.Scenes;
40
41namespace OpenSim.Data.MySQL
42{
43 /// <summary>
44 /// A MySQL Interface for the Region Server
45 /// </summary>
46 public class MySQLDataStore : IRegionDataStore
47 {
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49
50 private const string m_primSelect = "select * from prims";
51 private const string m_shapeSelect = "select * from primshapes";
52 private const string m_itemsSelect = "select * from primitems";
53 private const string m_terrainSelect = "select * from terrain limit 1";
54 private const string m_landSelect = "select * from land";
55 private const string m_landAccessListSelect = "select * from landaccesslist";
56 private const string m_regionSettingsSelect = "select * from regionsettings";
57 private const string m_waitTimeoutSelect = "select @@wait_timeout";
58
59 private MySqlConnection m_connection;
60 private string m_connectionString;
61
62 /// <summary>
63 /// Wait timeout for our connection in ticks.
64 /// </summary>
65 private long m_waitTimeout;
66
67 /// <summary>
68 /// Make our storage of the timeout this amount smaller than it actually is, to give us a margin on long
69 /// running database operations.
70 /// </summary>
71 private long m_waitTimeoutLeeway = 60 * TimeSpan.TicksPerSecond;
72
73 /// <summary>
74 /// Holds the last tick time that the connection was used.
75 /// </summary>
76 private long m_lastConnectionUse;
77
78 private DataSet m_dataSet;
79 private MySqlDataAdapter m_primDataAdapter;
80 private MySqlDataAdapter m_shapeDataAdapter;
81 private MySqlDataAdapter m_itemsDataAdapter;
82 private MySqlDataAdapter m_terrainDataAdapter;
83 private MySqlDataAdapter m_landDataAdapter;
84 private MySqlDataAdapter m_landAccessListDataAdapter;
85 private MySqlDataAdapter m_regionSettingsDataAdapter;
86
87 private DataTable m_primTable;
88 private DataTable m_shapeTable;
89 private DataTable m_itemsTable;
90 private DataTable m_terrainTable;
91 private DataTable m_landTable;
92 private DataTable m_landAccessListTable;
93 private DataTable m_regionSettingsTable;
94
95 /***********************************************************************
96 *
97 * Public Interface Functions
98 *
99 **********************************************************************/
100
101 /// <summary>
102 /// see IRegionDataStore
103 /// </summary>
104 /// <param name="connectionstring"></param>
105 public void Initialise(string connectionString)
106 {
107 m_connectionString = connectionString;
108
109 m_dataSet = new DataSet();
110
111 int passPosition = 0;
112 int passEndPosition = 0;
113 string displayConnectionString = null;
114
115 try
116 { // hide the password in the connection string
117 passPosition = m_connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase);
118 passPosition = m_connectionString.IndexOf("=", passPosition);
119 if (passPosition < m_connectionString.Length)
120 passPosition += 1;
121 passEndPosition = m_connectionString.IndexOf(";", passPosition);
122
123 displayConnectionString = m_connectionString.Substring(0, passPosition);
124 displayConnectionString += "***";
125 displayConnectionString += m_connectionString.Substring(passEndPosition, m_connectionString.Length - passEndPosition);
126 }
127 catch (Exception e )
128 {
129 m_log.Debug("Exception: password not found in connection string\n" + e.ToString());
130 }
131
132 m_log.Info("[REGION DB]: MySql - connecting: " + displayConnectionString);
133 m_connection = new MySqlConnection(m_connectionString);
134 m_connection.Open();
135
136 GetWaitTimeout();
137
138 // This actually does the roll forward assembly stuff
139 Assembly assem = GetType().Assembly;
140 Migration m = new Migration(m_connection, assem, "RegionStore");
141
142 // TODO: After rev 6000, remove this. People should have
143 // been rolled onto the new migration code by then.
144 TestTables(m_connection, m);
145
146 m.Update();
147
148 MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, m_connection);
149 m_primDataAdapter = new MySqlDataAdapter(primSelectCmd);
150
151 MySqlCommand shapeSelectCmd = new MySqlCommand(m_shapeSelect, m_connection);
152 m_shapeDataAdapter = new MySqlDataAdapter(shapeSelectCmd);
153
154 MySqlCommand itemsSelectCmd = new MySqlCommand(m_itemsSelect, m_connection);
155 m_itemsDataAdapter = new MySqlDataAdapter(itemsSelectCmd);
156
157 MySqlCommand terrainSelectCmd = new MySqlCommand(m_terrainSelect, m_connection);
158 m_terrainDataAdapter = new MySqlDataAdapter(terrainSelectCmd);
159
160 MySqlCommand landSelectCmd = new MySqlCommand(m_landSelect, m_connection);
161 m_landDataAdapter = new MySqlDataAdapter(landSelectCmd);
162
163 MySqlCommand landAccessListSelectCmd = new MySqlCommand(m_landAccessListSelect, m_connection);
164 m_landAccessListDataAdapter = new MySqlDataAdapter(landAccessListSelectCmd);
165
166 MySqlCommand regionSettingsSelectCmd = new MySqlCommand(m_regionSettingsSelect, m_connection);
167 m_regionSettingsDataAdapter = new MySqlDataAdapter(regionSettingsSelectCmd);
168
169 lock (m_dataSet)
170 {
171 m_primTable = createPrimTable();
172 m_dataSet.Tables.Add(m_primTable);
173 SetupPrimCommands(m_primDataAdapter, m_connection);
174 m_primDataAdapter.Fill(m_primTable);
175
176 m_shapeTable = createShapeTable();
177 m_dataSet.Tables.Add(m_shapeTable);
178 SetupShapeCommands(m_shapeDataAdapter, m_connection);
179 m_shapeDataAdapter.Fill(m_shapeTable);
180
181
182 m_itemsTable = createItemsTable();
183 m_dataSet.Tables.Add(m_itemsTable);
184 SetupItemsCommands(m_itemsDataAdapter, m_connection);
185 m_itemsDataAdapter.Fill(m_itemsTable);
186
187 m_terrainTable = createTerrainTable();
188 m_dataSet.Tables.Add(m_terrainTable);
189 SetupTerrainCommands(m_terrainDataAdapter, m_connection);
190 m_terrainDataAdapter.Fill(m_terrainTable);
191
192 m_landTable = createLandTable();
193 m_dataSet.Tables.Add(m_landTable);
194 setupLandCommands(m_landDataAdapter, m_connection);
195 m_landDataAdapter.Fill(m_landTable);
196
197 m_landAccessListTable = createLandAccessListTable();
198 m_dataSet.Tables.Add(m_landAccessListTable);
199 setupLandAccessCommands(m_landAccessListDataAdapter, m_connection);
200 m_landAccessListDataAdapter.Fill(m_landAccessListTable);
201
202 m_regionSettingsTable = createRegionSettingsTable();
203 m_dataSet.Tables.Add(m_regionSettingsTable);
204 SetupRegionSettingsCommands(m_regionSettingsDataAdapter, m_connection);
205 m_regionSettingsDataAdapter.Fill(m_regionSettingsTable);
206 }
207 }
208
209 /// <summary>
210 /// Get the wait_timeout value for our connection
211 /// </summary>
212 protected void GetWaitTimeout()
213 {
214 MySqlCommand cmd = new MySqlCommand(m_waitTimeoutSelect, m_connection);
215
216 using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
217 {
218 if (dbReader.Read())
219 {
220 m_waitTimeout
221 = Convert.ToInt32(dbReader["@@wait_timeout"]) * TimeSpan.TicksPerSecond + m_waitTimeoutLeeway;
222 }
223
224 dbReader.Close();
225 cmd.Dispose();
226 }
227
228 m_lastConnectionUse = System.DateTime.Now.Ticks;
229
230 m_log.DebugFormat(
231 "[REGION DB]: Connection wait timeout {0} seconds", m_waitTimeout / TimeSpan.TicksPerSecond);
232 }
233
234 /// <summary>
235 /// Should be called before any db operation. This checks to see if the connection has not timed out
236 /// </summary>
237 protected void CheckConnection()
238 {
239 //m_log.Debug("[REGION DB]: Checking connection");
240
241 long timeNow = System.DateTime.Now.Ticks;
242 if (timeNow - m_lastConnectionUse > m_waitTimeout || m_connection.State != ConnectionState.Open)
243 {
244 m_log.DebugFormat("[REGION DB]: Database connection has gone away - reconnecting");
245
246 lock (m_connection)
247 {
248 m_connection.Close();
249 m_connection = new MySqlConnection(m_connectionString);
250 m_connection.Open();
251 }
252 }
253
254 // Strictly, we should set this after the actual db operation. But it's more convenient to set here rather
255 // than require the code to call another method - the timeout leeway should be large enough to cover the
256 // inaccuracy.
257 m_lastConnectionUse = timeNow;
258 }
259
260 /// <summary>
261 /// Given a list of tables, return the version of the tables, as seen in the database
262 /// </summary>
263 /// <param name="tableList">The list of table</param>
264 /// <param name="dbcon">The database connection handler</param>
265 public void GetTableVersion(Dictionary<string, string> tableList, MySqlConnection dbcon)
266 {
267 lock (dbcon)
268 {
269 MySqlCommand tablesCmd =
270 new MySqlCommand(
271 "SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=?dbname",
272 dbcon);
273 tablesCmd.Parameters.AddWithValue("?dbname", dbcon.Database);
274
275 CheckConnection();
276 using (MySqlDataReader tables = tablesCmd.ExecuteReader())
277 {
278 while (tables.Read())
279 {
280 try
281 {
282 string tableName = (string)tables["TABLE_NAME"];
283 string comment = (string)tables["TABLE_COMMENT"];
284 if (tableList.ContainsKey(tableName))
285 {
286 tableList[tableName] = comment;
287 }
288 }
289 catch (Exception e)
290 {
291 m_log.Error(e.ToString());
292 }
293 }
294 tables.Close();
295 }
296 }
297 }
298 // private void TestTablesVersionable(MySqlConnection dbconn)
299 // {
300 // Dictionary<string, string> tableList = new Dictionary<string, string>();
301
302 // tableList["land"] = null;
303 // dbconn.Open();
304 // GetTableVersion(tableList,dbconn);
305
306 // UpgradeLandTable(tableList["land"], dbconn);
307 // //database.Close();
308
309 // }
310
311 /// <summary>
312 /// Execute a SQL statement stored in a resource, as a string
313 /// </summary>
314 /// <param name="name">the ressource name</param>
315 /// <param name="dbcon">The database connection handler</param>
316 public void ExecuteResourceSql(string name, MySqlConnection dbcon)
317 {
318 MySqlCommand cmd = new MySqlCommand(getResourceString(name), dbcon);
319 cmd.ExecuteNonQuery();
320 }
321
322 /// <summary>
323 /// Extract a named string resource from the embedded resources
324 /// </summary>
325 /// <param name="name">name of embedded resource</param>
326 /// <returns>string contained within the embedded resource</returns>
327 private string getResourceString(string name)
328 {
329 Assembly assem = GetType().Assembly;
330 string[] names = assem.GetManifestResourceNames();
331
332 foreach (string s in names)
333 {
334 if (s.EndsWith(name))
335 {
336 using (Stream resource = assem.GetManifestResourceStream(s))
337 {
338 using (StreamReader resourceReader = new StreamReader(resource))
339 {
340 string resourceString = resourceReader.ReadToEnd();
341 return resourceString;
342 }
343 }
344 }
345 }
346 throw new Exception(string.Format("Resource '{0}' was not found", name));
347 }
348
349 /// <summary>
350 /// <list type="bullet">
351 /// <item>Execute CreateLandTable.sql if oldVersion == null</item>
352 /// <item>Execute UpgradeLandTable.sqm if oldVersion contain "Rev."</item>
353 /// </list>
354 /// </summary>
355 /// <param name="oldVersion"></param>
356 /// <param name="dbconn">The database connection handler</param>
357 // private void UpgradeLandTable(string oldVersion, MySqlConnection dbconn)
358 // {
359 // // null as the version, indicates that the table didn't exist
360 // if (oldVersion == null)
361 // {
362 // ExecuteResourceSql("CreateLandTable.sql",dbconn);
363 // oldVersion = "Rev. 2; InnoDB free: 0 kB";
364 // }
365 // if (!oldVersion.Contains("Rev."))
366 // {
367 // ExecuteResourceSql("UpgradeLandTableToVersion2.sql", dbconn);
368 // }
369 // }
370
371 /// <summary>
372 /// Adds an object into region storage
373 /// </summary>
374 /// <param name="obj">The object</param>
375 /// <param name="regionUUID">The region UUID</param>
376 public void StoreObject(SceneObjectGroup obj, LLUUID regionUUID)
377 {
378 lock (m_dataSet)
379 {
380 foreach (SceneObjectPart prim in obj.Children.Values)
381 {
382 if ((prim.GetEffectiveObjectFlags() & (uint)LLObject.ObjectFlags.Physics) == 0
383 && (prim.GetEffectiveObjectFlags() & (uint)LLObject.ObjectFlags.Temporary) == 0
384 && (prim.GetEffectiveObjectFlags() & (uint)LLObject.ObjectFlags.TemporaryOnRez) == 0)
385 {
386 //m_log.Info("[REGION DB]: Adding obj: " + obj.UUID + " to region: " + regionUUID);
387 addPrim(prim, obj.UUID, regionUUID);
388 }
389 else
390 {
391 // m_log.Info("[DATASTORE]: Ignoring Physical obj: " + obj.UUID + " in region: " + regionUUID);
392 }
393 }
394 Commit();
395 }
396 }
397
398 /// <summary>
399 /// removes an object from region storage
400 /// </summary>
401 /// <param name="obj">The object</param>
402 /// <param name="regionUUID">The Region UUID</param>
403 public void RemoveObject(LLUUID obj, LLUUID regionUUID)
404 {
405 m_log.InfoFormat("[REGION DB]: Removing obj: {0} from region: {1}", obj.UUID, regionUUID);
406
407 DataTable prims = m_primTable;
408 DataTable shapes = m_shapeTable;
409
410 string selectExp = "SceneGroupID = '" + Util.ToRawUuidString(obj) + "'";
411 lock (m_dataSet)
412 {
413 DataRow[] primRows = prims.Select(selectExp);
414 foreach (DataRow row in primRows)
415 {
416 // Remove shapes row
417 LLUUID uuid = new LLUUID((string) row["UUID"]);
418 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(uuid));
419 if (shapeRow != null)
420 {
421 shapeRow.Delete();
422 }
423
424 RemoveItems(uuid);
425
426 // Remove prim row
427 row.Delete();
428 }
429 Commit();
430 }
431 }
432
433 /// <summary>
434 /// Remove all persisted items of the given prim.
435 /// The caller must acquire the necessrary synchronization locks and commit or rollback changes.
436 /// </summary>
437 /// <param name="uuid">the Item UUID</param>
438 private void RemoveItems(LLUUID uuid)
439 {
440 String sql = String.Format("primID = '{0}'", uuid);
441 DataRow[] itemRows = m_itemsTable.Select(sql);
442
443 foreach (DataRow itemRow in itemRows)
444 {
445 itemRow.Delete();
446 }
447 }
448
449 /// <summary>
450 /// Load persisted objects from region storage.
451 /// </summary>
452 /// <param name="regionUUID">the Region UUID</param>
453 /// <returns>List of loaded groups</returns>
454 public List<SceneObjectGroup> LoadObjects(LLUUID regionUUID)
455 {
456 Dictionary<LLUUID, SceneObjectGroup> createdObjects = new Dictionary<LLUUID, SceneObjectGroup>();
457
458 List<SceneObjectGroup> retvals = new List<SceneObjectGroup>();
459
460 DataTable prims = m_primTable;
461 DataTable shapes = m_shapeTable;
462
463 string byRegion = "RegionUUID = '" + Util.ToRawUuidString(regionUUID) + "'";
464 string orderByParent = "ParentID ASC";
465
466 lock (m_dataSet)
467 {
468 CheckConnection();
469 DataRow[] primsForRegion = prims.Select(byRegion, orderByParent);
470 m_log.Info("[REGION DB]: " +
471 "Loaded " + primsForRegion.Length + " prims for region: " + regionUUID);
472
473 foreach (DataRow primRow in primsForRegion)
474 {
475 try
476 {
477 string uuid = (string) primRow["UUID"];
478 string objID = (string) primRow["SceneGroupID"];
479
480 SceneObjectPart prim = buildPrim(primRow);
481
482 if (uuid == objID) //is new SceneObjectGroup ?
483 {
484 SceneObjectGroup group = new SceneObjectGroup();
485
486 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(prim.UUID));
487 if (shapeRow != null)
488 {
489 prim.Shape = buildShape(shapeRow);
490 }
491 else
492 {
493 m_log.Info(
494 "No shape found for prim in storage, so setting default box shape");
495 prim.Shape = PrimitiveBaseShape.Default;
496 }
497 group.AddPart(prim);
498 group.RootPart = prim;
499
500 createdObjects.Add(group.UUID, group);
501 retvals.Add(group);
502 }
503 else
504 {
505 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(prim.UUID));
506 if (shapeRow != null)
507 {
508 prim.Shape = buildShape(shapeRow);
509 }
510 else
511 {
512 m_log.Info(
513 "No shape found for prim in storage, so setting default box shape");
514 prim.Shape = PrimitiveBaseShape.Default;
515 }
516 createdObjects[new LLUUID(objID)].AddPart(prim);
517 }
518
519 LoadItems(prim);
520 }
521 catch (Exception e)
522 {
523 m_log.Error("[REGION DB]: Failed create prim object, exception and data follows");
524 m_log.Info("[REGION DB]: " + e.ToString());
525 foreach (DataColumn col in prims.Columns)
526 {
527 m_log.Info("[REGION DB]: Col: " + col.ColumnName + " => " + primRow[col]);
528 }
529 }
530 }
531 }
532 return retvals;
533 }
534
535 /// <summary>
536 /// Load in a prim's persisted inventory.
537 /// </summary>
538 /// <param name="prim">The prim</param>
539 private void LoadItems(SceneObjectPart prim)
540 {
541 lock (m_dataSet)
542 {
543 CheckConnection();
544 //m_log.InfoFormat("[DATASTORE]: Loading inventory for {0}, {1}", prim.Name, prim.UUID);
545
546 DataTable dbItems = m_itemsTable;
547
548 String sql = String.Format("primID = '{0}'", prim.UUID.ToString());
549 DataRow[] dbItemRows = dbItems.Select(sql);
550
551 IList<TaskInventoryItem> inventory = new List<TaskInventoryItem>();
552
553 foreach (DataRow row in dbItemRows)
554 {
555 TaskInventoryItem item = buildItem(row);
556 inventory.Add(item);
557
558 //m_log.DebugFormat("[DATASTORE]: Restored item {0}, {1}", item.Name, item.ItemID);
559 }
560
561 prim.RestoreInventoryItems(inventory);
562
563 // XXX A nasty little hack to recover the folder id for the prim (which is currently stored in
564 // every item). This data should really be stored in the prim table itself.
565 if (dbItemRows.Length > 0)
566 {
567 prim.FolderID = inventory[0].ParentID;
568 }
569 }
570 }
571
572 /// <summary>
573 /// Store a terrain revision in region storage
574 /// </summary>
575 /// <param name="ter">HeightField data</param>
576 /// <param name="regionID">region UUID</param>
577 public void StoreTerrain(double[,] ter, LLUUID regionID)
578 {
579 int revision = 1;
580 m_log.Info("[REGION DB]: Storing terrain revision r" + revision.ToString());
581
582 lock (m_dataSet)
583 {
584 MySqlCommand delete = new MySqlCommand("delete from terrain where RegionUUID=?RegionUUID", m_connection);
585 MySqlCommand cmd = new MySqlCommand("insert into terrain(RegionUUID, Revision, Heightfield)" +
586 " values(?RegionUUID, ?Revision, ?Heightfield)", m_connection);
587 using (cmd)
588 {
589 delete.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
590
591 CheckConnection();
592 delete.ExecuteNonQuery();
593
594 cmd.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
595 cmd.Parameters.Add(new MySqlParameter("?Revision", revision));
596 cmd.Parameters.Add(new MySqlParameter("?Heightfield", serializeTerrain(ter)));
597 cmd.ExecuteNonQuery();
598 }
599 }
600 }
601
602 /// <summary>
603 /// Load the latest terrain revision from region storage
604 /// </summary>
605 /// <param name="regionID">the region UUID</param>
606 /// <returns>Heightfield data</returns>
607 public double[,] LoadTerrain(LLUUID regionID)
608 {
609 double[,] terret = new double[256,256];
610 terret.Initialize();
611
612 MySqlCommand cmd = new MySqlCommand(
613 @"select RegionUUID, Revision, Heightfield from terrain
614 where RegionUUID=?RegionUUID order by Revision desc limit 1"
615 , m_connection);
616
617 // MySqlParameter param = new MySqlParameter();
618 cmd.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
619
620 if (m_connection.State != ConnectionState.Open)
621 {
622 m_connection.Open();
623 }
624
625 lock (m_dataSet)
626 {
627 CheckConnection();
628 using (MySqlDataReader row = cmd.ExecuteReader())
629 {
630 int rev = 0;
631 if (row.Read())
632 {
633 MemoryStream str = new MemoryStream((byte[]) row["Heightfield"]);
634 BinaryReader br = new BinaryReader(str);
635 for (int x = 0; x < 256; x++)
636 {
637 for (int y = 0; y < 256; y++)
638 {
639 terret[x, y] = br.ReadDouble();
640 }
641 }
642 rev = (int) row["Revision"];
643 }
644 else
645 {
646 m_log.Info("[REGION DB]: No terrain found for region");
647 return null;
648 }
649
650 m_log.Info("[REGION DB]: Loaded terrain revision r" + rev.ToString());
651 }
652 }
653 return terret;
654 }
655
656 /// <summary>
657 /// <list type="bullet">
658 /// <item>delete from land where UUID=globalID</item>
659 /// <item>delete from landaccesslist where LandUUID=globalID</item>
660 /// </list>
661 /// </summary>
662 /// <param name="globalID"></param>
663 public void RemoveLandObject(LLUUID globalID)
664 {
665 lock (m_dataSet)
666 {
667 CheckConnection();
668 using (MySqlCommand cmd = new MySqlCommand("delete from land where UUID=?UUID", m_connection))
669 {
670 cmd.Parameters.Add(new MySqlParameter("?UUID", Util.ToRawUuidString(globalID)));
671 cmd.ExecuteNonQuery();
672 }
673
674 using (
675 MySqlCommand cmd = new MySqlCommand("delete from landaccesslist where LandUUID=?UUID", m_connection)
676 )
677 {
678 cmd.Parameters.Add(new MySqlParameter("?UUID", Util.ToRawUuidString(globalID)));
679 cmd.ExecuteNonQuery();
680 }
681 }
682 }
683
684 /// <summary>
685 /// </summary>
686 /// <param name="parcel"></param>
687 public void StoreLandObject(ILandObject parcel)
688 {
689 lock (m_dataSet)
690 {
691 CheckConnection();
692 DataTable land = m_landTable;
693 DataTable landaccesslist = m_landAccessListTable;
694
695 DataRow landRow = land.Rows.Find(Util.ToRawUuidString(parcel.landData.GlobalID));
696 if (landRow == null)
697 {
698 landRow = land.NewRow();
699 fillLandRow(landRow, parcel.landData, parcel.regionUUID);
700 land.Rows.Add(landRow);
701 }
702 else
703 {
704 fillLandRow(landRow, parcel.landData, parcel.regionUUID);
705 }
706
707 using (
708 MySqlCommand cmd =
709 new MySqlCommand("delete from landaccesslist where LandUUID=?LandUUID", m_connection))
710 {
711 cmd.Parameters.Add(new MySqlParameter("?LandUUID", Util.ToRawUuidString(parcel.landData.GlobalID)));
712 cmd.ExecuteNonQuery();
713 }
714
715 foreach (ParcelManager.ParcelAccessEntry entry in parcel.landData.ParcelAccessList)
716 {
717 DataRow newAccessRow = landaccesslist.NewRow();
718 fillLandAccessRow(newAccessRow, entry, parcel.landData.GlobalID);
719 landaccesslist.Rows.Add(newAccessRow);
720 }
721
722 Commit();
723 }
724 }
725
726 public RegionSettings LoadRegionSettings(LLUUID regionUUID)
727 {
728 lock (m_dataSet)
729 {
730 CheckConnection();
731 DataTable regionsettings = m_regionSettingsTable;
732 string searchExp = "regionUUID = '" + regionUUID.ToString() + "'";
733 DataRow[] rawsettings = regionsettings.Select(searchExp);
734 if (rawsettings.Length == 0)
735 {
736 RegionSettings rs = new RegionSettings();
737 rs.RegionUUID = regionUUID;
738 rs.OnSave += StoreRegionSettings;
739
740 StoreRegionSettings(rs);
741
742 return rs;
743 }
744 DataRow row = rawsettings[0];
745
746 RegionSettings newSettings = buildRegionSettings(row);
747 newSettings.OnSave += StoreRegionSettings;
748
749 return newSettings;
750 }
751 }
752
753 public void StoreRegionSettings(RegionSettings rs)
754 {
755 lock (m_dataSet)
756 {
757 CheckConnection();
758 DataTable regionsettings = m_dataSet.Tables["regionsettings"];
759
760 DataRow settingsRow = regionsettings.Rows.Find(rs.RegionUUID.ToString());
761 if (settingsRow == null)
762 {
763 settingsRow = regionsettings.NewRow();
764 fillRegionSettingsRow(settingsRow, rs);
765 regionsettings.Rows.Add(settingsRow);
766 }
767 else
768 {
769 fillRegionSettingsRow(settingsRow, rs);
770 }
771
772 Commit();
773 }
774 }
775
776 /// <summary>
777 ///
778 /// </summary>
779 /// <param name="regionUUID"></param>
780 /// <returns></returns>
781 public List<LandData> LoadLandObjects(LLUUID regionUUID)
782 {
783 List<LandData> landDataForRegion = new List<LandData>();
784 lock (m_dataSet)
785 {
786 CheckConnection();
787 DataTable land = m_landTable;
788 DataTable landaccesslist = m_landAccessListTable;
789 string searchExp = "RegionUUID = '" + Util.ToRawUuidString(regionUUID) + "'";
790 DataRow[] rawDataForRegion = land.Select(searchExp);
791 foreach (DataRow rawDataLand in rawDataForRegion)
792 {
793 LandData newLand = buildLandData(rawDataLand);
794 string accessListSearchExp = "LandUUID = '" + Util.ToRawUuidString(newLand.GlobalID) + "'";
795 DataRow[] rawDataForLandAccessList = landaccesslist.Select(accessListSearchExp);
796 foreach (DataRow rawDataLandAccess in rawDataForLandAccessList)
797 {
798 newLand.ParcelAccessList.Add(buildLandAccessData(rawDataLandAccess));
799 }
800
801 landDataForRegion.Add(newLand);
802 }
803 }
804 return landDataForRegion;
805 }
806
807 /// <summary>
808 ///
809 /// </summary>
810 public void Commit()
811 {
812 lock (m_dataSet)
813 {
814 CheckConnection();
815 // DisplayDataSet(m_dataSet, "Region DataSet");
816
817 m_primDataAdapter.Update(m_primTable);
818 m_shapeDataAdapter.Update(m_shapeTable);
819
820 m_itemsDataAdapter.Update(m_itemsTable);
821
822 m_terrainDataAdapter.Update(m_terrainTable);
823 m_landDataAdapter.Update(m_landTable);
824 m_landAccessListDataAdapter.Update(m_landAccessListTable);
825 m_regionSettingsDataAdapter.Update(m_regionSettingsTable);
826
827 m_dataSet.AcceptChanges();
828 }
829 }
830
831 /// <summary>
832 /// See <see cref="Commit"/>
833 /// </summary>
834 public void Shutdown()
835 {
836 Commit();
837 }
838
839 /***********************************************************************
840 *
841 * Database Definition Functions
842 *
843 * This should be db agnostic as we define them in ADO.NET terms
844 *
845 **********************************************************************/
846
847 /// <summary>
848 ///
849 /// </summary>
850 /// <param name="dt"></param>
851 /// <param name="name"></param>
852 /// <param name="type"></param>
853 /// <returns></returns>
854 private static DataColumn createCol(DataTable dt, string name, Type type)
855 {
856 DataColumn col = new DataColumn(name, type);
857 dt.Columns.Add(col);
858 return col;
859 }
860
861 /// <summary>
862 /// Create the "terrain" table
863 /// </summary>
864 /// <returns></returns>
865 private static DataTable createTerrainTable()
866 {
867 DataTable terrain = new DataTable("terrain");
868
869 createCol(terrain, "RegionUUID", typeof (String));
870 createCol(terrain, "Revision", typeof (Int32));
871 createCol(terrain, "Heightfield", typeof (Byte[]));
872 return terrain;
873 }
874
875 /// <summary>
876 /// Create the "regionsettings" table
877 /// </summary>
878 /// <returns></returns>
879 private static DataTable createRegionSettingsTable()
880 {
881 DataTable regionsettings = new DataTable("regionsettings");
882 createCol(regionsettings, "regionUUID", typeof(String));
883 createCol(regionsettings, "block_terraform", typeof (Int32));
884 createCol(regionsettings, "block_fly", typeof (Int32));
885 createCol(regionsettings, "allow_damage", typeof (Int32));
886 createCol(regionsettings, "restrict_pushing", typeof (Int32));
887 createCol(regionsettings, "allow_land_resell", typeof (Int32));
888 createCol(regionsettings, "allow_land_join_divide", typeof (Int32));
889 createCol(regionsettings, "block_show_in_search", typeof (Int32));
890 createCol(regionsettings, "agent_limit", typeof (Int32));
891 createCol(regionsettings, "object_bonus", typeof (Double));
892 createCol(regionsettings, "maturity", typeof (Int32));
893 createCol(regionsettings, "disable_scripts", typeof (Int32));
894 createCol(regionsettings, "disable_collisions", typeof (Int32));
895 createCol(regionsettings, "disable_physics", typeof (Int32));
896 createCol(regionsettings, "terrain_texture_1", typeof(String));
897 createCol(regionsettings, "terrain_texture_2", typeof(String));
898 createCol(regionsettings, "terrain_texture_3", typeof(String));
899 createCol(regionsettings, "terrain_texture_4", typeof(String));
900 createCol(regionsettings, "elevation_1_nw", typeof (Double));
901 createCol(regionsettings, "elevation_2_nw", typeof (Double));
902 createCol(regionsettings, "elevation_1_ne", typeof (Double));
903 createCol(regionsettings, "elevation_2_ne", typeof (Double));
904 createCol(regionsettings, "elevation_1_se", typeof (Double));
905 createCol(regionsettings, "elevation_2_se", typeof (Double));
906 createCol(regionsettings, "elevation_1_sw", typeof (Double));
907 createCol(regionsettings, "elevation_2_sw", typeof (Double));
908 createCol(regionsettings, "water_height", typeof (Double));
909 createCol(regionsettings, "terrain_raise_limit", typeof (Double));
910 createCol(regionsettings, "terrain_lower_limit", typeof (Double));
911 createCol(regionsettings, "use_estate_sun", typeof (Int32));
912 createCol(regionsettings, "sandbox", typeof (Int32));
913 createCol(regionsettings, "fixed_sun", typeof (Int32));
914 createCol(regionsettings, "sun_position", typeof (Double));
915 createCol(regionsettings, "covenant", typeof(String));
916
917 regionsettings.PrimaryKey = new DataColumn[] {regionsettings.Columns["RegionUUID"]};
918
919 return regionsettings;
920 }
921
922 /// <summary>
923 /// Create the "prims" table
924 /// </summary>
925 /// <returns></returns>
926 private static DataTable createPrimTable()
927 {
928 DataTable prims = new DataTable("prims");
929
930 createCol(prims, "UUID", typeof (String));
931 createCol(prims, "RegionUUID", typeof (String));
932 createCol(prims, "ParentID", typeof (Int32));
933 createCol(prims, "CreationDate", typeof (Int32));
934 createCol(prims, "Name", typeof (String));
935 createCol(prims, "SceneGroupID", typeof (String));
936 // various text fields
937 createCol(prims, "Text", typeof (String));
938 createCol(prims, "Description", typeof (String));
939 createCol(prims, "SitName", typeof (String));
940 createCol(prims, "TouchName", typeof (String));
941 // permissions
942 createCol(prims, "ObjectFlags", typeof (Int32));
943 createCol(prims, "CreatorID", typeof (String));
944 createCol(prims, "OwnerID", typeof (String));
945 createCol(prims, "GroupID", typeof (String));
946 createCol(prims, "LastOwnerID", typeof (String));
947 createCol(prims, "OwnerMask", typeof (Int32));
948 createCol(prims, "NextOwnerMask", typeof (Int32));
949 createCol(prims, "GroupMask", typeof (Int32));
950 createCol(prims, "EveryoneMask", typeof (Int32));
951 createCol(prims, "BaseMask", typeof (Int32));
952 // vectors
953 createCol(prims, "PositionX", typeof (Double));
954 createCol(prims, "PositionY", typeof (Double));
955 createCol(prims, "PositionZ", typeof (Double));
956 createCol(prims, "GroupPositionX", typeof (Double));
957 createCol(prims, "GroupPositionY", typeof (Double));
958 createCol(prims, "GroupPositionZ", typeof (Double));
959 createCol(prims, "VelocityX", typeof (Double));
960 createCol(prims, "VelocityY", typeof (Double));
961 createCol(prims, "VelocityZ", typeof (Double));
962 createCol(prims, "AngularVelocityX", typeof (Double));
963 createCol(prims, "AngularVelocityY", typeof (Double));
964 createCol(prims, "AngularVelocityZ", typeof (Double));
965 createCol(prims, "AccelerationX", typeof (Double));
966 createCol(prims, "AccelerationY", typeof (Double));
967 createCol(prims, "AccelerationZ", typeof (Double));
968 // quaternions
969 createCol(prims, "RotationX", typeof (Double));
970 createCol(prims, "RotationY", typeof (Double));
971 createCol(prims, "RotationZ", typeof (Double));
972 createCol(prims, "RotationW", typeof (Double));
973 // sit target
974 createCol(prims, "SitTargetOffsetX", typeof (Double));
975 createCol(prims, "SitTargetOffsetY", typeof (Double));
976 createCol(prims, "SitTargetOffsetZ", typeof (Double));
977
978 createCol(prims, "SitTargetOrientW", typeof (Double));
979 createCol(prims, "SitTargetOrientX", typeof (Double));
980 createCol(prims, "SitTargetOrientY", typeof (Double));
981 createCol(prims, "SitTargetOrientZ", typeof (Double));
982
983
984 // Add in contraints
985 prims.PrimaryKey = new DataColumn[] {prims.Columns["UUID"]};
986
987 return prims;
988 }
989
990 /// <summary>
991 /// Create the "land" table
992 /// </summary>
993 /// <returns></returns>
994 private static DataTable createLandTable()
995 {
996 DataTable land = new DataTable("land");
997 createCol(land, "UUID", typeof (String));
998 createCol(land, "RegionUUID", typeof (String));
999 createCol(land, "LocalLandID", typeof (Int32));
1000
1001 // Bitmap is a byte[512]
1002 createCol(land, "Bitmap", typeof (Byte[]));
1003
1004 createCol(land, "Name", typeof (String));
1005 createCol(land, "Description", typeof (String));
1006 createCol(land, "OwnerUUID", typeof (String));
1007 createCol(land, "IsGroupOwned", typeof (Int32));
1008 createCol(land, "Area", typeof (Int32));
1009 createCol(land, "AuctionID", typeof (Int32)); //Unemplemented
1010 createCol(land, "Category", typeof (Int32)); //Enum libsecondlife.Parcel.ParcelCategory
1011 createCol(land, "ClaimDate", typeof (Int32));
1012 createCol(land, "ClaimPrice", typeof (Int32));
1013 createCol(land, "GroupUUID", typeof (String));
1014 createCol(land, "SalePrice", typeof (Int32));
1015 createCol(land, "LandStatus", typeof (Int32)); //Enum. libsecondlife.Parcel.ParcelStatus
1016 createCol(land, "LandFlags", typeof (Int32));
1017 createCol(land, "LandingType", typeof (Int32));
1018 createCol(land, "MediaAutoScale", typeof (Int32));
1019 createCol(land, "MediaTextureUUID", typeof (String));
1020 createCol(land, "MediaURL", typeof (String));
1021 createCol(land, "MusicURL", typeof (String));
1022 createCol(land, "PassHours", typeof (Double));
1023 createCol(land, "PassPrice", typeof (Int32));
1024 createCol(land, "SnapshotUUID", typeof (String));
1025 createCol(land, "UserLocationX", typeof (Double));
1026 createCol(land, "UserLocationY", typeof (Double));
1027 createCol(land, "UserLocationZ", typeof (Double));
1028 createCol(land, "UserLookAtX", typeof (Double));
1029 createCol(land, "UserLookAtY", typeof (Double));
1030 createCol(land, "UserLookAtZ", typeof (Double));
1031 createCol(land, "AuthBuyerID", typeof (String));
1032
1033 land.PrimaryKey = new DataColumn[] {land.Columns["UUID"]};
1034
1035 return land;
1036 }
1037
1038 /// <summary>
1039 /// Create the "landaccesslist" table
1040 /// </summary>
1041 /// <returns></returns>
1042 private static DataTable createLandAccessListTable()
1043 {
1044 DataTable landaccess = new DataTable("landaccesslist");
1045 createCol(landaccess, "LandUUID", typeof (String));
1046 createCol(landaccess, "AccessUUID", typeof (String));
1047 createCol(landaccess, "Flags", typeof (Int32));
1048
1049 return landaccess;
1050 }
1051
1052 /// <summary>
1053 /// Create the "primshapes" table
1054 /// </summary>
1055 /// <returns></returns>
1056 private static DataTable createShapeTable()
1057 {
1058 DataTable shapes = new DataTable("primshapes");
1059 createCol(shapes, "UUID", typeof (String));
1060 // shape is an enum
1061 createCol(shapes, "Shape", typeof (Int32));
1062 // vectors
1063 createCol(shapes, "ScaleX", typeof (Double));
1064 createCol(shapes, "ScaleY", typeof (Double));
1065 createCol(shapes, "ScaleZ", typeof (Double));
1066 // paths
1067 createCol(shapes, "PCode", typeof (Int32));
1068 createCol(shapes, "PathBegin", typeof (Int32));
1069 createCol(shapes, "PathEnd", typeof (Int32));
1070 createCol(shapes, "PathScaleX", typeof (Int32));
1071 createCol(shapes, "PathScaleY", typeof (Int32));
1072 createCol(shapes, "PathShearX", typeof (Int32));
1073 createCol(shapes, "PathShearY", typeof (Int32));
1074 createCol(shapes, "PathSkew", typeof (Int32));
1075 createCol(shapes, "PathCurve", typeof (Int32));
1076 createCol(shapes, "PathRadiusOffset", typeof (Int32));
1077 createCol(shapes, "PathRevolutions", typeof (Int32));
1078 createCol(shapes, "PathTaperX", typeof (Int32));
1079 createCol(shapes, "PathTaperY", typeof (Int32));
1080 createCol(shapes, "PathTwist", typeof (Int32));
1081 createCol(shapes, "PathTwistBegin", typeof (Int32));
1082 // profile
1083 createCol(shapes, "ProfileBegin", typeof (Int32));
1084 createCol(shapes, "ProfileEnd", typeof (Int32));
1085 createCol(shapes, "ProfileCurve", typeof (Int32));
1086 createCol(shapes, "ProfileHollow", typeof (Int32));
1087 createCol(shapes, "State", typeof(Int32));
1088 createCol(shapes, "Texture", typeof (Byte[]));
1089 createCol(shapes, "ExtraParams", typeof (Byte[]));
1090
1091 shapes.PrimaryKey = new DataColumn[] {shapes.Columns["UUID"]};
1092
1093 return shapes;
1094 }
1095
1096 /// <summary>
1097 /// Create the "primitems" table
1098 /// </summary>
1099 /// <returns></returns>
1100 private static DataTable createItemsTable()
1101 {
1102 DataTable items = new DataTable("primitems");
1103
1104 createCol(items, "itemID", typeof (String));
1105 createCol(items, "primID", typeof (String));
1106 createCol(items, "assetID", typeof (String));
1107 createCol(items, "parentFolderID", typeof (String));
1108
1109 createCol(items, "invType", typeof (Int32));
1110 createCol(items, "assetType", typeof (Int32));
1111
1112 createCol(items, "name", typeof (String));
1113 createCol(items, "description", typeof (String));
1114
1115 createCol(items, "creationDate", typeof (Int64));
1116 createCol(items, "creatorID", typeof (String));
1117 createCol(items, "ownerID", typeof (String));
1118 createCol(items, "lastOwnerID", typeof (String));
1119 createCol(items, "groupID", typeof (String));
1120
1121 createCol(items, "nextPermissions", typeof (Int32));
1122 createCol(items, "currentPermissions", typeof (Int32));
1123 createCol(items, "basePermissions", typeof (Int32));
1124 createCol(items, "everyonePermissions", typeof (Int32));
1125 createCol(items, "groupPermissions", typeof (Int32));
1126 createCol(items, "flags", typeof (Int32));
1127
1128 items.PrimaryKey = new DataColumn[] {items.Columns["itemID"]};
1129
1130 return items;
1131 }
1132
1133 /***********************************************************************
1134 *
1135 * Convert between ADO.NET <=> OpenSim Objects
1136 *
1137 * These should be database independant
1138 *
1139 **********************************************************************/
1140
1141 /// <summary>
1142 ///
1143 /// </summary>
1144 /// <param name="row"></param>
1145 /// <returns></returns>
1146 private SceneObjectPart buildPrim(DataRow row)
1147 {
1148 SceneObjectPart prim = new SceneObjectPart();
1149 prim.UUID = new LLUUID((String) row["UUID"]);
1150 // explicit conversion of integers is required, which sort
1151 // of sucks. No idea if there is a shortcut here or not.
1152 prim.ParentID = Convert.ToUInt32(row["ParentID"]);
1153 prim.CreationDate = Convert.ToInt32(row["CreationDate"]);
1154 prim.Name = (String) row["Name"];
1155 // various text fields
1156 prim.Text = (String) row["Text"];
1157 prim.Description = (String) row["Description"];
1158 prim.SitName = (String) row["SitName"];
1159 prim.TouchName = (String) row["TouchName"];
1160 // permissions
1161 prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]);
1162 prim.CreatorID = new LLUUID((String) row["CreatorID"]);
1163 prim.OwnerID = new LLUUID((String) row["OwnerID"]);
1164 prim.GroupID = new LLUUID((String) row["GroupID"]);
1165 prim.LastOwnerID = new LLUUID((String) row["LastOwnerID"]);
1166 prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]);
1167 prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]);
1168 prim.GroupMask = Convert.ToUInt32(row["GroupMask"]);
1169 prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]);
1170 prim.BaseMask = Convert.ToUInt32(row["BaseMask"]);
1171 // vectors
1172 prim.OffsetPosition = new LLVector3(
1173 Convert.ToSingle(row["PositionX"]),
1174 Convert.ToSingle(row["PositionY"]),
1175 Convert.ToSingle(row["PositionZ"])
1176 );
1177 prim.GroupPosition = new LLVector3(
1178 Convert.ToSingle(row["GroupPositionX"]),
1179 Convert.ToSingle(row["GroupPositionY"]),
1180 Convert.ToSingle(row["GroupPositionZ"])
1181 );
1182 prim.Velocity = new LLVector3(
1183 Convert.ToSingle(row["VelocityX"]),
1184 Convert.ToSingle(row["VelocityY"]),
1185 Convert.ToSingle(row["VelocityZ"])
1186 );
1187 prim.AngularVelocity = new LLVector3(
1188 Convert.ToSingle(row["AngularVelocityX"]),
1189 Convert.ToSingle(row["AngularVelocityY"]),
1190 Convert.ToSingle(row["AngularVelocityZ"])
1191 );
1192 prim.Acceleration = new LLVector3(
1193 Convert.ToSingle(row["AccelerationX"]),
1194 Convert.ToSingle(row["AccelerationY"]),
1195 Convert.ToSingle(row["AccelerationZ"])
1196 );
1197 // quaternions
1198 prim.RotationOffset = new LLQuaternion(
1199 Convert.ToSingle(row["RotationX"]),
1200 Convert.ToSingle(row["RotationY"]),
1201 Convert.ToSingle(row["RotationZ"]),
1202 Convert.ToSingle(row["RotationW"])
1203 );
1204 try
1205 {
1206 prim.SitTargetPositionLL = new LLVector3(
1207 Convert.ToSingle(row["SitTargetOffsetX"]),
1208 Convert.ToSingle(row["SitTargetOffsetY"]),
1209 Convert.ToSingle(row["SitTargetOffsetZ"]));
1210 prim.SitTargetOrientationLL = new LLQuaternion(
1211 Convert.ToSingle(
1212 row["SitTargetOrientX"]),
1213 Convert.ToSingle(
1214 row["SitTargetOrientY"]),
1215 Convert.ToSingle(
1216 row["SitTargetOrientZ"]),
1217 Convert.ToSingle(
1218 row["SitTargetOrientW"]));
1219 }
1220 catch (InvalidCastException)
1221 {
1222 // Database table was created before we got here and needs to be created! :P
1223
1224 lock (m_dataSet)
1225 {
1226 using (
1227 MySqlCommand cmd =
1228 new MySqlCommand(
1229 "ALTER TABLE `prims` ADD COLUMN `SitTargetOffsetX` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetY` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetZ` float NOT NULL default 0, ADD COLUMN `SitTargetOrientW` float NOT NULL default 0, ADD COLUMN `SitTargetOrientX` float NOT NULL default 0, ADD COLUMN `SitTargetOrientY` float NOT NULL default 0, ADD COLUMN `SitTargetOrientZ` float NOT NULL default 0;",
1230 m_connection))
1231 {
1232 cmd.ExecuteNonQuery();
1233 }
1234 }
1235 }
1236 return prim;
1237 }
1238
1239
1240 /// <summary>
1241 /// Build a prim inventory item from the persisted data.
1242 /// </summary>
1243 /// <param name="row"></param>
1244 /// <returns></returns>
1245 private static TaskInventoryItem buildItem(DataRow row)
1246 {
1247 TaskInventoryItem taskItem = new TaskInventoryItem();
1248
1249 taskItem.ItemID = new LLUUID((String)row["itemID"]);
1250 taskItem.ParentPartID = new LLUUID((String)row["primID"]);
1251 taskItem.AssetID = new LLUUID((String)row["assetID"]);
1252 taskItem.ParentID = new LLUUID((String)row["parentFolderID"]);
1253
1254 taskItem.InvType = Convert.ToInt32(row["invType"]);
1255 taskItem.Type = Convert.ToInt32(row["assetType"]);
1256
1257 taskItem.Name = (String)row["name"];
1258 taskItem.Description = (String)row["description"];
1259 taskItem.CreationDate = Convert.ToUInt32(row["creationDate"]);
1260 taskItem.CreatorID = new LLUUID((String)row["creatorID"]);
1261 taskItem.OwnerID = new LLUUID((String)row["ownerID"]);
1262 taskItem.LastOwnerID = new LLUUID((String)row["lastOwnerID"]);
1263 taskItem.GroupID = new LLUUID((String)row["groupID"]);
1264
1265 taskItem.NextPermissions = Convert.ToUInt32(row["nextPermissions"]);
1266 taskItem.CurrentPermissions = Convert.ToUInt32(row["currentPermissions"]);
1267 taskItem.BasePermissions = Convert.ToUInt32(row["basePermissions"]);
1268 taskItem.EveryonePermissions = Convert.ToUInt32(row["everyonePermissions"]);
1269 taskItem.GroupPermissions = Convert.ToUInt32(row["groupPermissions"]);
1270 taskItem.Flags = Convert.ToUInt32(row["flags"]);
1271
1272 return taskItem;
1273 }
1274
1275 private static RegionSettings buildRegionSettings(DataRow row)
1276 {
1277 RegionSettings newSettings = new RegionSettings();
1278
1279 newSettings.RegionUUID = new LLUUID((string) row["regionUUID"]);
1280 newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]);
1281 newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]);
1282 newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]);
1283 newSettings.RestrictPushing = Convert.ToBoolean(row["restrict_pushing"]);
1284 newSettings.AllowLandResell = Convert.ToBoolean(row["allow_land_resell"]);
1285 newSettings.AllowLandJoinDivide = Convert.ToBoolean(row["allow_land_join_divide"]);
1286 newSettings.BlockShowInSearch = Convert.ToBoolean(row["block_show_in_search"]);
1287 newSettings.AgentLimit = Convert.ToInt32(row["agent_limit"]);
1288 newSettings.ObjectBonus = Convert.ToDouble(row["object_bonus"]);
1289 newSettings.Maturity = Convert.ToInt32(row["maturity"]);
1290 newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]);
1291 newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]);
1292 newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]);
1293 newSettings.TerrainTexture1 = new LLUUID((String) row["terrain_texture_1"]);
1294 newSettings.TerrainTexture2 = new LLUUID((String) row["terrain_texture_2"]);
1295 newSettings.TerrainTexture3 = new LLUUID((String) row["terrain_texture_3"]);
1296 newSettings.TerrainTexture4 = new LLUUID((String) row["terrain_texture_4"]);
1297 newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]);
1298 newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]);
1299 newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]);
1300 newSettings.Elevation2NE = Convert.ToDouble(row["elevation_2_ne"]);
1301 newSettings.Elevation1SE = Convert.ToDouble(row["elevation_1_se"]);
1302 newSettings.Elevation2SE = Convert.ToDouble(row["elevation_2_se"]);
1303 newSettings.Elevation1SW = Convert.ToDouble(row["elevation_1_sw"]);
1304 newSettings.Elevation2SW = Convert.ToDouble(row["elevation_2_sw"]);
1305 newSettings.WaterHeight = Convert.ToDouble(row["water_height"]);
1306 newSettings.TerrainRaiseLimit = Convert.ToDouble(row["terrain_raise_limit"]);
1307 newSettings.TerrainLowerLimit = Convert.ToDouble(row["terrain_lower_limit"]);
1308 newSettings.UseEstateSun = Convert.ToBoolean(row["use_estate_sun"]);
1309 newSettings.Sandbox = Convert.ToBoolean(row["sandbox"]);
1310 newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]);
1311 newSettings.SunPosition = Convert.ToDouble(row["sun_position"]);
1312 newSettings.Covenant = new LLUUID((String) row["covenant"]);
1313
1314 return newSettings;
1315 }
1316
1317 /// <summary>
1318 ///
1319 /// </summary>
1320 /// <param name="row"></param>
1321 /// <returns></returns>
1322 private static LandData buildLandData(DataRow row)
1323 {
1324 LandData newData = new LandData();
1325
1326 newData.GlobalID = new LLUUID((String) row["UUID"]);
1327 newData.LocalID = Convert.ToInt32(row["LocalLandID"]);
1328
1329 // Bitmap is a byte[512]
1330 newData.Bitmap = (Byte[]) row["Bitmap"];
1331
1332 newData.Name = (String) row["Name"];
1333 newData.Description = (String) row["Description"];
1334 newData.OwnerID = (String) row["OwnerUUID"];
1335 newData.IsGroupOwned = Convert.ToBoolean(row["IsGroupOwned"]);
1336 newData.Area = Convert.ToInt32(row["Area"]);
1337 newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unemplemented
1338 newData.Category = (Parcel.ParcelCategory) Convert.ToInt32(row["Category"]);
1339 //Enum libsecondlife.Parcel.ParcelCategory
1340 newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]);
1341 newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]);
1342 newData.GroupID = new LLUUID((String) row["GroupUUID"]);
1343 newData.SalePrice = Convert.ToInt32(row["SalePrice"]);
1344 newData.Status = (Parcel.ParcelStatus) Convert.ToInt32(row["LandStatus"]);
1345 //Enum. libsecondlife.Parcel.ParcelStatus
1346 newData.Flags = Convert.ToUInt32(row["LandFlags"]);
1347 newData.LandingType = Convert.ToByte(row["LandingType"]);
1348 newData.MediaAutoScale = Convert.ToByte(row["MediaAutoScale"]);
1349 newData.MediaID = new LLUUID((String) row["MediaTextureUUID"]);
1350 newData.MediaURL = (String) row["MediaURL"];
1351 newData.MusicURL = (String) row["MusicURL"];
1352 newData.PassHours = Convert.ToSingle(row["PassHours"]);
1353 newData.PassPrice = Convert.ToInt32(row["PassPrice"]);
1354 LLUUID authedbuyer = LLUUID.Zero;
1355 LLUUID snapshotID = LLUUID.Zero;
1356
1357 Helpers.TryParse((string)row["AuthBuyerID"], out authedbuyer);
1358 Helpers.TryParse((string)row["SnapshotUUID"], out snapshotID);
1359
1360 newData.AuthBuyerID = authedbuyer;
1361 newData.SnapshotID = snapshotID;
1362 try
1363 {
1364 newData.UserLocation =
1365 new LLVector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]),
1366 Convert.ToSingle(row["UserLocationZ"]));
1367 newData.UserLookAt =
1368 new LLVector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]),
1369 Convert.ToSingle(row["UserLookAtZ"]));
1370 }
1371 catch (InvalidCastException)
1372 {
1373 newData.UserLocation = LLVector3.Zero;
1374 newData.UserLookAt = LLVector3.Zero;
1375 m_log.ErrorFormat("[PARCEL]: unable to get parcel telehub settings for {1}", newData.Name);
1376 }
1377
1378 newData.ParcelAccessList = new List<ParcelManager.ParcelAccessEntry>();
1379
1380 return newData;
1381 }
1382
1383 /// <summary>
1384 ///
1385 /// </summary>
1386 /// <param name="row"></param>
1387 /// <returns></returns>
1388 private static ParcelManager.ParcelAccessEntry buildLandAccessData(DataRow row)
1389 {
1390 ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry();
1391 entry.AgentID = new LLUUID((string) row["AccessUUID"]);
1392 entry.Flags = (ParcelManager.AccessList) Convert.ToInt32(row["Flags"]);
1393 entry.Time = new DateTime();
1394 return entry;
1395 }
1396
1397 /// <summary>
1398 ///
1399 /// </summary>
1400 /// <param name="val"></param>
1401 /// <returns></returns>
1402 private static Array serializeTerrain(double[,] val)
1403 {
1404 MemoryStream str = new MemoryStream(65536*sizeof (double));
1405 BinaryWriter bw = new BinaryWriter(str);
1406
1407 // TODO: COMPATIBILITY - Add byte-order conversions
1408 for (int x = 0; x < 256; x++)
1409 for (int y = 0; y < 256; y++)
1410 {
1411 double height = val[x, y];
1412 if (height == 0.0)
1413 height = double.Epsilon;
1414
1415 bw.Write(height);
1416 }
1417
1418 return str.ToArray();
1419 }
1420
1421 /// <summary>
1422 ///
1423 /// </summary>
1424 /// <param name="row"></param>
1425 /// <param name="prim"></param>
1426 /// <param name="sceneGroupID"></param>
1427 /// <param name="regionUUID"></param>
1428 private void fillPrimRow(DataRow row, SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID)
1429 {
1430 row["UUID"] = Util.ToRawUuidString(prim.UUID);
1431 row["RegionUUID"] = Util.ToRawUuidString(regionUUID);
1432 row["ParentID"] = prim.ParentID;
1433 row["CreationDate"] = prim.CreationDate;
1434 row["Name"] = prim.Name;
1435 row["SceneGroupID"] = Util.ToRawUuidString(sceneGroupID);
1436 // the UUID of the root part for this SceneObjectGroup
1437 // various text fields
1438 row["Text"] = prim.Text;
1439 row["Description"] = prim.Description;
1440 row["SitName"] = prim.SitName;
1441 row["TouchName"] = prim.TouchName;
1442 // permissions
1443 row["ObjectFlags"] = prim.ObjectFlags;
1444 row["CreatorID"] = Util.ToRawUuidString(prim.CreatorID);
1445 row["OwnerID"] = Util.ToRawUuidString(prim.OwnerID);
1446 row["GroupID"] = Util.ToRawUuidString(prim.GroupID);
1447 row["LastOwnerID"] = Util.ToRawUuidString(prim.LastOwnerID);
1448 row["OwnerMask"] = prim.OwnerMask;
1449 row["NextOwnerMask"] = prim.NextOwnerMask;
1450 row["GroupMask"] = prim.GroupMask;
1451 row["EveryoneMask"] = prim.EveryoneMask;
1452 row["BaseMask"] = prim.BaseMask;
1453 // vectors
1454 row["PositionX"] = prim.OffsetPosition.X;
1455 row["PositionY"] = prim.OffsetPosition.Y;
1456 row["PositionZ"] = prim.OffsetPosition.Z;
1457 row["GroupPositionX"] = prim.GroupPosition.X;
1458 row["GroupPositionY"] = prim.GroupPosition.Y;
1459 row["GroupPositionZ"] = prim.GroupPosition.Z;
1460 row["VelocityX"] = prim.Velocity.X;
1461 row["VelocityY"] = prim.Velocity.Y;
1462 row["VelocityZ"] = prim.Velocity.Z;
1463 row["AngularVelocityX"] = prim.AngularVelocity.X;
1464 row["AngularVelocityY"] = prim.AngularVelocity.Y;
1465 row["AngularVelocityZ"] = prim.AngularVelocity.Z;
1466 row["AccelerationX"] = prim.Acceleration.X;
1467 row["AccelerationY"] = prim.Acceleration.Y;
1468 row["AccelerationZ"] = prim.Acceleration.Z;
1469 // quaternions
1470 row["RotationX"] = prim.RotationOffset.X;
1471 row["RotationY"] = prim.RotationOffset.Y;
1472 row["RotationZ"] = prim.RotationOffset.Z;
1473 row["RotationW"] = prim.RotationOffset.W;
1474
1475 try
1476 {
1477 // Sit target
1478 LLVector3 sitTargetPos = prim.SitTargetPositionLL;
1479 row["SitTargetOffsetX"] = sitTargetPos.X;
1480 row["SitTargetOffsetY"] = sitTargetPos.Y;
1481 row["SitTargetOffsetZ"] = sitTargetPos.Z;
1482
1483 LLQuaternion sitTargetOrient = prim.SitTargetOrientationLL;
1484 row["SitTargetOrientW"] = sitTargetOrient.W;
1485 row["SitTargetOrientX"] = sitTargetOrient.X;
1486 row["SitTargetOrientY"] = sitTargetOrient.Y;
1487 row["SitTargetOrientZ"] = sitTargetOrient.Z;
1488 }
1489 catch (MySqlException)
1490 {
1491 // Database table was created before we got here and needs to be created! :P
1492
1493 using (
1494 MySqlCommand cmd =
1495 new MySqlCommand(
1496 "ALTER TABLE `prims` ADD COLUMN `SitTargetOffsetX` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetY` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetZ` float NOT NULL default 0, ADD COLUMN `SitTargetOrientW` float NOT NULL default 0, ADD COLUMN `SitTargetOrientX` float NOT NULL default 0, ADD COLUMN `SitTargetOrientY` float NOT NULL default 0, ADD COLUMN `SitTargetOrientZ` float NOT NULL default 0;",
1497 m_connection))
1498 {
1499 cmd.ExecuteNonQuery();
1500 }
1501 }
1502 }
1503
1504 /// <summary>
1505 ///
1506 /// </summary>
1507 /// <param name="row"></param>
1508 /// <param name="taskItem"></param>
1509 private static void fillItemRow(DataRow row, TaskInventoryItem taskItem)
1510 {
1511 row["itemID"] = taskItem.ItemID;
1512 row["primID"] = taskItem.ParentPartID;
1513 row["assetID"] = taskItem.AssetID;
1514 row["parentFolderID"] = taskItem.ParentID;
1515
1516 row["invType"] = taskItem.InvType;
1517 row["assetType"] = taskItem.Type;
1518
1519 row["name"] = taskItem.Name;
1520 row["description"] = taskItem.Description;
1521 row["creationDate"] = taskItem.CreationDate;
1522 row["creatorID"] = taskItem.CreatorID;
1523 row["ownerID"] = taskItem.OwnerID;
1524 row["lastOwnerID"] = taskItem.LastOwnerID;
1525 row["groupID"] = taskItem.GroupID;
1526 row["nextPermissions"] = taskItem.NextPermissions;
1527 row["currentPermissions"] = taskItem.CurrentPermissions;
1528 row["basePermissions"] = taskItem.BasePermissions;
1529 row["everyonePermissions"] = taskItem.EveryonePermissions;
1530 row["groupPermissions"] = taskItem.GroupPermissions;
1531 row["flags"] = taskItem.Flags;
1532 }
1533
1534 /// <summary>
1535 ///
1536 /// </summary>
1537 private static void fillRegionSettingsRow(DataRow row, RegionSettings settings)
1538 {
1539 row["regionUUID"] = settings.RegionUUID.ToString();
1540 row["block_terraform"] = settings.BlockTerraform;
1541 row["block_fly"] = settings.BlockFly;
1542 row["allow_damage"] = settings.AllowDamage;
1543 row["restrict_pushing"] = settings.RestrictPushing;
1544 row["allow_land_resell"] = settings.AllowLandResell;
1545 row["allow_land_join_divide"] = settings.AllowLandJoinDivide;
1546 row["block_show_in_search"] = settings.BlockShowInSearch;
1547 row["agent_limit"] = settings.AgentLimit;
1548 row["object_bonus"] = settings.ObjectBonus;
1549 row["maturity"] = settings.Maturity;
1550 row["disable_scripts"] = settings.DisableScripts;
1551 row["disable_collisions"] = settings.DisableCollisions;
1552 row["disable_physics"] = settings.DisablePhysics;
1553 row["terrain_texture_1"] = settings.TerrainTexture1.ToString();
1554 row["terrain_texture_2"] = settings.TerrainTexture2.ToString();
1555 row["terrain_texture_3"] = settings.TerrainTexture3.ToString();
1556 row["terrain_texture_4"] = settings.TerrainTexture4.ToString();
1557 row["elevation_1_nw"] = settings.Elevation1NW;
1558 row["elevation_2_nw"] = settings.Elevation2NW;
1559 row["elevation_1_ne"] = settings.Elevation1NE;
1560 row["elevation_2_ne"] = settings.Elevation2NE;
1561 row["elevation_1_se"] = settings.Elevation1SE;
1562 row["elevation_2_se"] = settings.Elevation2SE;
1563 row["elevation_1_sw"] = settings.Elevation1SW;
1564 row["elevation_2_sw"] = settings.Elevation2SW;
1565 row["water_height"] = settings.WaterHeight;
1566 row["terrain_raise_limit"] = settings.TerrainRaiseLimit;
1567 row["terrain_lower_limit"] = settings.TerrainLowerLimit;
1568 row["use_estate_sun"] = settings.UseEstateSun;
1569 row["sandbox"] = settings.Sandbox;
1570 row["fixed_sun"] = settings.FixedSun;
1571 row["sun_position"] = settings.SunPosition;
1572 row["covenant"] = settings.Covenant.ToString();
1573 }
1574
1575 /// <summary>
1576 ///
1577 /// </summary>
1578 /// <param name="row"></param>
1579 /// <param name="land"></param>
1580 /// <param name="regionUUID"></param>
1581 private static void fillLandRow(DataRow row, LandData land, LLUUID regionUUID)
1582 {
1583 row["UUID"] = Util.ToRawUuidString(land.GlobalID);
1584 row["RegionUUID"] = Util.ToRawUuidString(regionUUID);
1585 row["LocalLandID"] = land.LocalID;
1586
1587 // Bitmap is a byte[512]
1588 row["Bitmap"] = land.Bitmap;
1589
1590 row["Name"] = land.Name;
1591 row["Description"] = land.Description;
1592 row["OwnerUUID"] = Util.ToRawUuidString(land.OwnerID);
1593 row["IsGroupOwned"] = land.IsGroupOwned;
1594 row["Area"] = land.Area;
1595 row["AuctionID"] = land.AuctionID; //Unemplemented
1596 row["Category"] = land.Category; //Enum libsecondlife.Parcel.ParcelCategory
1597 row["ClaimDate"] = land.ClaimDate;
1598 row["ClaimPrice"] = land.ClaimPrice;
1599 row["GroupUUID"] = Util.ToRawUuidString(land.GroupID);
1600 row["SalePrice"] = land.SalePrice;
1601 row["LandStatus"] = land.Status; //Enum. libsecondlife.Parcel.ParcelStatus
1602 row["LandFlags"] = land.Flags;
1603 row["LandingType"] = land.LandingType;
1604 row["MediaAutoScale"] = land.MediaAutoScale;
1605 row["MediaTextureUUID"] = Util.ToRawUuidString(land.MediaID);
1606 row["MediaURL"] = land.MediaURL;
1607 row["MusicURL"] = land.MusicURL;
1608 row["PassHours"] = land.PassHours;
1609 row["PassPrice"] = land.PassPrice;
1610 row["SnapshotUUID"] = Util.ToRawUuidString(land.SnapshotID);
1611 row["UserLocationX"] = land.UserLocation.X;
1612 row["UserLocationY"] = land.UserLocation.Y;
1613 row["UserLocationZ"] = land.UserLocation.Z;
1614 row["UserLookAtX"] = land.UserLookAt.X;
1615 row["UserLookAtY"] = land.UserLookAt.Y;
1616 row["UserLookAtZ"] = land.UserLookAt.Z;
1617 row["AuthBuyerID"] = land.AuthBuyerID;
1618 }
1619
1620 /// <summary>
1621 ///
1622 /// </summary>
1623 /// <param name="row"></param>
1624 /// <param name="entry"></param>
1625 /// <param name="parcelID"></param>
1626 private static void fillLandAccessRow(DataRow row, ParcelManager.ParcelAccessEntry entry, LLUUID parcelID)
1627 {
1628 row["LandUUID"] = Util.ToRawUuidString(parcelID);
1629 row["AccessUUID"] = Util.ToRawUuidString(entry.AgentID);
1630 row["Flags"] = entry.Flags;
1631 }
1632
1633 /// <summary>
1634 ///
1635 /// </summary>
1636 /// <param name="row"></param>
1637 /// <returns></returns>
1638 private PrimitiveBaseShape buildShape(DataRow row)
1639 {
1640 PrimitiveBaseShape s = new PrimitiveBaseShape();
1641 s.Scale = new LLVector3(
1642 Convert.ToSingle(row["ScaleX"]),
1643 Convert.ToSingle(row["ScaleY"]),
1644 Convert.ToSingle(row["ScaleZ"])
1645 );
1646 // paths
1647 s.PCode = Convert.ToByte(row["PCode"]);
1648 s.PathBegin = Convert.ToUInt16(row["PathBegin"]);
1649 s.PathEnd = Convert.ToUInt16(row["PathEnd"]);
1650 s.PathScaleX = Convert.ToByte(row["PathScaleX"]);
1651 s.PathScaleY = Convert.ToByte(row["PathScaleY"]);
1652 s.PathShearX = Convert.ToByte(row["PathShearX"]);
1653 s.PathShearY = Convert.ToByte(row["PathShearY"]);
1654 s.PathSkew = Convert.ToSByte(row["PathSkew"]);
1655 s.PathCurve = Convert.ToByte(row["PathCurve"]);
1656 s.PathRadiusOffset = Convert.ToSByte(row["PathRadiusOffset"]);
1657 s.PathRevolutions = Convert.ToByte(row["PathRevolutions"]);
1658 s.PathTaperX = Convert.ToSByte(row["PathTaperX"]);
1659 s.PathTaperY = Convert.ToSByte(row["PathTaperY"]);
1660 s.PathTwist = Convert.ToSByte(row["PathTwist"]);
1661 s.PathTwistBegin = Convert.ToSByte(row["PathTwistBegin"]);
1662 // profile
1663 s.ProfileBegin = Convert.ToUInt16(row["ProfileBegin"]);
1664 s.ProfileEnd = Convert.ToUInt16(row["ProfileEnd"]);
1665 s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]);
1666 s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]);
1667
1668 byte[] textureEntry = (byte[]) row["Texture"];
1669 s.TextureEntry = textureEntry;
1670
1671 s.ExtraParams = (byte[]) row["ExtraParams"];
1672
1673 try
1674 {
1675 s.State = Convert.ToByte(row["State"]);
1676 }
1677 catch (InvalidCastException)
1678 {
1679 // Database table was created before we got here and needs to be created! :P
1680 lock (m_dataSet)
1681 {
1682 using (
1683 MySqlCommand cmd =
1684 new MySqlCommand(
1685 "ALTER TABLE `primshapes` ADD COLUMN `State` int NOT NULL default 0;",
1686 m_connection))
1687 {
1688 cmd.ExecuteNonQuery();
1689 }
1690 }
1691 }
1692
1693 return s;
1694 }
1695
1696 /// <summary>
1697 ///
1698 /// </summary>
1699 /// <param name="row"></param>
1700 /// <param name="prim"></param>
1701 private void fillShapeRow(DataRow row, SceneObjectPart prim)
1702 {
1703 PrimitiveBaseShape s = prim.Shape;
1704 row["UUID"] = Util.ToRawUuidString(prim.UUID);
1705 // shape is an enum
1706 row["Shape"] = 0;
1707 // vectors
1708 row["ScaleX"] = s.Scale.X;
1709 row["ScaleY"] = s.Scale.Y;
1710 row["ScaleZ"] = s.Scale.Z;
1711 // paths
1712 row["PCode"] = s.PCode;
1713 row["PathBegin"] = s.PathBegin;
1714 row["PathEnd"] = s.PathEnd;
1715 row["PathScaleX"] = s.PathScaleX;
1716 row["PathScaleY"] = s.PathScaleY;
1717 row["PathShearX"] = s.PathShearX;
1718 row["PathShearY"] = s.PathShearY;
1719 row["PathSkew"] = s.PathSkew;
1720 row["PathCurve"] = s.PathCurve;
1721 row["PathRadiusOffset"] = s.PathRadiusOffset;
1722 row["PathRevolutions"] = s.PathRevolutions;
1723 row["PathTaperX"] = s.PathTaperX;
1724 row["PathTaperY"] = s.PathTaperY;
1725 row["PathTwist"] = s.PathTwist;
1726 row["PathTwistBegin"] = s.PathTwistBegin;
1727 // profile
1728 row["ProfileBegin"] = s.ProfileBegin;
1729 row["ProfileEnd"] = s.ProfileEnd;
1730 row["ProfileCurve"] = s.ProfileCurve;
1731 row["ProfileHollow"] = s.ProfileHollow;
1732 row["Texture"] = s.TextureEntry;
1733 row["ExtraParams"] = s.ExtraParams;
1734
1735 try
1736 {
1737 row["State"] = s.State;
1738 }
1739 catch (MySqlException)
1740 {
1741 lock (m_dataSet)
1742 {
1743 // Database table was created before we got here and needs to be created! :P
1744 using (
1745 MySqlCommand cmd =
1746 new MySqlCommand(
1747 "ALTER TABLE `primshapes` ADD COLUMN `State` int NOT NULL default 0;",
1748 m_connection))
1749 {
1750 cmd.ExecuteNonQuery();
1751 }
1752 }
1753 }
1754 }
1755
1756 /// <summary>
1757 ///
1758 /// </summary>
1759 /// <param name="prim"></param>
1760 /// <param name="sceneGroupID"></param>
1761 /// <param name="regionUUID"></param>
1762 private void addPrim(SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID)
1763 {
1764 lock (m_dataSet)
1765 {
1766 DataTable prims = m_dataSet.Tables["prims"];
1767 DataTable shapes = m_dataSet.Tables["primshapes"];
1768
1769 DataRow primRow = prims.Rows.Find(Util.ToRawUuidString(prim.UUID));
1770 if (primRow == null)
1771 {
1772 primRow = prims.NewRow();
1773 fillPrimRow(primRow, prim, sceneGroupID, regionUUID);
1774 prims.Rows.Add(primRow);
1775 }
1776 else
1777 {
1778 fillPrimRow(primRow, prim, sceneGroupID, regionUUID);
1779 }
1780
1781 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(prim.UUID));
1782 if (shapeRow == null)
1783 {
1784 shapeRow = shapes.NewRow();
1785 fillShapeRow(shapeRow, prim);
1786 shapes.Rows.Add(shapeRow);
1787 }
1788 else
1789 {
1790 fillShapeRow(shapeRow, prim);
1791 }
1792 }
1793 }
1794
1795 /// <summary>
1796 /// see IRegionDatastore
1797 /// </summary>
1798 /// <param name="primID"></param>
1799 /// <param name="items"></param>
1800 public void StorePrimInventory(LLUUID primID, ICollection<TaskInventoryItem> items)
1801 {
1802 m_log.InfoFormat("[REGION DB]: Persisting Prim Inventory with prim ID {0}", primID);
1803
1804 // For now, we're just going to crudely remove all the previous inventory items
1805 // no matter whether they have changed or not, and replace them with the current set.
1806 lock (m_dataSet)
1807 {
1808 RemoveItems(primID);
1809
1810 // repalce with current inventory details
1811 foreach (TaskInventoryItem newItem in items)
1812 {
1813// m_log.InfoFormat(
1814// "[REGION DB]: " +
1815// "Adding item {0}, {1} to prim ID {2}",
1816// newItem.Name, newItem.ItemID, newItem.ParentPartID);
1817
1818 DataRow newItemRow = m_itemsTable.NewRow();
1819 fillItemRow(newItemRow, newItem);
1820 m_itemsTable.Rows.Add(newItemRow);
1821 }
1822 }
1823
1824 Commit();
1825 }
1826
1827 /***********************************************************************
1828 *
1829 * SQL Statement Creation Functions
1830 *
1831 * These functions create SQL statements for update, insert, and create.
1832 * They can probably be factored later to have a db independant
1833 * portion and a db specific portion
1834 *
1835 **********************************************************************/
1836
1837 /// <summary>
1838 /// Create a MySQL insert command
1839 /// </summary>
1840 /// <param name="table"></param>
1841 /// <param name="dt"></param>
1842 /// <returns></returns>
1843 /// <remarks>
1844 /// This is subtle enough to deserve some commentary.
1845 /// Instead of doing *lots* and *lots of hardcoded strings
1846 /// for database definitions we'll use the fact that
1847 /// realistically all insert statements look like "insert
1848 /// into A(b, c) values(:b, :c) on the parameterized query
1849 /// front. If we just have a list of b, c, etc... we can
1850 /// generate these strings instead of typing them out.
1851 /// </remarks>
1852 private static MySqlCommand createInsertCommand(string table, DataTable dt)
1853 {
1854
1855 string[] cols = new string[dt.Columns.Count];
1856 for (int i = 0; i < dt.Columns.Count; i++)
1857 {
1858 DataColumn col = dt.Columns[i];
1859 cols[i] = col.ColumnName;
1860 }
1861
1862 string sql = "insert into " + table + "(";
1863 sql += String.Join(", ", cols);
1864 // important, the first ':' needs to be here, the rest get added in the join
1865 sql += ") values (?";
1866 sql += String.Join(", ?", cols);
1867 sql += ")";
1868 MySqlCommand cmd = new MySqlCommand(sql);
1869
1870 // this provides the binding for all our parameters, so
1871 // much less code than it used to be
1872 foreach (DataColumn col in dt.Columns)
1873 {
1874 cmd.Parameters.Add(createMySqlParameter(col.ColumnName, col.DataType));
1875 }
1876 return cmd;
1877 }
1878
1879 /// <summary>
1880 /// Create a MySQL update command
1881 /// </summary>
1882 /// <param name="table"></param>
1883 /// <param name="pk"></param>
1884 /// <param name="dt"></param>
1885 /// <returns></returns>
1886 private static MySqlCommand createUpdateCommand(string table, string pk, DataTable dt)
1887 {
1888 string sql = "update " + table + " set ";
1889 string subsql = String.Empty;
1890 foreach (DataColumn col in dt.Columns)
1891 {
1892 if (subsql.Length > 0)
1893 {
1894 // a map function would rock so much here
1895 subsql += ", ";
1896 }
1897 subsql += col.ColumnName + "=?" + col.ColumnName;
1898 }
1899 sql += subsql;
1900 sql += " where " + pk;
1901 MySqlCommand cmd = new MySqlCommand(sql);
1902
1903 // this provides the binding for all our parameters, so
1904 // much less code than it used to be
1905
1906 foreach (DataColumn col in dt.Columns)
1907 {
1908 cmd.Parameters.Add(createMySqlParameter(col.ColumnName, col.DataType));
1909 }
1910 return cmd;
1911 }
1912
1913 /// <summary>
1914 ///
1915 /// </summary>
1916 /// <param name="dt"></param>
1917 /// <returns></returns>
1918 // private static string defineTable(DataTable dt)
1919 // {
1920 // string sql = "create table " + dt.TableName + "(";
1921 // string subsql = String.Empty;
1922 // foreach (DataColumn col in dt.Columns)
1923 // {
1924 // if (subsql.Length > 0)
1925 // {
1926 // // a map function would rock so much here
1927 // subsql += ",\n";
1928 // }
1929 // subsql += col.ColumnName + " " + MySqlType(col.DataType);
1930 // if (dt.PrimaryKey.Length > 0 && col == dt.PrimaryKey[0])
1931 // {
1932 // subsql += " primary key";
1933 // }
1934 // }
1935 // sql += subsql;
1936 // sql += ")";
1937
1938 // //m_log.InfoFormat("[DATASTORE]: defineTable() sql {0}", sql);
1939
1940 // return sql;
1941 // }
1942
1943 /***********************************************************************
1944 *
1945 * Database Binding functions
1946 *
1947 * These will be db specific due to typing, and minor differences
1948 * in databases.
1949 *
1950 **********************************************************************/
1951
1952 ///<summary>
1953 /// <para>This is a convenience function that collapses 5 repetitive
1954 /// lines for defining MySqlParameters to 2 parameters:
1955 /// column name and database type.
1956 /// </para>
1957 /// <para>
1958 /// It assumes certain conventions like ?param as the param
1959 /// name to replace in parametrized queries, and that source
1960 /// version is always current version, both of which are fine
1961 /// for us.
1962 /// </para>
1963 /// </summary>
1964 /// <returns>a built MySql parameter</returns>
1965 private static MySqlParameter createMySqlParameter(string name, Type type)
1966 {
1967 MySqlParameter param = new MySqlParameter();
1968 param.ParameterName = "?" + name;
1969 param.DbType = dbtypeFromType(type);
1970 param.SourceColumn = name;
1971 param.SourceVersion = DataRowVersion.Current;
1972 return param;
1973 }
1974
1975 /// <summary>
1976 ///
1977 /// </summary>
1978 /// <param name="da"></param>
1979 /// <param name="conn"></param>
1980 private void SetupPrimCommands(MySqlDataAdapter da, MySqlConnection conn)
1981 {
1982 MySqlCommand insertCommand = createInsertCommand("prims", m_primTable);
1983 insertCommand.Connection = conn;
1984 da.InsertCommand = insertCommand;
1985
1986 MySqlCommand updateCommand = createUpdateCommand("prims", "UUID=?UUID", m_primTable);
1987 updateCommand.Connection = conn;
1988 da.UpdateCommand = updateCommand;
1989
1990 MySqlCommand delete = new MySqlCommand("delete from prims where UUID=?UUID");
1991 delete.Parameters.Add(createMySqlParameter("UUID", typeof (String)));
1992 delete.Connection = conn;
1993 da.DeleteCommand = delete;
1994 }
1995
1996 /// <summary>
1997 ///
1998 /// </summary>
1999 /// <param name="da"></param>
2000 /// <param name="conn"></param>
2001 private void SetupItemsCommands(MySqlDataAdapter da, MySqlConnection conn)
2002 {
2003 da.InsertCommand = createInsertCommand("primitems", m_itemsTable);
2004 da.InsertCommand.Connection = conn;
2005
2006 da.UpdateCommand = createUpdateCommand("primitems", "itemID = ?itemID", m_itemsTable);
2007 da.UpdateCommand.Connection = conn;
2008
2009 MySqlCommand delete = new MySqlCommand("delete from primitems where itemID = ?itemID");
2010 delete.Parameters.Add(createMySqlParameter("itemID", typeof (String)));
2011 delete.Connection = conn;
2012 da.DeleteCommand = delete;
2013 }
2014
2015 private void SetupRegionSettingsCommands(MySqlDataAdapter da, MySqlConnection conn)
2016 {
2017 da.InsertCommand = createInsertCommand("regionsettings", m_regionSettingsTable);
2018 da.InsertCommand.Connection = conn;
2019
2020 da.UpdateCommand = createUpdateCommand("regionsettings", "regionUUID = ?regionUUID", m_regionSettingsTable);
2021 da.UpdateCommand.Connection = conn;
2022
2023 MySqlCommand delete = new MySqlCommand("delete from regionsettings where regionUUID = ?regionUUID");
2024 delete.Parameters.Add(createMySqlParameter("regionUUID", typeof(String)));
2025 delete.Connection = conn;
2026 da.DeleteCommand = delete;
2027 }
2028
2029 /// <summary>
2030 ///
2031 /// </summary>
2032 /// <param name="da"></param>
2033 /// <param name="conn"></param>
2034 private void SetupTerrainCommands(MySqlDataAdapter da, MySqlConnection conn)
2035 {
2036 da.InsertCommand = createInsertCommand("terrain", m_dataSet.Tables["terrain"]);
2037 da.InsertCommand.Connection = conn;
2038 }
2039
2040 /// <summary>
2041 ///
2042 /// </summary>
2043 /// <param name="da"></param>
2044 /// <param name="conn"></param>
2045 private void setupLandCommands(MySqlDataAdapter da, MySqlConnection conn)
2046 {
2047 da.InsertCommand = createInsertCommand("land", m_dataSet.Tables["land"]);
2048 da.InsertCommand.Connection = conn;
2049
2050 da.UpdateCommand = createUpdateCommand("land", "UUID=?UUID", m_dataSet.Tables["land"]);
2051 da.UpdateCommand.Connection = conn;
2052 }
2053
2054 /// <summary>
2055 ///
2056 /// </summary>
2057 /// <param name="da"></param>
2058 /// <param name="conn"></param>
2059 private void setupLandAccessCommands(MySqlDataAdapter da, MySqlConnection conn)
2060 {
2061 da.InsertCommand = createInsertCommand("landaccesslist", m_dataSet.Tables["landaccesslist"]);
2062 da.InsertCommand.Connection = conn;
2063 }
2064
2065 /// <summary>
2066 ///
2067 /// </summary>
2068 /// <param name="da"></param>
2069 /// <param name="conn"></param>
2070 private void SetupShapeCommands(MySqlDataAdapter da, MySqlConnection conn)
2071 {
2072 da.InsertCommand = createInsertCommand("primshapes", m_dataSet.Tables["primshapes"]);
2073 da.InsertCommand.Connection = conn;
2074
2075 da.UpdateCommand = createUpdateCommand("primshapes", "UUID=?UUID", m_dataSet.Tables["primshapes"]);
2076 da.UpdateCommand.Connection = conn;
2077
2078 MySqlCommand delete = new MySqlCommand("delete from primshapes where UUID = ?UUID");
2079 delete.Parameters.Add(createMySqlParameter("UUID", typeof (String)));
2080 delete.Connection = conn;
2081 da.DeleteCommand = delete;
2082 }
2083
2084 /// <summary>
2085 ///
2086 /// </summary>
2087 /// <param name="conn">MySQL connection handler</param>
2088 // private static void InitDB(MySqlConnection conn)
2089 // {
2090 // string createPrims = defineTable(createPrimTable());
2091 // string createShapes = defineTable(createShapeTable());
2092 // string createItems = defineTable(createItemsTable());
2093 // string createTerrain = defineTable(createTerrainTable());
2094
2095 // // Land table is created from the Versionable Test Table routine now.
2096 // //string createLand = defineTable(createLandTable());
2097 // string createLandAccessList = defineTable(createLandAccessListTable());
2098
2099 // MySqlCommand pcmd = new MySqlCommand(createPrims, conn);
2100 // MySqlCommand scmd = new MySqlCommand(createShapes, conn);
2101 // MySqlCommand icmd = new MySqlCommand(createItems, conn);
2102 // MySqlCommand tcmd = new MySqlCommand(createTerrain, conn);
2103 // //MySqlCommand lcmd = new MySqlCommand(createLand, conn);
2104 // MySqlCommand lalcmd = new MySqlCommand(createLandAccessList, conn);
2105
2106 // if (conn.State != ConnectionState.Open)
2107 // {
2108 // try
2109 // {
2110 // conn.Open();
2111 // }
2112 // catch (Exception ex)
2113 // {
2114 // m_log.Error("[REGION DB]: Error connecting to MySQL server: " + ex.Message);
2115 // m_log.Error("[REGION DB]: Application is terminating!");
2116 // Thread.CurrentThread.Abort();
2117 // }
2118 // }
2119
2120 // try
2121 // {
2122 // pcmd.ExecuteNonQuery();
2123 // }
2124 // catch (MySqlException e)
2125 // {
2126 // m_log.WarnFormat("[REGION DB]: Primitives Table Already Exists: {0}", e);
2127 // }
2128
2129 // try
2130 // {
2131 // scmd.ExecuteNonQuery();
2132 // }
2133 // catch (MySqlException e)
2134 // {
2135 // m_log.WarnFormat("[REGION DB]: Shapes Table Already Exists: {0}", e);
2136 // }
2137
2138 // try
2139 // {
2140 // icmd.ExecuteNonQuery();
2141 // }
2142 // catch (MySqlException e)
2143 // {
2144 // m_log.WarnFormat("[REGION DB]: Items Table Already Exists: {0}", e);
2145 // }
2146
2147 // try
2148 // {
2149 // tcmd.ExecuteNonQuery();
2150 // }
2151 // catch (MySqlException e)
2152 // {
2153 // m_log.WarnFormat("[REGION DB]: Terrain Table Already Exists: {0}", e);
2154 // }
2155
2156 // //try
2157 // //{
2158 // //lcmd.ExecuteNonQuery();
2159 // //}
2160 // //catch (MySqlException e)
2161 // //{
2162 // //m_log.WarnFormat("[MySql]: Land Table Already Exists: {0}", e);
2163 // //}
2164
2165 // try
2166 // {
2167 // lalcmd.ExecuteNonQuery();
2168 // }
2169 // catch (MySqlException e)
2170 // {
2171 // m_log.WarnFormat("[REGION DB]: LandAccessList Table Already Exists: {0}", e);
2172 // }
2173 // conn.Close();
2174 // }
2175
2176 /// <summary>
2177 ///
2178 /// </summary>
2179 /// <param name="conn"></param>
2180 /// <param name="m"></param>
2181 /// <returns></returns>
2182 private bool TestTables(MySqlConnection conn, Migration m)
2183 {
2184 // we already have migrations, get out of here
2185 if (m.Version > 0)
2186 return false;
2187
2188 MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, conn);
2189 MySqlDataAdapter pDa = new MySqlDataAdapter(primSelectCmd);
2190 MySqlCommand shapeSelectCmd = new MySqlCommand(m_shapeSelect, conn);
2191 MySqlDataAdapter sDa = new MySqlDataAdapter(shapeSelectCmd);
2192 MySqlCommand itemsSelectCmd = new MySqlCommand(m_itemsSelect, conn);
2193 MySqlDataAdapter iDa = new MySqlDataAdapter(itemsSelectCmd);
2194 MySqlCommand terrainSelectCmd = new MySqlCommand(m_terrainSelect, conn);
2195 MySqlDataAdapter tDa = new MySqlDataAdapter(terrainSelectCmd);
2196 MySqlCommand landSelectCmd = new MySqlCommand(m_landSelect, conn);
2197 MySqlDataAdapter lDa = new MySqlDataAdapter(landSelectCmd);
2198 MySqlCommand landAccessListSelectCmd = new MySqlCommand(m_landAccessListSelect, conn);
2199 MySqlDataAdapter lalDa = new MySqlDataAdapter(landAccessListSelectCmd);
2200
2201 DataSet tmpDS = new DataSet();
2202 try
2203 {
2204 pDa.Fill(tmpDS, "prims");
2205 sDa.Fill(tmpDS, "primshapes");
2206
2207 iDa.Fill(tmpDS, "primitems");
2208
2209 tDa.Fill(tmpDS, "terrain");
2210 lDa.Fill(tmpDS, "land");
2211 lalDa.Fill(tmpDS, "landaccesslist");
2212 }
2213 catch (MySqlException)
2214 {
2215 m_log.Info("[DATASTORE]: MySql Database doesn't exist... creating");
2216 return false;
2217 }
2218
2219 // we have tables, but not a migration model yet
2220 if (m.Version == 0)
2221 m.Version = 1;
2222
2223 return true;
2224
2225 // pDa.Fill(tmpDS, "prims");
2226 // sDa.Fill(tmpDS, "primshapes");
2227
2228 // iDa.Fill(tmpDS, "primitems");
2229
2230 // tDa.Fill(tmpDS, "terrain");
2231 // lDa.Fill(tmpDS, "land");
2232 // lalDa.Fill(tmpDS, "landaccesslist");
2233
2234 // foreach (DataColumn col in createPrimTable().Columns)
2235 // {
2236 // if (!tmpDS.Tables["prims"].Columns.Contains(col.ColumnName))
2237 // {
2238 // m_log.Info("[REGION DB]: Missing required column:" + col.ColumnName);
2239 // return false;
2240 // }
2241 // }
2242
2243 // foreach (DataColumn col in createShapeTable().Columns)
2244 // {
2245 // if (!tmpDS.Tables["primshapes"].Columns.Contains(col.ColumnName))
2246 // {
2247 // m_log.Info("[REGION DB]: Missing required column:" + col.ColumnName);
2248 // return false;
2249 // }
2250 // }
2251
2252 // // XXX primitems should probably go here eventually
2253
2254 // foreach (DataColumn col in createTerrainTable().Columns)
2255 // {
2256 // if (!tmpDS.Tables["terrain"].Columns.Contains(col.ColumnName))
2257 // {
2258 // m_log.Info("[REGION DB]: Missing require column:" + col.ColumnName);
2259 // return false;
2260 // }
2261 // }
2262
2263 // foreach (DataColumn col in createLandTable().Columns)
2264 // {
2265 // if (!tmpDS.Tables["land"].Columns.Contains(col.ColumnName))
2266 // {
2267 // m_log.Info("[REGION DB]: Missing require column:" + col.ColumnName);
2268 // return false;
2269 // }
2270 // }
2271
2272 // foreach (DataColumn col in createLandAccessListTable().Columns)
2273 // {
2274 // if (!tmpDS.Tables["landaccesslist"].Columns.Contains(col.ColumnName))
2275 // {
2276 // m_log.Info("[DATASTORE]: Missing require column:" + col.ColumnName);
2277 // return false;
2278 // }
2279 // }
2280
2281 // return true;
2282 }
2283
2284 /***********************************************************************
2285 *
2286 * Type conversion functions
2287 *
2288 **********************************************************************/
2289
2290 /// <summary>
2291 /// Type conversion functions
2292 /// </summary>
2293 /// <param name="type"></param>
2294 /// <returns></returns>
2295 private static DbType dbtypeFromType(Type type)
2296 {
2297 if (type == typeof (String))
2298 {
2299 return DbType.String;
2300 }
2301 else if (type == typeof (Int32))
2302 {
2303 return DbType.Int32;
2304 }
2305 else if (type == typeof (Double))
2306 {
2307 return DbType.Double;
2308 }
2309 else if (type == typeof (Byte))
2310 {
2311 return DbType.Byte;
2312 }
2313 else if (type == typeof (Double))
2314 {
2315 return DbType.Double;
2316 }
2317 else if (type == typeof (Byte[]))
2318 {
2319 return DbType.Binary;
2320 }
2321 else
2322 {
2323 return DbType.String;
2324 }
2325 }
2326
2327 /// <summary>
2328 /// </summary>
2329 /// <param name="type"></param>
2330 /// <returns></returns>
2331 /// <remarks>this is something we'll need to implement for each db slightly differently.</remarks>
2332 // private static string MySqlType(Type type)
2333 // {
2334 // if (type == typeof (String))
2335 // {
2336 // return "varchar(255)";
2337 // }
2338 // else if (type == typeof (Int32))
2339 // {
2340 // return "integer";
2341 // }
2342 // else if (type == typeof (Int64))
2343 // {
2344 // return "bigint";
2345 // }
2346 // else if (type == typeof (Double))
2347 // {
2348 // return "float";
2349 // }
2350 // else if (type == typeof (Byte[]))
2351 // {
2352 // return "longblob";
2353 // }
2354 // else
2355 // {
2356 // return "string";
2357 // }
2358 // }
2359 }
2360}