database.vala 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763
  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 struct PropertiesSlice
  363. {
  364. int start; // Index of first property.
  365. int end; // Index of last property + 1.
  366. }
  367. public struct ObjectTypeInfo
  368. {
  369. PropertiesSlice properties;
  370. string name;
  371. double ui_order;
  372. ObjectTypeFlags flags;
  373. string? user_data;
  374. }
  375. public class Database
  376. {
  377. private static bool _debug = false;
  378. private static bool _debug_getters = false;
  379. private enum Action
  380. {
  381. CREATE,
  382. DESTROY,
  383. SET_PROPERTY_NULL,
  384. SET_PROPERTY_BOOL,
  385. SET_PROPERTY_DOUBLE,
  386. SET_PROPERTY_STRING,
  387. SET_PROPERTY_GUID,
  388. SET_PROPERTY_VECTOR3,
  389. SET_PROPERTY_QUATERNION,
  390. ADD_TO_SET,
  391. REMOVE_FROM_SET
  392. }
  393. // Data
  394. private PropertyDefinition[] _property_definitions;
  395. private Gee.HashMap<StringId64?, ObjectTypeInfo?> _object_definitions;
  396. private Gee.HashMap<Guid?, Gee.HashMap<string, Value?>> _data;
  397. private UndoRedo? _undo_redo;
  398. public Project _project;
  399. // The number of changes to the database since the last successful state
  400. // synchronization (load(), save() etc.). If it is less than 0, the changes
  401. // came from undo(), otherwise they came from redo() or from regular calls to
  402. // create(), destroy(), set_*() etc. A value of 0 means there were no changes.
  403. public int _distance_from_last_sync;
  404. // Signals
  405. public signal void undo_redo(bool undo, uint32 id, Guid?[] data);
  406. public signal void restore_point_added(int id, Guid?[] data, uint32 flags);
  407. public signal void object_type_added(ObjectTypeInfo info);
  408. public Database(Project project, UndoRedo? undo_redo = null)
  409. {
  410. _property_definitions = new PropertyDefinition[0];
  411. _object_definitions = new Gee.HashMap<StringId64?, ObjectTypeInfo?>(StringId64.hash_func, StringId64.equal_func);
  412. _data = new Gee.HashMap<Guid?, Gee.HashMap<string, Value?>>(Guid.hash_func, Guid.equal_func);
  413. _project = project;
  414. _undo_redo = undo_redo;
  415. reset();
  416. }
  417. /// Resets database to clean state.
  418. public void reset()
  419. {
  420. _data.clear();
  421. if (_undo_redo != null)
  422. _undo_redo.reset();
  423. _distance_from_last_sync = 0;
  424. // This is a special field which stores all objects
  425. _data[GUID_ZERO] = new Gee.HashMap<string, Value?>();
  426. }
  427. /// Returns whether the database has been changed since last call to Save().
  428. public bool changed()
  429. {
  430. return _distance_from_last_sync != 0;
  431. }
  432. /// Saves database to path without marking it as not changed.
  433. public int dump(string path, Guid id)
  434. {
  435. try {
  436. Hashtable json = encode(id);
  437. SJSON.save(json, path);
  438. return 0;
  439. } catch (JsonWriteError e) {
  440. return -1;
  441. }
  442. }
  443. /// Saves database to path.
  444. public int save(string path, Guid id)
  445. {
  446. int err = dump(path, id);
  447. if (err == 0)
  448. _distance_from_last_sync = 0;
  449. return err;
  450. }
  451. // See: add_from_path().
  452. public int add_from_file(out Guid object_id, FileStream? fs, string resource_path)
  453. {
  454. try {
  455. Hashtable json = SJSON.load_from_file(fs);
  456. // Parse the object's ID or generate a new one if none is found.
  457. if (json.has_key("id"))
  458. object_id = Guid.parse((string)json["id"]);
  459. else if (json.has_key("_guid"))
  460. object_id = Guid.parse((string)json["_guid"]);
  461. else
  462. object_id = Guid.new_guid();
  463. create_internal(0, object_id);
  464. set_object_type(object_id, ResourceId.type(resource_path));
  465. decode_object(object_id, GUID_ZERO, "", json);
  466. // Create a mapping between the path and the object it has been loaded into.
  467. set_property_internal(0, GUID_ZERO, resource_path, object_id);
  468. return 0;
  469. } catch (JsonSyntaxError e) {
  470. object_id = GUID_ZERO;
  471. return -1;
  472. }
  473. }
  474. // Adds the object stored at @a path to the database.
  475. // This makes it possible to load multiple objects from distinct
  476. // paths in the same database. @a resource_path is used as a key in the
  477. // database to refer to the object that has been loaded. This is useful when
  478. // you do not have the object ID but only its path, as it is often the case
  479. // since resources use paths and not IDs to reference each other.
  480. public int add_from_path(out Guid object_id, string path, string resource_path)
  481. {
  482. object_id = GUID_ZERO;
  483. FileStream fs = FileStream.open(path, "rb");
  484. if (fs == null)
  485. return 1;
  486. return add_from_file(out object_id, fs, resource_path);
  487. }
  488. public int add_from_resource_path(out Guid object_id, string resource_path)
  489. {
  490. // If the resource is already loaded.
  491. if (has_property(GUID_ZERO, resource_path)) {
  492. object_id = get_property_guid(GUID_ZERO, resource_path);
  493. return 0;
  494. }
  495. string path = _project.absolute_path(resource_path);
  496. return add_from_path(out object_id, path, resource_path);
  497. }
  498. /// Loads the database with the object stored at @a path.
  499. public int load_from_file(out Guid object_id, FileStream fs, string resource_path)
  500. {
  501. reset();
  502. return add_from_file(out object_id, fs, resource_path);
  503. }
  504. /// Loads the database with the object stored at @a path.
  505. public int load_from_path(out Guid object_id, string path, string resource_path)
  506. {
  507. reset();
  508. return add_from_path(out object_id, path, resource_path);
  509. }
  510. /// Encodes the object @a id into SJSON object.
  511. private Hashtable encode(Guid id)
  512. {
  513. return encode_object(id, get_data(id));
  514. }
  515. private static bool is_valid_value(Value? value)
  516. {
  517. return value == null
  518. || value.holds(typeof(bool))
  519. || value.holds(typeof(double))
  520. || value.holds(typeof(string))
  521. || value.holds(typeof(Guid))
  522. || value.holds(typeof(Vector3))
  523. || value.holds(typeof(Quaternion))
  524. ;
  525. }
  526. private static bool is_valid_key(string key)
  527. {
  528. return key.length > 0
  529. && !key.has_prefix(".")
  530. && !key.has_suffix(".")
  531. ;
  532. }
  533. private static string value_to_string(Value? value)
  534. {
  535. if (value == null)
  536. return "null";
  537. if (value.holds(typeof(bool)))
  538. return ((bool)value).to_string();
  539. if (value.holds(typeof(double)))
  540. return ((double)value).to_string();
  541. if (value.holds(typeof(string)))
  542. return ((string)value).to_string();
  543. if (value.holds(typeof(Guid)))
  544. return ((Guid)value).to_string();
  545. if (value.holds(typeof(Vector3)))
  546. return ((Vector3)value).to_string();
  547. if (value.holds(typeof(Quaternion)))
  548. return ((Quaternion)value).to_string();
  549. if (value.holds(typeof(Gee.HashSet)))
  550. return "Set<Guid>";
  551. return "<invalid>";
  552. }
  553. private void decode_object(Guid id, Guid owner_id, string db_key, Hashtable json)
  554. {
  555. string old_db = db_key;
  556. string k = db_key;
  557. string[] keys = json.keys.to_array();
  558. foreach (string key in keys) {
  559. // ID is filled by decode_set().
  560. if (key == "id" || key == "_guid")
  561. continue;
  562. // The "type" key defines object type only if it appears
  563. // in the root of a JSON object (k == "").
  564. if (k == "") {
  565. if (key == "type" || key == "_type")
  566. set_object_type(id, (string)json[key]);
  567. }
  568. Value? val = json[key];
  569. k += k == "" ? key : ("." + key);
  570. if (val.holds(typeof(Hashtable))) {
  571. Hashtable ht = (Hashtable)val;
  572. decode_object(id, owner_id, k, ht);
  573. } else if (val.holds(typeof(Gee.ArrayList))) {
  574. Gee.ArrayList<Value?> arr = (Gee.ArrayList<Value?>)val;
  575. if (arr.size > 0
  576. && arr[0].holds(typeof(double))
  577. && k != "frames" // sprite_animation
  578. )
  579. set_property_internal(0, id, k, decode_value(val));
  580. else
  581. decode_set(id, key, arr);
  582. } else {
  583. set_property_internal(0, id, k, decode_value(val));
  584. }
  585. k = old_db;
  586. }
  587. }
  588. private void decode_set(Guid owner_id, string key, Gee.ArrayList<Value?> json)
  589. {
  590. // Set should be created even if it is empty.
  591. create_empty_set(owner_id, key);
  592. for (int i = 0; i < json.size; ++i) {
  593. Hashtable obj;
  594. string owner_type = object_type(owner_id);
  595. if (owner_type == OBJECT_TYPE_SPRITE_ANIMATION)
  596. obj = new Hashtable();
  597. else
  598. obj = (Hashtable)json[i];
  599. // Decode object ID.
  600. Guid obj_id;
  601. if (obj.has_key("id") && owner_type != OBJECT_TYPE_FONT)
  602. obj_id = Guid.parse((string)obj["id"]);
  603. else if (obj.has_key("_guid"))
  604. obj_id = Guid.parse((string)obj["_guid"]);
  605. else
  606. obj_id = Guid.new_guid();
  607. create_internal(0, obj_id);
  608. // Determine the object's type based on the type of its
  609. // parent and other heuristics.
  610. if (owner_type == OBJECT_TYPE_LEVEL) {
  611. if (key == "units")
  612. set_object_type(obj_id, OBJECT_TYPE_UNIT);
  613. else if (key == "sounds")
  614. set_object_type(obj_id, OBJECT_TYPE_SOUND_SOURCE);
  615. else
  616. set_object_type(obj_id, "undefined");
  617. } else if (owner_type == OBJECT_TYPE_STATE_MACHINE) {
  618. if (key == "states")
  619. set_object_type(obj_id, OBJECT_TYPE_STATE_MACHINE_NODE);
  620. else if (key == "variables")
  621. set_object_type(obj_id, OBJECT_TYPE_STATE_MACHINE_VARIABLE);
  622. else
  623. set_object_type(obj_id, "undefined");
  624. } else if (owner_type == OBJECT_TYPE_STATE_MACHINE_NODE) {
  625. if (key == "animations")
  626. set_object_type(obj_id, OBJECT_TYPE_NODE_ANIMATION);
  627. else if (key == "transitions")
  628. set_object_type(obj_id, OBJECT_TYPE_NODE_TRANSITION);
  629. } else if (owner_type == OBJECT_TYPE_SPRITE) {
  630. if (key == "frames") {
  631. set_object_type(obj_id, "sprite_frame");
  632. set_property_internal(0, obj_id, "index", (double)i);
  633. }
  634. } else if (owner_type == OBJECT_TYPE_SPRITE_ANIMATION) {
  635. if (key == "frames") {
  636. set_object_type(obj_id, "animation_frame");
  637. set_property_internal(0, obj_id, "index", (double)json[i]);
  638. }
  639. } else if (owner_type == OBJECT_TYPE_FONT) {
  640. if (key == "glyphs") {
  641. set_object_type(obj_id, "font_glyph");
  642. set_property_internal(0, obj_id, "cp", (double)obj["id"]);
  643. }
  644. }
  645. decode_object(obj_id, owner_id, "", obj);
  646. assert(has_property(obj_id, "_type"));
  647. add_to_set_internal(0, owner_id, key, obj_id);
  648. }
  649. }
  650. private Value? decode_value(Value? value)
  651. {
  652. if (value.holds(typeof(Gee.ArrayList))) {
  653. Gee.ArrayList<Value?> al = (Gee.ArrayList<Value?>)value;
  654. if (al.size == 1)
  655. return Vector3((double)al[0], 0.0, 0.0);
  656. else if (al.size == 2)
  657. return Vector3((double)al[0], (double)al[1], 0.0);
  658. else if (al.size == 3)
  659. return Vector3((double)al[0], (double)al[1], (double)al[2]);
  660. else if (al.size == 4)
  661. return Quaternion((double)al[0], (double)al[1], (double)al[2], (double)al[3]);
  662. else
  663. return Vector3(0.0, 0.0, 0.0);
  664. } else if (value.holds(typeof(string))) {
  665. Guid id;
  666. if (Guid.try_parse((string)value, out id))
  667. return id;
  668. return value;
  669. } else if (value == null
  670. || value.holds(typeof(bool))
  671. || value.holds(typeof(double))) {
  672. return value;
  673. } else {
  674. return null;
  675. }
  676. }
  677. private Hashtable encode_object(Guid id, Gee.HashMap<string, Value?> db)
  678. {
  679. Hashtable obj = new Hashtable();
  680. if (id != GUID_ZERO)
  681. obj["_guid"] = id.to_string();
  682. string[] keys = db.keys.to_array();
  683. foreach (string key in keys) {
  684. // Since null-key is equivalent to non-existent key, skip serialization.
  685. if (db[key] == null)
  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
  1363. , PropertyDefinition[] properties
  1364. , double ui_order = 0.0
  1365. , ObjectTypeFlags flags = ObjectTypeFlags.NONE
  1366. , string? user_data = null
  1367. )
  1368. {
  1369. StringId64 type_hash = StringId64(type);
  1370. assert(!_object_definitions.has_key(type_hash));
  1371. assert(properties.length > 0);
  1372. ObjectTypeInfo info = {};
  1373. info.properties = { _property_definitions.length, _property_definitions.length + properties.length };
  1374. info.name = type;
  1375. info.ui_order = ui_order;
  1376. info.flags = flags;
  1377. info.user_data = user_data;
  1378. _object_definitions[type_hash] = info;
  1379. foreach (PropertyDefinition def in properties) {
  1380. // Generate labels if missing.
  1381. if (def.label == null) {
  1382. int ld = def.name.last_index_of_char('.');
  1383. string label = ld == -1 ? def.name : def.name.substring(ld + 1);
  1384. def.label = camel_case(label);
  1385. }
  1386. if (def.enum_labels.length == 0) {
  1387. string[] labels = new string[def.enum_values.length];
  1388. for (int i = 0; i < def.enum_values.length; ++i)
  1389. labels[i] = camel_case(def.enum_values[i]);
  1390. def.enum_labels = labels;
  1391. }
  1392. // Assign default/min/max values.
  1393. switch (def.type) {
  1394. case PropertyType.BOOL:
  1395. if (def.deffault == null)
  1396. def.deffault = false;
  1397. assert(def.deffault.holds(typeof(bool)));
  1398. break;
  1399. case PropertyType.DOUBLE:
  1400. if (def.deffault == null)
  1401. def.deffault = 0.0;
  1402. if (def.min == null)
  1403. def.min = double.MIN;
  1404. if (def.max == null)
  1405. def.max = double.MAX;
  1406. assert(def.deffault.holds(typeof(double)));
  1407. assert(def.min.holds(typeof(double)));
  1408. assert(def.max.holds(typeof(double)));
  1409. break;
  1410. case PropertyType.STRING:
  1411. if (def.deffault == null) {
  1412. if (def.enum_values.length > 0)
  1413. def.deffault = def.enum_values[0];
  1414. else
  1415. def.deffault = "";
  1416. }
  1417. assert(def.deffault.holds(typeof(string)));
  1418. break;
  1419. case PropertyType.GUID:
  1420. break;
  1421. case PropertyType.VECTOR3:
  1422. if (def.deffault == null)
  1423. def.deffault = VECTOR3_ZERO;
  1424. if (def.min == null)
  1425. def.min = VECTOR3_MIN;
  1426. if (def.max == null)
  1427. def.max = VECTOR3_MAX;
  1428. assert(def.deffault.holds(typeof(Vector3)));
  1429. assert(def.min.holds(typeof(Vector3)));
  1430. assert(def.max.holds(typeof(Vector3)));
  1431. break;
  1432. case PropertyType.QUATERNION:
  1433. if (def.deffault == null)
  1434. def.deffault = QUATERNION_IDENTITY;
  1435. assert(def.deffault.holds(typeof(Quaternion)));
  1436. break;
  1437. default:
  1438. case PropertyType.OBJECTS_SET:
  1439. break;
  1440. case PropertyType.NULL:
  1441. assert(false);
  1442. break;
  1443. }
  1444. _property_definitions += def;
  1445. }
  1446. object_type_added(info);
  1447. return type_hash;
  1448. }
  1449. // Returns the array of properties (i.e. its definition) of the object @a type.
  1450. public unowned PropertyDefinition[] object_definition(StringId64 type)
  1451. {
  1452. assert(_object_definitions.has_key(type));
  1453. PropertiesSlice ps = _object_definitions[type].properties;
  1454. return _property_definitions[ps.start : ps.end];
  1455. }
  1456. // Returns the name of the object @id. If the object has no name set, it returns
  1457. // OBJECT_NAME_UNNAMED.
  1458. public string object_name(Guid id)
  1459. {
  1460. string name = get_property_string(id, "editor.name", OBJECT_NAME_UNNAMED);
  1461. if (name == OBJECT_NAME_UNNAMED)
  1462. return get_property_string(id, "name", OBJECT_NAME_UNNAMED);
  1463. return name;
  1464. }
  1465. // Sets the @a name of the object @a id.
  1466. public void set_object_name(Guid id, string name)
  1467. {
  1468. set_property_string(id, "editor.name", name);
  1469. }
  1470. // Returns whether the object @a type exists (i.e. has been created with create_object_type()).
  1471. public bool has_object_type(StringId64 type)
  1472. {
  1473. return _object_definitions.has_key(type);
  1474. }
  1475. public string object_type_name(StringId64 type)
  1476. {
  1477. return _object_definitions[type].name;
  1478. }
  1479. public uint object_type_flags(StringId64 type)
  1480. {
  1481. return _object_definitions[type].flags;
  1482. }
  1483. public ObjectTypeInfo object_type_info(StringId64 type)
  1484. {
  1485. return _object_definitions[type];
  1486. }
  1487. }
  1488. } /* namespace Crown */