database.vala 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750
  1. /*
  2. * Copyright (c) 2012-2025 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. public enum PropertyType
  8. {
  9. NULL,
  10. BOOL,
  11. DOUBLE,
  12. STRING,
  13. GUID,
  14. VECTOR3,
  15. QUATERNION,
  16. OBJECTS_SET
  17. }
  18. public enum PropertyEditorType
  19. {
  20. DEFAULT, ///< Default editor for the property type.
  21. ENUM, ///< A string selected from a list.
  22. RESOURCE, ///< A resource name selected from a project.
  23. ANGLE, ///< An angle value displayed in degrees.
  24. COLOR, ///< An RGB color from a color picker.
  25. }
  26. public delegate void EnumCallback(InputField enum_property, InputEnum property, Project project);
  27. public delegate void ResourceCallback(InputField enum_property, InputResource property, Project project);
  28. public struct PropertyDefinition
  29. {
  30. public PropertyType type;
  31. public string name;
  32. public string? label;
  33. public PropertyEditorType editor;
  34. public Value? min;
  35. public Value? max;
  36. public Value? deffault;
  37. public string[] enum_values;
  38. public string[] enum_labels;
  39. public string? enum_property;
  40. public unowned EnumCallback? enum_callback;
  41. public unowned ResourceCallback? resource_callback;
  42. public string? resource_type;
  43. public StringId64 object_type;
  44. public bool hidden;
  45. }
  46. public enum UndoRedoAction
  47. {
  48. RESTORE_POINT = int.MAX;
  49. }
  50. public struct RestorePointHeader
  51. {
  52. public uint32 id;
  53. public uint32 flags;
  54. public uint32 size;
  55. public uint32 num_guids;
  56. }
  57. public struct RestorePoint
  58. {
  59. public RestorePointHeader header;
  60. public Guid?[] data;
  61. }
  62. public class Stack
  63. {
  64. public uint32 _capacity;
  65. public uint8[] _data;
  66. public uint32 _read; // Position of the read/write head.
  67. public uint32 _size; // Size of the data written in the stack.
  68. public uint32 _last_write_restore_point_size; // Size when write_restore_point() was last called.
  69. ///
  70. public Stack(uint32 capacity)
  71. {
  72. assert(capacity > 0);
  73. _capacity = capacity;
  74. _data = new uint8[_capacity];
  75. clear();
  76. }
  77. ///
  78. public uint32 size()
  79. {
  80. return _size;
  81. }
  82. ///
  83. public void clear()
  84. {
  85. _read = 0;
  86. _size = 0;
  87. _last_write_restore_point_size = 0;
  88. }
  89. // Copies @a data into @a destination.
  90. public void copy_data(uint8* destination, void* data, ulong len)
  91. {
  92. uint8* source = (uint8*)data;
  93. for (ulong ii = 0; ii < len; ++ii)
  94. destination[ii] = source[ii];
  95. }
  96. // Writes @a data into the current page.
  97. public void write_data_internal(uint8* data, uint32 len)
  98. {
  99. assert(data != null);
  100. uint32 bytes_left = len;
  101. uint32 bytes_avail;
  102. // Write the data that wraps around.
  103. while (bytes_left > (bytes_avail = _capacity - _read)) {
  104. copy_data(&_data[_read]
  105. , ((uint8*)data) + (len - bytes_left)
  106. , bytes_avail
  107. );
  108. _read = (_read + bytes_avail) % _capacity;
  109. _size = uint32.min(_capacity, _size + bytes_avail);
  110. bytes_left -= bytes_avail;
  111. }
  112. // Write the remaining data.
  113. copy_data(&_data[_read]
  114. , ((uint8*)data) + (len - bytes_left)
  115. , bytes_left
  116. );
  117. _read += bytes_left;
  118. _size = uint32.min(_capacity, _size + bytes_left);
  119. _last_write_restore_point_size += len;
  120. }
  121. // Wrapper to avoid casting sizeof() manually.
  122. public void write_data(void* data, ulong len)
  123. {
  124. write_data_internal((uint8*)data, (uint32)len);
  125. }
  126. public void read_data_internal(uint8* data, uint32 len)
  127. {
  128. assert(data != null);
  129. uint32 bytes_left = len;
  130. // Read the data that wraps around.
  131. while (bytes_left > _read) {
  132. copy_data(data + bytes_left - _read
  133. , &_data[0]
  134. , _read
  135. );
  136. bytes_left -= _read;
  137. _size -= _read;
  138. assert(_size <= _capacity);
  139. _read = _capacity;
  140. }
  141. // Read the remaining data.
  142. copy_data(data
  143. , &_data[_read - bytes_left]
  144. , bytes_left
  145. );
  146. _read -= bytes_left;
  147. _size -= bytes_left;
  148. assert(_size <= _capacity);
  149. }
  150. // Wrapper to avoid casting sizeof() manually.
  151. public void read_data(void* data, ulong size)
  152. {
  153. read_data_internal((uint8*)data, (uint32)size);
  154. }
  155. public void write_bool(bool a)
  156. {
  157. write_data(&a, sizeof(bool));
  158. }
  159. public void write_uint32(uint32 a)
  160. {
  161. write_data(&a, sizeof(uint32));
  162. }
  163. public void write_double(double a)
  164. {
  165. write_data(&a, sizeof(double));
  166. }
  167. public void write_string(string str)
  168. {
  169. uint32 len = str.length;
  170. write_data(&str.data[0], len);
  171. write_data(&len, sizeof(uint32));
  172. }
  173. public void write_guid(Guid a)
  174. {
  175. write_data(&a, sizeof(Guid));
  176. }
  177. public void write_vector3(Vector3 a)
  178. {
  179. write_data(&a, sizeof(Vector3));
  180. }
  181. public void write_quaternion(Quaternion a)
  182. {
  183. write_data(&a, sizeof(Quaternion));
  184. }
  185. public bool read_bool()
  186. {
  187. bool a = false;
  188. read_data(&a, sizeof(bool));
  189. return a;
  190. }
  191. public int read_int()
  192. {
  193. int a = 0;
  194. read_data(&a, sizeof(int));
  195. return a;
  196. }
  197. public uint32 read_uint32()
  198. {
  199. uint32 a = 0;
  200. read_data(&a, sizeof(uint32));
  201. return a;
  202. }
  203. public double read_double()
  204. {
  205. double a = 0;
  206. read_data(&a, sizeof(double));
  207. return a;
  208. }
  209. public Guid read_guid()
  210. {
  211. Guid a = GUID_ZERO;
  212. read_data(&a, sizeof(Guid));
  213. return a;
  214. }
  215. public Vector3 read_vector3()
  216. {
  217. Vector3 a = VECTOR3_ZERO;
  218. read_data(&a, sizeof(Vector3));
  219. return a;
  220. }
  221. public Quaternion read_quaternion()
  222. {
  223. Quaternion a = QUATERNION_IDENTITY;
  224. read_data(&a, sizeof(Quaternion));
  225. return a;
  226. }
  227. public string read_string()
  228. {
  229. uint32 len = 0;
  230. read_data(&len, sizeof(uint32));
  231. uint8[] str = new uint8[len + 1];
  232. read_data(str, len);
  233. str[len] = '\0';
  234. return (string)str;
  235. }
  236. public void write_create_action(uint32 action, Guid id, string type)
  237. {
  238. write_string(type);
  239. write_guid(id);
  240. write_uint32(action);
  241. }
  242. public void write_destroy_action(uint32 action, Guid id, string type)
  243. {
  244. write_string(type);
  245. write_guid(id);
  246. write_uint32(action);
  247. }
  248. public void write_set_property_null_action(uint32 action, Guid id, string key)
  249. {
  250. // No value to push
  251. write_string(key);
  252. write_guid(id);
  253. write_uint32(action);
  254. }
  255. public void write_set_property_bool_action(uint32 action, Guid id, string key, bool val)
  256. {
  257. write_bool(val);
  258. write_string(key);
  259. write_guid(id);
  260. write_uint32(action);
  261. }
  262. public void write_set_property_double_action(uint32 action, Guid id, string key, double val)
  263. {
  264. write_double(val);
  265. write_string(key);
  266. write_guid(id);
  267. write_uint32(action);
  268. }
  269. public void write_set_property_string_action(uint32 action, Guid id, string key, string val)
  270. {
  271. write_string(val);
  272. write_string(key);
  273. write_guid(id);
  274. write_uint32(action);
  275. }
  276. public void write_set_property_guid_action(uint32 action, Guid id, string key, Guid val)
  277. {
  278. write_guid(val);
  279. write_string(key);
  280. write_guid(id);
  281. write_uint32(action);
  282. }
  283. public void write_set_property_vector3_action(uint32 action, Guid id, string key, Vector3 val)
  284. {
  285. write_vector3(val);
  286. write_string(key);
  287. write_guid(id);
  288. write_uint32(action);
  289. }
  290. public void write_set_property_quaternion_action(uint32 action, Guid id, string key, Quaternion val)
  291. {
  292. write_quaternion(val);
  293. write_string(key);
  294. write_guid(id);
  295. write_uint32(action);
  296. }
  297. public void write_add_to_set_action(uint32 action, Guid id, string key, Guid item_id)
  298. {
  299. write_guid(item_id);
  300. write_string(key);
  301. write_guid(id);
  302. write_uint32(action);
  303. }
  304. public void write_remove_from_set_action(uint32 action, Guid id, string key, Guid item_id)
  305. {
  306. write_guid(item_id);
  307. write_string(key);
  308. write_guid(id);
  309. write_uint32(action);
  310. }
  311. public void write_restore_point(uint32 id, uint32 flags, Guid?[] data)
  312. {
  313. uint32 size = _last_write_restore_point_size;
  314. uint32 num_guids = data.length;
  315. for (uint32 i = 0; i < num_guids; ++i)
  316. write_guid(data[num_guids - 1 - i]);
  317. write_uint32(num_guids);
  318. write_uint32(size);
  319. write_uint32(flags);
  320. write_uint32(id);
  321. write_uint32(UndoRedoAction.RESTORE_POINT);
  322. _last_write_restore_point_size = 0;
  323. }
  324. public RestorePoint read_restore_point()
  325. {
  326. uint32 t = read_uint32();
  327. assert(t == UndoRedoAction.RESTORE_POINT);
  328. uint32 id = read_uint32();
  329. uint32 flags = read_uint32();
  330. uint32 size = read_uint32();
  331. uint32 num_guids = read_uint32();
  332. Guid?[] ids = new Guid?[num_guids];
  333. for (uint32 i = 0; i < num_guids; ++i)
  334. ids[i] = read_guid();
  335. RestorePointHeader rph = { id, flags, size, num_guids };
  336. return { rph, ids };
  337. }
  338. }
  339. public class UndoRedo
  340. {
  341. public Stack _undo;
  342. public Stack _redo;
  343. ///
  344. public UndoRedo(uint32 undo_redo_size = 0)
  345. {
  346. uint32 size = uint32.max(1024, undo_redo_size);
  347. _undo = new Stack(size);
  348. _redo = new Stack(size);
  349. }
  350. public void reset()
  351. {
  352. _undo.clear();
  353. _redo.clear();
  354. }
  355. }
  356. const string OBJECT_NAME_UNNAMED = "Unnamed";
  357. public enum ObjectTypeFlags
  358. {
  359. NONE = 0,
  360. UNIT_COMPONENT = 1 << 0,
  361. }
  362. public class Database
  363. {
  364. private static bool _debug = false;
  365. private static bool _debug_getters = false;
  366. private enum Action
  367. {
  368. CREATE,
  369. DESTROY,
  370. SET_PROPERTY_NULL,
  371. SET_PROPERTY_BOOL,
  372. SET_PROPERTY_DOUBLE,
  373. SET_PROPERTY_STRING,
  374. SET_PROPERTY_GUID,
  375. SET_PROPERTY_VECTOR3,
  376. SET_PROPERTY_QUATERNION,
  377. ADD_TO_SET,
  378. REMOVE_FROM_SET
  379. }
  380. private struct PropertiesSlice
  381. {
  382. int start; // Index of first property.
  383. int end; // Index of last property + 1.
  384. }
  385. private struct ObjectTypeInfo
  386. {
  387. PropertiesSlice properties;
  388. string name;
  389. ObjectTypeFlags flags;
  390. }
  391. // Data
  392. private PropertyDefinition[] _property_definitions;
  393. private Gee.HashMap<StringId64?, ObjectTypeInfo?> _object_definitions;
  394. private Gee.HashMap<Guid?, Gee.HashMap<string, Value?>> _data;
  395. private UndoRedo? _undo_redo;
  396. public Project _project;
  397. // The number of changes to the database since the last successful state
  398. // synchronization (load(), save() etc.). If it is less than 0, the changes
  399. // came from undo(), otherwise they came from redo() or from regular calls to
  400. // create(), destroy(), set_*() etc. A value of 0 means there were no changes.
  401. public int _distance_from_last_sync;
  402. // Signals
  403. public signal void undo_redo(bool undo, uint32 id, Guid?[] data);
  404. public signal void restore_point_added(int id, Guid?[] data, uint32 flags);
  405. public Database(Project project, UndoRedo? undo_redo = null)
  406. {
  407. _property_definitions = new PropertyDefinition[0];
  408. _object_definitions = new Gee.HashMap<StringId64?, ObjectTypeInfo?>(StringId64.hash_func, StringId64.equal_func);
  409. _data = new Gee.HashMap<Guid?, Gee.HashMap<string, Value?>>(Guid.hash_func, Guid.equal_func);
  410. _project = project;
  411. _undo_redo = undo_redo;
  412. reset();
  413. }
  414. /// Resets database to clean state.
  415. public void reset()
  416. {
  417. _data.clear();
  418. if (_undo_redo != null)
  419. _undo_redo.reset();
  420. _distance_from_last_sync = 0;
  421. // This is a special field which stores all objects
  422. _data[GUID_ZERO] = new Gee.HashMap<string, Value?>();
  423. }
  424. /// Returns whether the database has been changed since last call to Save().
  425. public bool changed()
  426. {
  427. return _distance_from_last_sync != 0;
  428. }
  429. /// Saves database to path without marking it as not changed.
  430. public int dump(string path, Guid id)
  431. {
  432. try {
  433. Hashtable json = encode(id);
  434. SJSON.save(json, path);
  435. return 0;
  436. } catch (JsonWriteError e) {
  437. return -1;
  438. }
  439. }
  440. /// Saves database to path.
  441. public int save(string path, Guid id)
  442. {
  443. int err = dump(path, id);
  444. if (err == 0)
  445. _distance_from_last_sync = 0;
  446. return err;
  447. }
  448. // See: add_from_path().
  449. public int add_from_file(out Guid object_id, FileStream? fs, string resource_path)
  450. {
  451. try {
  452. Hashtable json = SJSON.load_from_file(fs);
  453. // Parse the object's ID or generate a new one if none is found.
  454. if (json.has_key("id"))
  455. object_id = Guid.parse((string)json["id"]);
  456. else if (json.has_key("_guid"))
  457. object_id = Guid.parse((string)json["_guid"]);
  458. else
  459. object_id = Guid.new_guid();
  460. create_internal(0, object_id);
  461. set_object_type(object_id, ResourceId.type(resource_path));
  462. decode_object(object_id, GUID_ZERO, "", json);
  463. // Create a mapping between the path and the object it has been loaded into.
  464. set_property_internal(0, GUID_ZERO, resource_path, object_id);
  465. return 0;
  466. } catch (JsonSyntaxError e) {
  467. object_id = GUID_ZERO;
  468. return -1;
  469. }
  470. }
  471. // Adds the object stored at @a path to the database.
  472. // This makes it possible to load multiple objects from distinct
  473. // paths in the same database. @a resource_path is used as a key in the
  474. // database to refer to the object that has been loaded. This is useful when
  475. // you do not have the object ID but only its path, as it is often the case
  476. // since resources use paths and not IDs to reference each other.
  477. public int add_from_path(out Guid object_id, string path, string resource_path)
  478. {
  479. object_id = GUID_ZERO;
  480. FileStream fs = FileStream.open(path, "rb");
  481. if (fs == null)
  482. return 1;
  483. return add_from_file(out object_id, fs, resource_path);
  484. }
  485. public int add_from_resource_path(out Guid object_id, string resource_path)
  486. {
  487. // If the resource is already loaded.
  488. if (has_property(GUID_ZERO, resource_path)) {
  489. object_id = get_property_guid(GUID_ZERO, resource_path);
  490. return 0;
  491. }
  492. string path = _project.absolute_path(resource_path);
  493. return add_from_path(out object_id, path, resource_path);
  494. }
  495. /// Loads the database with the object stored at @a path.
  496. public int load_from_file(out Guid object_id, FileStream fs, string resource_path)
  497. {
  498. reset();
  499. return add_from_file(out object_id, fs, resource_path);
  500. }
  501. /// Loads the database with the object stored at @a path.
  502. public int load_from_path(out Guid object_id, string path, string resource_path)
  503. {
  504. reset();
  505. return add_from_path(out object_id, path, resource_path);
  506. }
  507. /// Encodes the object @a id into SJSON object.
  508. private Hashtable encode(Guid id)
  509. {
  510. return encode_object(id, get_data(id));
  511. }
  512. private static bool is_valid_value(Value? value)
  513. {
  514. return value == null
  515. || value.holds(typeof(bool))
  516. || value.holds(typeof(double))
  517. || value.holds(typeof(string))
  518. || value.holds(typeof(Guid))
  519. || value.holds(typeof(Vector3))
  520. || value.holds(typeof(Quaternion))
  521. ;
  522. }
  523. private static bool is_valid_key(string key)
  524. {
  525. return key.length > 0
  526. && !key.has_prefix(".")
  527. && !key.has_suffix(".")
  528. ;
  529. }
  530. private static string value_to_string(Value? value)
  531. {
  532. if (value == null)
  533. return "null";
  534. if (value.holds(typeof(bool)))
  535. return ((bool)value).to_string();
  536. if (value.holds(typeof(double)))
  537. return ((double)value).to_string();
  538. if (value.holds(typeof(string)))
  539. return ((string)value).to_string();
  540. if (value.holds(typeof(Guid)))
  541. return ((Guid)value).to_string();
  542. if (value.holds(typeof(Vector3)))
  543. return ((Vector3)value).to_string();
  544. if (value.holds(typeof(Quaternion)))
  545. return ((Quaternion)value).to_string();
  546. if (value.holds(typeof(Gee.HashSet)))
  547. return "Set<Guid>";
  548. return "<invalid>";
  549. }
  550. private void decode_object(Guid id, Guid owner_id, string db_key, Hashtable json)
  551. {
  552. string old_db = db_key;
  553. string k = db_key;
  554. string[] keys = json.keys.to_array();
  555. foreach (string key in keys) {
  556. // ID is filled by decode_set().
  557. if (key == "id" || key == "_guid")
  558. continue;
  559. // The "type" key defines object type only if it appears
  560. // in the root of a JSON object (k == "").
  561. if (k == "") {
  562. if (key == "type" || key == "_type")
  563. set_object_type(id, (string)json[key]);
  564. }
  565. Value? val = json[key];
  566. k += k == "" ? key : ("." + key);
  567. if (val.holds(typeof(Hashtable))) {
  568. Hashtable ht = (Hashtable)val;
  569. decode_object(id, owner_id, k, ht);
  570. } else if (val.holds(typeof(Gee.ArrayList))) {
  571. Gee.ArrayList<Value?> arr = (Gee.ArrayList<Value?>)val;
  572. if (arr.size > 0
  573. && arr[0].holds(typeof(double))
  574. && k != "frames" // sprite_animation
  575. )
  576. set_property_internal(0, id, k, decode_value(val));
  577. else
  578. decode_set(id, key, arr);
  579. } else {
  580. set_property_internal(0, id, k, decode_value(val));
  581. }
  582. k = old_db;
  583. }
  584. }
  585. private void decode_set(Guid owner_id, string key, Gee.ArrayList<Value?> json)
  586. {
  587. // Set should be created even if it is empty.
  588. create_empty_set(owner_id, key);
  589. for (int i = 0; i < json.size; ++i) {
  590. Hashtable obj;
  591. string owner_type = object_type(owner_id);
  592. if (owner_type == OBJECT_TYPE_SPRITE_ANIMATION)
  593. obj = new Hashtable();
  594. else
  595. obj = (Hashtable)json[i];
  596. // Decode object ID.
  597. Guid obj_id;
  598. if (obj.has_key("id") && owner_type != OBJECT_TYPE_FONT)
  599. obj_id = Guid.parse((string)obj["id"]);
  600. else if (obj.has_key("_guid"))
  601. obj_id = Guid.parse((string)obj["_guid"]);
  602. else
  603. obj_id = Guid.new_guid();
  604. create_internal(0, obj_id);
  605. // Determine the object's type based on the type of its
  606. // parent and other heuristics.
  607. if (owner_type == OBJECT_TYPE_LEVEL) {
  608. if (key == "units")
  609. set_object_type(obj_id, OBJECT_TYPE_UNIT);
  610. else if (key == "sounds")
  611. set_object_type(obj_id, OBJECT_TYPE_SOUND_SOURCE);
  612. else
  613. set_object_type(obj_id, "undefined");
  614. } else if (owner_type == OBJECT_TYPE_STATE_MACHINE) {
  615. if (key == "states")
  616. set_object_type(obj_id, OBJECT_TYPE_STATE_MACHINE_NODE);
  617. else if (key == "variables")
  618. set_object_type(obj_id, OBJECT_TYPE_STATE_MACHINE_VARIABLE);
  619. else
  620. set_object_type(obj_id, "undefined");
  621. } else if (owner_type == OBJECT_TYPE_STATE_MACHINE_NODE) {
  622. if (key == "animations")
  623. set_object_type(obj_id, OBJECT_TYPE_NODE_ANIMATION);
  624. else if (key == "transitions")
  625. set_object_type(obj_id, OBJECT_TYPE_NODE_TRANSITION);
  626. } else if (owner_type == OBJECT_TYPE_SPRITE) {
  627. if (key == "frames") {
  628. set_object_type(obj_id, "sprite_frame");
  629. set_property_internal(0, obj_id, "index", (double)i);
  630. }
  631. } else if (owner_type == OBJECT_TYPE_SPRITE_ANIMATION) {
  632. if (key == "frames") {
  633. set_object_type(obj_id, "animation_frame");
  634. set_property_internal(0, obj_id, "index", (double)json[i]);
  635. }
  636. } else if (owner_type == OBJECT_TYPE_FONT) {
  637. if (key == "glyphs") {
  638. set_object_type(obj_id, "font_glyph");
  639. set_property_internal(0, obj_id, "cp", (double)obj["id"]);
  640. }
  641. }
  642. decode_object(obj_id, owner_id, "", obj);
  643. assert(has_property(obj_id, "_type"));
  644. add_to_set_internal(0, owner_id, key, obj_id);
  645. }
  646. }
  647. private Value? decode_value(Value? value)
  648. {
  649. if (value.holds(typeof(Gee.ArrayList))) {
  650. Gee.ArrayList<Value?> al = (Gee.ArrayList<Value?>)value;
  651. if (al.size == 1)
  652. return Vector3((double)al[0], 0.0, 0.0);
  653. else if (al.size == 2)
  654. return Vector3((double)al[0], (double)al[1], 0.0);
  655. else if (al.size == 3)
  656. return Vector3((double)al[0], (double)al[1], (double)al[2]);
  657. else if (al.size == 4)
  658. return Quaternion((double)al[0], (double)al[1], (double)al[2], (double)al[3]);
  659. else
  660. return Vector3(0.0, 0.0, 0.0);
  661. } else if (value.holds(typeof(string))) {
  662. Guid id;
  663. if (Guid.try_parse((string)value, out id))
  664. return id;
  665. return value;
  666. } else if (value == null
  667. || value.holds(typeof(bool))
  668. || value.holds(typeof(double))) {
  669. return value;
  670. } else {
  671. return null;
  672. }
  673. }
  674. private Hashtable encode_object(Guid id, Gee.HashMap<string, Value?> db)
  675. {
  676. Hashtable obj = new Hashtable();
  677. if (id != GUID_ZERO)
  678. obj["_guid"] = id.to_string();
  679. string[] keys = db.keys.to_array();
  680. foreach (string key in keys) {
  681. // Since null-key is equivalent to non-existent key, skip serialization.
  682. if (db[key] == null)
  683. continue;
  684. // Skip empty sets.
  685. if (db[key].holds(typeof(Gee.HashSet)) && ((Gee.HashSet)db[key]).size == 0)
  686. continue;
  687. string[] foo = key.split(".");
  688. Hashtable x = obj;
  689. if (foo.length > 1) {
  690. for (int i = 0; i < foo.length - 1; ++i) {
  691. string f = foo[i];
  692. if (x.has_key(f)) {
  693. x = (Hashtable)x[f];
  694. continue;
  695. }
  696. Hashtable y = new Hashtable();
  697. x.set(f, y);
  698. x = y;
  699. }
  700. }
  701. x.set(foo[foo.length - 1], encode_value(db[key]));
  702. }
  703. return obj;
  704. }
  705. private Value? encode_value(Value? value)
  706. {
  707. assert(is_valid_value(value) || value.holds(typeof(Gee.HashSet)));
  708. if (value.holds(typeof(Vector3))) {
  709. Vector3 v = (Vector3)value;
  710. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  711. arr.add(v.x);
  712. arr.add(v.y);
  713. arr.add(v.z);
  714. return arr;
  715. } else if (value.holds(typeof(Quaternion))) {
  716. Quaternion q = (Quaternion)value;
  717. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  718. arr.add(q.x);
  719. arr.add(q.y);
  720. arr.add(q.z);
  721. arr.add(q.w);
  722. return arr;
  723. } else if (value.holds(typeof(Guid))) {
  724. Guid id = (Guid)value;
  725. return id.to_string();
  726. } else if (value.holds(typeof(Gee.HashSet))) {
  727. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  728. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  729. foreach (Guid id in hs) {
  730. arr.add(encode_object(id, get_data(id)));
  731. }
  732. return arr;
  733. } else {
  734. return value;
  735. }
  736. }
  737. private Gee.HashMap<string, Value?> get_data(Guid id)
  738. {
  739. assert(has_object(id));
  740. return _data[id];
  741. }
  742. private void create_internal(int dir, Guid id)
  743. {
  744. assert(id != GUID_ZERO);
  745. if (_debug)
  746. logi("create %s".printf(id.to_string()));
  747. _data[id] = new Gee.HashMap<string, Value?>();
  748. _distance_from_last_sync += dir;
  749. }
  750. private void destroy_internal(int dir, Guid id)
  751. {
  752. assert(id != GUID_ZERO);
  753. assert(has_object(id));
  754. if (_debug)
  755. logi("destroy %s".printf(id.to_string()));
  756. _distance_from_last_sync += dir;
  757. }
  758. public void set_property_internal(int dir, Guid id, string key, Value? value)
  759. {
  760. assert(has_object(id));
  761. assert(is_valid_key(key));
  762. assert(is_valid_value(value));
  763. if (_debug)
  764. logi("set_property %s %s %s".printf(id.to_string(), key, value_to_string(value)));
  765. Gee.HashMap<string, Value?> ob = get_data(id);
  766. ob[key] = value;
  767. _distance_from_last_sync += dir;
  768. }
  769. public void create_empty_set(Guid id, string key)
  770. {
  771. assert(has_object(id));
  772. assert(is_valid_key(key));
  773. Gee.HashMap<string, Value?> ob = get_data(id);
  774. assert(!ob.has_key(key));
  775. ob[key] = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  776. }
  777. private void add_to_set_internal(int dir, Guid id, string key, Guid item_id)
  778. {
  779. assert(has_object(id));
  780. assert(is_valid_key(key));
  781. assert(item_id != GUID_ZERO);
  782. assert(has_object(item_id));
  783. if (_debug)
  784. logi("add_to_set %s %s %s".printf(id.to_string(), key, item_id.to_string()));
  785. Gee.HashMap<string, Value?> ob = get_data(id);
  786. if (!ob.has_key(key)) {
  787. Gee.HashSet<Guid?> hs = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  788. hs.add(item_id);
  789. ob[key] = hs;
  790. } else {
  791. ((Gee.HashSet<Guid?>)ob[key]).add(item_id);
  792. }
  793. _distance_from_last_sync += dir;
  794. }
  795. private void remove_from_set_internal(int dir, Guid id, string key, Guid item_id)
  796. {
  797. assert(has_object(id));
  798. assert(is_valid_key(key));
  799. assert(item_id != GUID_ZERO);
  800. if (_debug)
  801. logi("remove_from_set %s %s %s".printf(id.to_string(), key, item_id.to_string()));
  802. Gee.HashMap<string, Value?> ob = get_data(id);
  803. ((Gee.HashSet<Guid?>)ob[key]).remove(item_id);
  804. _distance_from_last_sync += dir;
  805. }
  806. // Returns the type of the object @a id.
  807. public string object_type(Guid id)
  808. {
  809. assert(has_object(id));
  810. if (id == GUID_ZERO)
  811. return "database";
  812. else
  813. return (string)get_data(id)["_type"];
  814. }
  815. // Sets the @a type of the object @a id.
  816. // This is called automatically when loading data or when new objects are created via create().
  817. // It can occasionally be called manually after loading legacy data with no type information
  818. // stored inside objects.
  819. public void set_object_type(Guid id, string type)
  820. {
  821. assert(has_object(id));
  822. get_data(id)["_type"] = type;
  823. }
  824. public void _init_object(Guid id, PropertyDefinition[] properties)
  825. {
  826. foreach (PropertyDefinition def in properties) {
  827. switch (def.type) {
  828. case PropertyType.BOOL:
  829. set_property_bool(id, def.name, (bool)def.deffault);
  830. break;
  831. case PropertyType.DOUBLE:
  832. set_property_double(id, def.name, (double)def.deffault);
  833. break;
  834. case PropertyType.STRING:
  835. set_property_string(id, def.name, (string)def.deffault);
  836. break;
  837. case PropertyType.GUID:
  838. set_property_guid(id, def.name, (Guid)def.deffault);
  839. break;
  840. case PropertyType.VECTOR3:
  841. set_property_vector3(id, def.name, (Vector3)def.deffault);
  842. break;
  843. case PropertyType.QUATERNION:
  844. set_property_quaternion(id, def.name, (Quaternion)def.deffault);
  845. break;
  846. case PropertyType.OBJECTS_SET:
  847. create_empty_set(id, def.name);
  848. break;
  849. case PropertyType.NULL:
  850. break;
  851. }
  852. }
  853. }
  854. public void create(Guid id, string type)
  855. {
  856. assert(id != GUID_ZERO);
  857. assert(!has_object(id));
  858. if (_undo_redo != null) {
  859. _undo_redo._undo.write_destroy_action(Action.DESTROY, id, type);
  860. _undo_redo._redo.clear();
  861. }
  862. create_internal(1, id);
  863. set_object_type(id, type);
  864. StringId64 type_hash = StringId64(type);
  865. if (has_object_type(type_hash))
  866. _init_object(id, object_definition(type_hash));
  867. }
  868. public void destroy(Guid id)
  869. {
  870. assert(id != GUID_ZERO);
  871. assert(has_object(id));
  872. string obj_type = object_type(id);
  873. Gee.HashMap<string, Value?> o = get_data(id);
  874. string[] keys = o.keys.to_array();
  875. foreach (string key in keys) {
  876. Value? value = o[key];
  877. if (value.holds(typeof(Gee.HashSet))) {
  878. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  879. Guid?[] ids = hs.to_array();
  880. foreach (Guid item_id in ids) {
  881. remove_from_set(id, key, item_id);
  882. destroy(item_id);
  883. }
  884. } else {
  885. if (key != "type" && key != "_type")
  886. set_property_null(id, key);
  887. }
  888. }
  889. if (_undo_redo != null) {
  890. _undo_redo._undo.write_create_action(Action.CREATE, id, obj_type);
  891. _undo_redo._redo.clear();
  892. }
  893. destroy_internal(1, id);
  894. }
  895. public void set_property_null(Guid id, string key)
  896. {
  897. assert(has_object(id));
  898. assert(is_valid_key(key));
  899. assert(is_valid_value(null));
  900. if (_undo_redo != null) {
  901. Gee.HashMap<string, Value?> ob = get_data(id);
  902. if (ob.has_key(key) && ob[key] != null) {
  903. if (ob[key].holds(typeof(bool)))
  904. _undo_redo._undo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, (bool)ob[key]);
  905. if (ob[key].holds(typeof(double)))
  906. _undo_redo._undo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, (double)ob[key]);
  907. if (ob[key].holds(typeof(string)))
  908. _undo_redo._undo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, (string)ob[key]);
  909. if (ob[key].holds(typeof(Guid)))
  910. _undo_redo._undo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, (Guid)ob[key]);
  911. if (ob[key].holds(typeof(Vector3)))
  912. _undo_redo._undo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, (Vector3)ob[key]);
  913. if (ob[key].holds(typeof(Quaternion)))
  914. _undo_redo._undo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, (Quaternion)ob[key]);
  915. } else {
  916. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  917. }
  918. _undo_redo._redo.clear();
  919. }
  920. set_property_internal(1, id, key, null);
  921. }
  922. public void set_property_bool(Guid id, string key, bool val)
  923. {
  924. assert(has_object(id));
  925. assert(is_valid_key(key));
  926. assert(is_valid_value(val));
  927. if (_undo_redo != null) {
  928. Gee.HashMap<string, Value?> ob = get_data(id);
  929. if (ob.has_key(key) && ob[key] != null)
  930. _undo_redo._undo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, (bool)ob[key]);
  931. else
  932. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  933. _undo_redo._redo.clear();
  934. }
  935. set_property_internal(1, id, key, val);
  936. }
  937. public void set_property_double(Guid id, string key, double val)
  938. {
  939. assert(has_object(id));
  940. assert(is_valid_key(key));
  941. assert(is_valid_value(val));
  942. if (_undo_redo != null) {
  943. Gee.HashMap<string, Value?> ob = get_data(id);
  944. if (ob.has_key(key) && ob[key] != null)
  945. _undo_redo._undo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, (double)ob[key]);
  946. else
  947. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  948. _undo_redo._redo.clear();
  949. }
  950. set_property_internal(1, id, key, val);
  951. }
  952. public void set_property_string(Guid id, string key, string val)
  953. {
  954. assert(has_object(id));
  955. assert(is_valid_key(key));
  956. assert(is_valid_value(val));
  957. if (_undo_redo != null) {
  958. Gee.HashMap<string, Value?> ob = get_data(id);
  959. if (ob.has_key(key) && ob[key] != null)
  960. _undo_redo._undo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, (string)ob[key]);
  961. else
  962. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  963. _undo_redo._redo.clear();
  964. }
  965. set_property_internal(1, id, key, val);
  966. }
  967. public void set_property_guid(Guid id, string key, Guid val)
  968. {
  969. assert(has_object(id));
  970. assert(is_valid_key(key));
  971. assert(is_valid_value(val));
  972. if (_undo_redo != null) {
  973. Gee.HashMap<string, Value?> ob = get_data(id);
  974. if (ob.has_key(key) && ob[key] != null)
  975. _undo_redo._undo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, (Guid)ob[key]);
  976. else
  977. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  978. _undo_redo._redo.clear();
  979. }
  980. set_property_internal(1, id, key, val);
  981. }
  982. public void set_property_vector3(Guid id, string key, Vector3 val)
  983. {
  984. assert(has_object(id));
  985. assert(is_valid_key(key));
  986. assert(is_valid_value(val));
  987. if (_undo_redo != null) {
  988. Gee.HashMap<string, Value?> ob = get_data(id);
  989. if (ob.has_key(key) && ob[key] != null)
  990. _undo_redo._undo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, (Vector3)ob[key]);
  991. else
  992. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  993. _undo_redo._redo.clear();
  994. }
  995. set_property_internal(1, id, key, val);
  996. }
  997. public void set_property_quaternion(Guid id, string key, Quaternion val)
  998. {
  999. assert(has_object(id));
  1000. assert(is_valid_key(key));
  1001. assert(is_valid_value(val));
  1002. if (_undo_redo != null) {
  1003. Gee.HashMap<string, Value?> ob = get_data(id);
  1004. if (ob.has_key(key) && ob[key] != null)
  1005. _undo_redo._undo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, (Quaternion)ob[key]);
  1006. else
  1007. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1008. _undo_redo._redo.clear();
  1009. }
  1010. set_property_internal(1, id, key, val);
  1011. }
  1012. public void set_property(Guid id, string key, Value? val)
  1013. {
  1014. if (val == null)
  1015. set_property_null(id, key);
  1016. if (val.holds(typeof(bool)))
  1017. set_property_bool(id, key, (bool)val);
  1018. else if (val.holds(typeof(double)))
  1019. set_property_double(id, key, (double)val);
  1020. else if (val.holds(typeof(string)))
  1021. set_property_string(id, key, (string)val);
  1022. else if (val.holds(typeof(Guid)))
  1023. set_property_guid(id, key, (Guid)val);
  1024. else if (val.holds(typeof(Vector3)))
  1025. set_property_vector3(id, key, (Vector3)val);
  1026. else if (val.holds(typeof(Quaternion)))
  1027. set_property_quaternion(id, key, (Quaternion)val);
  1028. else
  1029. assert(false);
  1030. }
  1031. public void add_to_set(Guid id, string key, Guid item_id)
  1032. {
  1033. assert(has_object(id));
  1034. assert(is_valid_key(key));
  1035. assert(item_id != GUID_ZERO);
  1036. assert(has_object(item_id));
  1037. if (_undo_redo != null) {
  1038. _undo_redo._undo.write_remove_from_set_action(Action.REMOVE_FROM_SET, id, key, item_id);
  1039. _undo_redo._redo.clear();
  1040. }
  1041. add_to_set_internal(1, id, key, item_id);
  1042. }
  1043. public void remove_from_set(Guid id, string key, Guid item_id)
  1044. {
  1045. assert(has_object(id));
  1046. assert(is_valid_key(key));
  1047. assert(item_id != GUID_ZERO);
  1048. if (_undo_redo != null) {
  1049. _undo_redo._undo.write_add_to_set_action(Action.ADD_TO_SET, id, key, item_id);
  1050. _undo_redo._redo.clear();
  1051. }
  1052. remove_from_set_internal(1, id, key, item_id);
  1053. }
  1054. public bool has_object(Guid id)
  1055. {
  1056. return id == GUID_ZERO || _data.has_key(id);
  1057. }
  1058. public bool has_property(Guid id, string key)
  1059. {
  1060. return get_property(id, key) != null;
  1061. }
  1062. public Value? get_property(Guid id, string key, Value? val = null)
  1063. {
  1064. assert(has_object(id));
  1065. assert(is_valid_key(key));
  1066. Gee.HashMap<string, Value?> ob = get_data(id);
  1067. Value? value = (ob.has_key(key) ? ob[key] : val);
  1068. if (_debug_getters)
  1069. logi("get_property %s %s %s".printf(id.to_string(), key, value_to_string(value)));
  1070. return value;
  1071. }
  1072. public bool get_property_bool(Guid id, string key, bool deffault = false)
  1073. {
  1074. return (bool)get_property(id, key, deffault);
  1075. }
  1076. public double get_property_double(Guid id, string key, double deffault = 0.0)
  1077. {
  1078. return (double)get_property(id, key, deffault);
  1079. }
  1080. public string get_property_string(Guid id, string key, string deffault = "")
  1081. {
  1082. return (string)get_property(id, key, deffault);
  1083. }
  1084. public Guid get_property_guid(Guid id, string key, Guid deffault = GUID_ZERO)
  1085. {
  1086. return (Guid)get_property(id, key, deffault);
  1087. }
  1088. public Vector3 get_property_vector3(Guid id, string key, Vector3 deffault = VECTOR3_ZERO)
  1089. {
  1090. return (Vector3)get_property(id, key, deffault);
  1091. }
  1092. public Quaternion get_property_quaternion(Guid id, string key, Quaternion deffault = QUATERNION_IDENTITY)
  1093. {
  1094. return (Quaternion)get_property(id, key, deffault);
  1095. }
  1096. public Gee.HashSet<Guid?> get_property_set(Guid id, string key, Gee.HashSet<Guid?> deffault)
  1097. {
  1098. assert(has_object(id));
  1099. assert(is_valid_key(key));
  1100. Gee.HashMap<string, Value?> ob = get_data(id);
  1101. Gee.HashSet<Guid?> value;
  1102. if (ob.has_key(key))
  1103. value = ob[key] as Gee.HashSet<Guid?>;
  1104. else
  1105. value = deffault;
  1106. if (_debug_getters)
  1107. logi("get_property %s %s %s".printf(id.to_string(), key, value_to_string(value)));
  1108. return value;
  1109. }
  1110. public Gee.HashMap<string, Value?> get_object(Guid id)
  1111. {
  1112. return (Gee.HashMap<string, Value?>)get_data(GUID_ZERO)[id.to_string()];
  1113. }
  1114. public string[] get_keys(Guid id)
  1115. {
  1116. Gee.HashMap<string, Value?> data = get_data(id);
  1117. return data.keys.to_array();
  1118. }
  1119. public void add_restore_point(int id, Guid?[] data, uint32 flags = 0u)
  1120. {
  1121. if (_debug)
  1122. logi("add_restore_point %d, undo size = %u".printf(id, _undo_redo._undo.size()));
  1123. if (_undo_redo != null) {
  1124. _undo_redo._undo.write_restore_point(id, flags, data);
  1125. _undo_redo._redo.clear();
  1126. restore_point_added(id, data, flags);
  1127. }
  1128. }
  1129. /// Duplicates the object specified by id and assign new_id to the duplicated object.
  1130. public void duplicate(Guid id, Guid new_id, Database? dest = null)
  1131. {
  1132. assert(id != GUID_ZERO);
  1133. assert(new_id != GUID_ZERO);
  1134. assert(id != new_id);
  1135. assert(has_object(id));
  1136. if (dest == null)
  1137. dest = this;
  1138. dest.create(new_id, object_type(id));
  1139. Gee.HashMap<string, Value?> ob = get_data(id);
  1140. string[] keys = ob.keys.to_array();
  1141. foreach (string key in keys) {
  1142. Value? val = ob[key];
  1143. if (val.holds(typeof(Gee.HashSet))) {
  1144. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)val;
  1145. foreach (Guid j in hs) {
  1146. Guid x = Guid.new_guid();
  1147. duplicate(j, x, dest);
  1148. dest.add_to_set(new_id, key, x);
  1149. }
  1150. } else {
  1151. if (ob[key] == null)
  1152. dest.set_property_null(new_id, key);
  1153. else if (ob[key].holds(typeof(bool)))
  1154. dest.set_property_bool(new_id, key, (bool)ob[key]);
  1155. else if (ob[key].holds(typeof(double)))
  1156. dest.set_property_double(new_id, key, (double)ob[key]);
  1157. else if (ob[key].holds(typeof(string)))
  1158. dest.set_property_string(new_id, key, (string)ob[key]);
  1159. else if (ob[key].holds(typeof(Guid)))
  1160. dest.set_property_guid(new_id, key, (Guid)ob[key]);
  1161. else if (ob[key].holds(typeof(Vector3)))
  1162. dest.set_property_vector3(new_id, key, (Vector3)ob[key]);
  1163. else if (ob[key].holds(typeof(Quaternion)))
  1164. dest.set_property_quaternion(new_id, key, (Quaternion)ob[key]);
  1165. else
  1166. assert(false);
  1167. }
  1168. }
  1169. }
  1170. /// Copies the database to db under the given new_key.
  1171. public void copy_to(Database db, string new_key)
  1172. {
  1173. assert(db != null);
  1174. assert(is_valid_key(new_key));
  1175. copy_deep(db, GUID_ZERO, new_key);
  1176. }
  1177. public void copy_deep(Database db, Guid id, string new_key)
  1178. {
  1179. Gee.HashMap<string, Value?> ob = get_data(id);
  1180. string[] keys = ob.keys.to_array();
  1181. foreach (string key in keys) {
  1182. Value? value = ob[key];
  1183. if (value.holds(typeof(Gee.HashSet))) {
  1184. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  1185. foreach (Guid j in hs) {
  1186. db.create(j, object_type(j));
  1187. copy_deep(db, j, "");
  1188. db.add_to_set(id, new_key + (new_key == "" ? "" : ".") + key, j);
  1189. }
  1190. } else {
  1191. if (!db.has_object(id))
  1192. db.create(id, object_type(id));
  1193. string kk = new_key + (new_key == "" ? "" : ".") + key;
  1194. if (ob[key] == null)
  1195. db.set_property_null(id, kk);
  1196. if (ob[key].holds(typeof(bool)))
  1197. db.set_property_bool(id, kk, (bool)ob[key]);
  1198. if (ob[key].holds(typeof(double)))
  1199. db.set_property_double(id, kk, (double)ob[key]);
  1200. if (ob[key].holds(typeof(string)))
  1201. db.set_property_string(id, kk, (string)ob[key]);
  1202. if (ob[key].holds(typeof(Guid)))
  1203. db.set_property_guid(id, kk, (Guid)ob[key]);
  1204. if (ob[key].holds(typeof(Vector3)))
  1205. db.set_property_vector3(id, kk, (Vector3)ob[key]);
  1206. if (ob[key].holds(typeof(Quaternion)))
  1207. db.set_property_quaternion(id, kk, (Quaternion)ob[key]);
  1208. }
  1209. }
  1210. }
  1211. // Tries to read a restore point @a rp from the @a stack and returns
  1212. // 0 if successful.
  1213. private int try_read_restore_point(ref RestorePoint rp, Stack stack)
  1214. {
  1215. if (stack.size() < sizeof(Action) + sizeof(RestorePointHeader))
  1216. return -1;
  1217. rp = stack.read_restore_point();
  1218. if (stack.size() < rp.header.size) {
  1219. // The restore point has been overwritten.
  1220. stack.clear();
  1221. return -1;
  1222. }
  1223. return 0;
  1224. }
  1225. // Un-does the last action and returns its ID, or -1 if there is no
  1226. // action to undo.
  1227. public int undo()
  1228. {
  1229. if (_undo_redo == null)
  1230. return -1;
  1231. RestorePoint rp = {};
  1232. if (try_read_restore_point(ref rp, _undo_redo._undo) != 0)
  1233. return -1;
  1234. undo_or_redo(_undo_redo._undo, _undo_redo._redo, rp.header.size);
  1235. undo_redo(true, rp.header.id, rp.data);
  1236. _undo_redo._redo.write_restore_point(rp.header.id, rp.header.flags, rp.data);
  1237. return (int)rp.header.id;
  1238. }
  1239. // Re-does the last action and returns its ID, or -1 if there is no
  1240. // action to redo.
  1241. public int redo()
  1242. {
  1243. if (_undo_redo == null)
  1244. return -1;
  1245. RestorePoint rp = {};
  1246. if (try_read_restore_point(ref rp, _undo_redo._redo) != 0)
  1247. return -1;
  1248. undo_or_redo(_undo_redo._redo, _undo_redo._undo, rp.header.size);
  1249. undo_redo(false, rp.header.id, rp.data);
  1250. _undo_redo._undo.write_restore_point(rp.header.id, rp.header.flags, rp.data);
  1251. return (int)rp.header.id;
  1252. }
  1253. private void undo_or_redo(Stack undo, Stack redo, uint32 restore_point_size)
  1254. {
  1255. assert(undo.size() >= restore_point_size);
  1256. int dir = undo == _undo_redo._undo ? -1 : 1;
  1257. // Read up to restore_point_size bytes.
  1258. uint32 undo_size_start = undo.size();
  1259. while (undo_size_start - undo.size() < restore_point_size) {
  1260. Action action = (Action)undo.read_uint32();
  1261. if (action == Action.CREATE) {
  1262. Guid id = undo.read_guid();
  1263. string obj_type = undo.read_string();
  1264. redo.write_destroy_action(Action.DESTROY, id, obj_type);
  1265. create_internal(dir, id);
  1266. set_object_type(id, obj_type);
  1267. } else if (action == Action.DESTROY) {
  1268. Guid id = undo.read_guid();
  1269. string obj_type = undo.read_string();
  1270. redo.write_create_action(Action.CREATE, id, obj_type);
  1271. destroy_internal(dir, id);
  1272. } else if (action == Action.SET_PROPERTY_NULL) {
  1273. Guid id = undo.read_guid();
  1274. string key = undo.read_string();
  1275. if (has_property(id, key)) {
  1276. if (get_data(id)[key].holds(typeof(bool)))
  1277. redo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, get_property_bool(id, key));
  1278. if (get_data(id)[key].holds(typeof(double)))
  1279. redo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, get_property_double(id, key));
  1280. if (get_data(id)[key].holds(typeof(string)))
  1281. redo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, get_property_string(id, key));
  1282. if (get_data(id)[key].holds(typeof(Guid)))
  1283. redo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, get_property_guid(id, key));
  1284. if (get_data(id)[key].holds(typeof(Vector3)))
  1285. redo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, get_property_vector3(id, key));
  1286. if (get_data(id)[key].holds(typeof(Quaternion)))
  1287. redo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, get_property_quaternion(id, key));
  1288. } else {
  1289. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1290. }
  1291. set_property_internal(dir, id, key, null);
  1292. } else if (action == Action.SET_PROPERTY_BOOL) {
  1293. Guid id = undo.read_guid();
  1294. string key = undo.read_string();
  1295. bool val = undo.read_bool();
  1296. if (has_property(id, key))
  1297. redo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, get_property_bool(id, key));
  1298. else
  1299. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1300. set_property_internal(dir, id, key, val);
  1301. } else if (action == Action.SET_PROPERTY_DOUBLE) {
  1302. Guid id = undo.read_guid();
  1303. string key = undo.read_string();
  1304. double val = undo.read_double();
  1305. if (has_property(id, key))
  1306. redo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, get_property_double(id, key));
  1307. else
  1308. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1309. set_property_internal(dir, id, key, val);
  1310. } else if (action == Action.SET_PROPERTY_STRING) {
  1311. Guid id = undo.read_guid();
  1312. string key = undo.read_string();
  1313. string val = undo.read_string();
  1314. if (has_property(id, key))
  1315. redo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, get_property_string(id, key));
  1316. else
  1317. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1318. set_property_internal(dir, id, key, val);
  1319. } else if (action == Action.SET_PROPERTY_GUID) {
  1320. Guid id = undo.read_guid();
  1321. string key = undo.read_string();
  1322. Guid val = undo.read_guid();
  1323. if (has_property(id, key))
  1324. redo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, get_property_guid(id, key));
  1325. else
  1326. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1327. set_property_internal(dir, id, key, val);
  1328. } else if (action == Action.SET_PROPERTY_VECTOR3) {
  1329. Guid id = undo.read_guid();
  1330. string key = undo.read_string();
  1331. Vector3 val = undo.read_vector3();
  1332. if (has_property(id, key))
  1333. redo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, get_property_vector3(id, key));
  1334. else
  1335. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1336. set_property_internal(dir, id, key, val);
  1337. } else if (action == Action.SET_PROPERTY_QUATERNION) {
  1338. Guid id = undo.read_guid();
  1339. string key = undo.read_string();
  1340. Quaternion val = undo.read_quaternion();
  1341. if (has_property(id, key))
  1342. redo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, get_property_quaternion(id, key));
  1343. else
  1344. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1345. set_property_internal(dir, id, key, val);
  1346. } else if (action == Action.ADD_TO_SET) {
  1347. Guid id = undo.read_guid();
  1348. string key = undo.read_string();
  1349. Guid item_id = undo.read_guid();
  1350. redo.write_remove_from_set_action(Action.REMOVE_FROM_SET, id, key, item_id);
  1351. add_to_set_internal(dir, id, key, item_id);
  1352. } else if (action == Action.REMOVE_FROM_SET) {
  1353. Guid id = undo.read_guid();
  1354. string key = undo.read_string();
  1355. Guid item_id = undo.read_guid();
  1356. redo.write_add_to_set_action(Action.ADD_TO_SET, id, key, item_id);
  1357. remove_from_set_internal(dir, id, key, item_id);
  1358. }
  1359. }
  1360. }
  1361. // Creates a new object @a type with the specified @a properties and returns its ID.
  1362. public StringId64 create_object_type(string type, PropertyDefinition[] properties, ObjectTypeFlags flags = ObjectTypeFlags.NONE)
  1363. {
  1364. StringId64 type_hash = StringId64(type);
  1365. assert(!_object_definitions.has_key(type_hash));
  1366. assert(properties.length > 0);
  1367. ObjectTypeInfo info = {};
  1368. info.properties = { _property_definitions.length, _property_definitions.length + properties.length };
  1369. info.name = type;
  1370. info.flags = flags;
  1371. _object_definitions[type_hash] = info;
  1372. foreach (PropertyDefinition def in properties) {
  1373. // Generate labels if missing.
  1374. if (def.label == null) {
  1375. int ld = def.name.last_index_of_char('.');
  1376. string label = ld == -1 ? def.name : def.name.substring(ld + 1);
  1377. def.label = camel_case(label);
  1378. }
  1379. if (def.enum_labels.length == 0) {
  1380. string[] labels = new string[def.enum_values.length];
  1381. for (int i = 0; i < def.enum_values.length; ++i)
  1382. labels[i] = camel_case(def.enum_values[i]);
  1383. def.enum_labels = labels;
  1384. }
  1385. // Assign default/min/max values.
  1386. switch (def.type) {
  1387. case PropertyType.BOOL:
  1388. if (def.deffault == null)
  1389. def.deffault = false;
  1390. assert(def.deffault.holds(typeof(bool)));
  1391. break;
  1392. case PropertyType.DOUBLE:
  1393. if (def.deffault == null)
  1394. def.deffault = 0.0;
  1395. if (def.min == null)
  1396. def.min = double.MIN;
  1397. if (def.max == null)
  1398. def.max = double.MAX;
  1399. assert(def.deffault.holds(typeof(double)));
  1400. assert(def.min.holds(typeof(double)));
  1401. assert(def.max.holds(typeof(double)));
  1402. break;
  1403. case PropertyType.STRING:
  1404. if (def.deffault == null) {
  1405. if (def.enum_values.length > 0)
  1406. def.deffault = def.enum_values[0];
  1407. else
  1408. def.deffault = "";
  1409. }
  1410. assert(def.deffault.holds(typeof(string)));
  1411. break;
  1412. case PropertyType.GUID:
  1413. break;
  1414. case PropertyType.VECTOR3:
  1415. if (def.deffault == null)
  1416. def.deffault = VECTOR3_ZERO;
  1417. if (def.min == null)
  1418. def.min = VECTOR3_MIN;
  1419. if (def.max == null)
  1420. def.max = VECTOR3_MAX;
  1421. assert(def.deffault.holds(typeof(Vector3)));
  1422. assert(def.min.holds(typeof(Vector3)));
  1423. assert(def.max.holds(typeof(Vector3)));
  1424. break;
  1425. case PropertyType.QUATERNION:
  1426. if (def.deffault == null)
  1427. def.deffault = QUATERNION_IDENTITY;
  1428. assert(def.deffault.holds(typeof(Quaternion)));
  1429. break;
  1430. default:
  1431. case PropertyType.OBJECTS_SET:
  1432. break;
  1433. case PropertyType.NULL:
  1434. assert(false);
  1435. break;
  1436. }
  1437. _property_definitions += def;
  1438. }
  1439. return type_hash;
  1440. }
  1441. // Returns the array of properties (i.e. its definition) of the object @a type.
  1442. public unowned PropertyDefinition[] object_definition(StringId64 type)
  1443. {
  1444. assert(_object_definitions.has_key(type));
  1445. PropertiesSlice ps = _object_definitions[type].properties;
  1446. return _property_definitions[ps.start : ps.end];
  1447. }
  1448. // Returns the name of the object @id. If the object has no name set, it returns
  1449. // OBJECT_NAME_UNNAMED.
  1450. public string object_name(Guid id)
  1451. {
  1452. string name = get_property_string(id, "editor.name", OBJECT_NAME_UNNAMED);
  1453. if (name == OBJECT_NAME_UNNAMED)
  1454. return get_property_string(id, "name", OBJECT_NAME_UNNAMED);
  1455. return name;
  1456. }
  1457. // Sets the @a name of the object @a id.
  1458. public void set_object_name(Guid id, string name)
  1459. {
  1460. set_property_string(id, "editor.name", name);
  1461. }
  1462. // Returns whether the object @a type exists (i.e. has been created with create_object_type()).
  1463. public bool has_object_type(StringId64 type)
  1464. {
  1465. return _object_definitions.has_key(type);
  1466. }
  1467. public string object_type_name(StringId64 type)
  1468. {
  1469. return _object_definitions[type].name;
  1470. }
  1471. public uint object_type_flags(StringId64 type)
  1472. {
  1473. return _object_definitions[type].flags;
  1474. }
  1475. }
  1476. } /* namespace Crown */