diff options
author | Justin Clarke Casey | 2008-08-16 20:50:43 +0000 |
---|---|---|
committer | Justin Clarke Casey | 2008-08-16 20:50:43 +0000 |
commit | d7f2c454f1f00f3d3d98b880a9c7cd18181112ae (patch) | |
tree | aa9935b51c62af8717597e5dfd035143544d8a4a /OpenSim/Data/MySQL/MySQLDataStore.cs | |
parent | * Move GridInfoService into Framework.Communications and eliminate Common.Com... (diff) | |
download | opensim-SC-d7f2c454f1f00f3d3d98b880a9c7cd18181112ae.zip opensim-SC-d7f2c454f1f00f3d3d98b880a9c7cd18181112ae.tar.gz opensim-SC-d7f2c454f1f00f3d3d98b880a9c7cd18181112ae.tar.bz2 opensim-SC-d7f2c454f1f00f3d3d98b880a9c7cd18181112ae.tar.xz |
* Rename *DataStore classes to *RegionData to align with SQLite
Diffstat (limited to 'OpenSim/Data/MySQL/MySQLDataStore.cs')
-rw-r--r-- | OpenSim/Data/MySQL/MySQLDataStore.cs | 2360 |
1 files changed, 0 insertions, 2360 deletions
diff --git a/OpenSim/Data/MySQL/MySQLDataStore.cs b/OpenSim/Data/MySQL/MySQLDataStore.cs deleted file mode 100644 index 70b6d3c..0000000 --- a/OpenSim/Data/MySQL/MySQLDataStore.cs +++ /dev/null | |||
@@ -1,2360 +0,0 @@ | |||
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Data; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Threading; | ||
34 | using libsecondlife; | ||
35 | using log4net; | ||
36 | using MySql.Data.MySqlClient; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Region.Environment.Interfaces; | ||
39 | using OpenSim.Region.Environment.Scenes; | ||
40 | |||
41 | namespace 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 | } | ||