database.vala 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766
  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. // Skip empty sets.
  688. if (db[key].holds(typeof(Gee.HashSet)) && ((Gee.HashSet)db[key]).size == 0)
  689. continue;
  690. string[] foo = key.split(".");
  691. Hashtable x = obj;
  692. if (foo.length > 1) {
  693. for (int i = 0; i < foo.length - 1; ++i) {
  694. string f = foo[i];
  695. if (x.has_key(f)) {
  696. x = (Hashtable)x[f];
  697. continue;
  698. }
  699. Hashtable y = new Hashtable();
  700. x.set(f, y);
  701. x = y;
  702. }
  703. }
  704. x.set(foo[foo.length - 1], encode_value(db[key]));
  705. }
  706. return obj;
  707. }
  708. private Value? encode_value(Value? value)
  709. {
  710. assert(is_valid_value(value) || value.holds(typeof(Gee.HashSet)));
  711. if (value.holds(typeof(Vector3))) {
  712. Vector3 v = (Vector3)value;
  713. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  714. arr.add(v.x);
  715. arr.add(v.y);
  716. arr.add(v.z);
  717. return arr;
  718. } else if (value.holds(typeof(Quaternion))) {
  719. Quaternion q = (Quaternion)value;
  720. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  721. arr.add(q.x);
  722. arr.add(q.y);
  723. arr.add(q.z);
  724. arr.add(q.w);
  725. return arr;
  726. } else if (value.holds(typeof(Guid))) {
  727. Guid id = (Guid)value;
  728. return id.to_string();
  729. } else if (value.holds(typeof(Gee.HashSet))) {
  730. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  731. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  732. foreach (Guid id in hs) {
  733. arr.add(encode_object(id, get_data(id)));
  734. }
  735. return arr;
  736. } else {
  737. return value;
  738. }
  739. }
  740. private Gee.HashMap<string, Value?> get_data(Guid id)
  741. {
  742. assert(has_object(id));
  743. return _data[id];
  744. }
  745. private void create_internal(int dir, Guid id)
  746. {
  747. assert(id != GUID_ZERO);
  748. if (_debug)
  749. logi("create %s".printf(id.to_string()));
  750. _data[id] = new Gee.HashMap<string, Value?>();
  751. _distance_from_last_sync += dir;
  752. }
  753. private void destroy_internal(int dir, Guid id)
  754. {
  755. assert(id != GUID_ZERO);
  756. assert(has_object(id));
  757. if (_debug)
  758. logi("destroy %s".printf(id.to_string()));
  759. _distance_from_last_sync += dir;
  760. }
  761. public void set_property_internal(int dir, Guid id, string key, Value? value)
  762. {
  763. assert(has_object(id));
  764. assert(is_valid_key(key));
  765. assert(is_valid_value(value));
  766. if (_debug)
  767. logi("set_property %s %s %s".printf(id.to_string(), key, value_to_string(value)));
  768. Gee.HashMap<string, Value?> ob = get_data(id);
  769. ob[key] = value;
  770. _distance_from_last_sync += dir;
  771. }
  772. public void create_empty_set(Guid id, string key)
  773. {
  774. assert(has_object(id));
  775. assert(is_valid_key(key));
  776. Gee.HashMap<string, Value?> ob = get_data(id);
  777. assert(!ob.has_key(key));
  778. ob[key] = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  779. }
  780. private void add_to_set_internal(int dir, Guid id, string key, Guid item_id)
  781. {
  782. assert(has_object(id));
  783. assert(is_valid_key(key));
  784. assert(item_id != GUID_ZERO);
  785. assert(has_object(item_id));
  786. if (_debug)
  787. logi("add_to_set %s %s %s".printf(id.to_string(), key, item_id.to_string()));
  788. Gee.HashMap<string, Value?> ob = get_data(id);
  789. if (!ob.has_key(key)) {
  790. Gee.HashSet<Guid?> hs = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  791. hs.add(item_id);
  792. ob[key] = hs;
  793. } else {
  794. ((Gee.HashSet<Guid?>)ob[key]).add(item_id);
  795. }
  796. _distance_from_last_sync += dir;
  797. }
  798. private void remove_from_set_internal(int dir, Guid id, string key, Guid item_id)
  799. {
  800. assert(has_object(id));
  801. assert(is_valid_key(key));
  802. assert(item_id != GUID_ZERO);
  803. if (_debug)
  804. logi("remove_from_set %s %s %s".printf(id.to_string(), key, item_id.to_string()));
  805. Gee.HashMap<string, Value?> ob = get_data(id);
  806. ((Gee.HashSet<Guid?>)ob[key]).remove(item_id);
  807. _distance_from_last_sync += dir;
  808. }
  809. // Returns the type of the object @a id.
  810. public string object_type(Guid id)
  811. {
  812. assert(has_object(id));
  813. if (id == GUID_ZERO)
  814. return "database";
  815. else
  816. return (string)get_data(id)["_type"];
  817. }
  818. // Sets the @a type of the object @a id.
  819. // This is called automatically when loading data or when new objects are created via create().
  820. // It can occasionally be called manually after loading legacy data with no type information
  821. // stored inside objects.
  822. public void set_object_type(Guid id, string type)
  823. {
  824. assert(has_object(id));
  825. get_data(id)["_type"] = type;
  826. }
  827. public void _init_object(Guid id, PropertyDefinition[] properties)
  828. {
  829. foreach (PropertyDefinition def in properties) {
  830. switch (def.type) {
  831. case PropertyType.BOOL:
  832. set_property_bool(id, def.name, (bool)def.deffault);
  833. break;
  834. case PropertyType.DOUBLE:
  835. set_property_double(id, def.name, (double)def.deffault);
  836. break;
  837. case PropertyType.STRING:
  838. set_property_string(id, def.name, (string)def.deffault);
  839. break;
  840. case PropertyType.GUID:
  841. set_property_guid(id, def.name, (Guid)def.deffault);
  842. break;
  843. case PropertyType.VECTOR3:
  844. set_property_vector3(id, def.name, (Vector3)def.deffault);
  845. break;
  846. case PropertyType.QUATERNION:
  847. set_property_quaternion(id, def.name, (Quaternion)def.deffault);
  848. break;
  849. case PropertyType.OBJECTS_SET:
  850. create_empty_set(id, def.name);
  851. break;
  852. case PropertyType.NULL:
  853. break;
  854. }
  855. }
  856. }
  857. public void create(Guid id, string type)
  858. {
  859. assert(id != GUID_ZERO);
  860. assert(!has_object(id));
  861. if (_undo_redo != null) {
  862. _undo_redo._undo.write_destroy_action(Action.DESTROY, id, type);
  863. _undo_redo._redo.clear();
  864. }
  865. create_internal(1, id);
  866. set_object_type(id, type);
  867. StringId64 type_hash = StringId64(type);
  868. if (has_object_type(type_hash))
  869. _init_object(id, object_definition(type_hash));
  870. }
  871. public void destroy(Guid id)
  872. {
  873. assert(id != GUID_ZERO);
  874. assert(has_object(id));
  875. string obj_type = object_type(id);
  876. Gee.HashMap<string, Value?> o = get_data(id);
  877. string[] keys = o.keys.to_array();
  878. foreach (string key in keys) {
  879. Value? value = o[key];
  880. if (value.holds(typeof(Gee.HashSet))) {
  881. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  882. Guid?[] ids = hs.to_array();
  883. foreach (Guid item_id in ids) {
  884. remove_from_set(id, key, item_id);
  885. destroy(item_id);
  886. }
  887. } else {
  888. if (key != "type" && key != "_type")
  889. set_property_null(id, key);
  890. }
  891. }
  892. if (_undo_redo != null) {
  893. _undo_redo._undo.write_create_action(Action.CREATE, id, obj_type);
  894. _undo_redo._redo.clear();
  895. }
  896. destroy_internal(1, id);
  897. }
  898. public void set_property_null(Guid id, string key)
  899. {
  900. assert(has_object(id));
  901. assert(is_valid_key(key));
  902. assert(is_valid_value(null));
  903. if (_undo_redo != null) {
  904. Gee.HashMap<string, Value?> ob = get_data(id);
  905. if (ob.has_key(key) && ob[key] != null) {
  906. if (ob[key].holds(typeof(bool)))
  907. _undo_redo._undo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, (bool)ob[key]);
  908. if (ob[key].holds(typeof(double)))
  909. _undo_redo._undo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, (double)ob[key]);
  910. if (ob[key].holds(typeof(string)))
  911. _undo_redo._undo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, (string)ob[key]);
  912. if (ob[key].holds(typeof(Guid)))
  913. _undo_redo._undo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, (Guid)ob[key]);
  914. if (ob[key].holds(typeof(Vector3)))
  915. _undo_redo._undo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, (Vector3)ob[key]);
  916. if (ob[key].holds(typeof(Quaternion)))
  917. _undo_redo._undo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, (Quaternion)ob[key]);
  918. } else {
  919. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  920. }
  921. _undo_redo._redo.clear();
  922. }
  923. set_property_internal(1, id, key, null);
  924. }
  925. public void set_property_bool(Guid id, string key, bool val)
  926. {
  927. assert(has_object(id));
  928. assert(is_valid_key(key));
  929. assert(is_valid_value(val));
  930. if (_undo_redo != null) {
  931. Gee.HashMap<string, Value?> ob = get_data(id);
  932. if (ob.has_key(key) && ob[key] != null)
  933. _undo_redo._undo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, (bool)ob[key]);
  934. else
  935. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  936. _undo_redo._redo.clear();
  937. }
  938. set_property_internal(1, id, key, val);
  939. }
  940. public void set_property_double(Guid id, string key, double val)
  941. {
  942. assert(has_object(id));
  943. assert(is_valid_key(key));
  944. assert(is_valid_value(val));
  945. if (_undo_redo != null) {
  946. Gee.HashMap<string, Value?> ob = get_data(id);
  947. if (ob.has_key(key) && ob[key] != null)
  948. _undo_redo._undo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, (double)ob[key]);
  949. else
  950. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  951. _undo_redo._redo.clear();
  952. }
  953. set_property_internal(1, id, key, val);
  954. }
  955. public void set_property_string(Guid id, string key, string val)
  956. {
  957. assert(has_object(id));
  958. assert(is_valid_key(key));
  959. assert(is_valid_value(val));
  960. if (_undo_redo != null) {
  961. Gee.HashMap<string, Value?> ob = get_data(id);
  962. if (ob.has_key(key) && ob[key] != null)
  963. _undo_redo._undo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, (string)ob[key]);
  964. else
  965. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  966. _undo_redo._redo.clear();
  967. }
  968. set_property_internal(1, id, key, val);
  969. }
  970. public void set_property_guid(Guid id, string key, Guid val)
  971. {
  972. assert(has_object(id));
  973. assert(is_valid_key(key));
  974. assert(is_valid_value(val));
  975. if (_undo_redo != null) {
  976. Gee.HashMap<string, Value?> ob = get_data(id);
  977. if (ob.has_key(key) && ob[key] != null)
  978. _undo_redo._undo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, (Guid)ob[key]);
  979. else
  980. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  981. _undo_redo._redo.clear();
  982. }
  983. set_property_internal(1, id, key, val);
  984. }
  985. public void set_property_vector3(Guid id, string key, Vector3 val)
  986. {
  987. assert(has_object(id));
  988. assert(is_valid_key(key));
  989. assert(is_valid_value(val));
  990. if (_undo_redo != null) {
  991. Gee.HashMap<string, Value?> ob = get_data(id);
  992. if (ob.has_key(key) && ob[key] != null)
  993. _undo_redo._undo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, (Vector3)ob[key]);
  994. else
  995. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  996. _undo_redo._redo.clear();
  997. }
  998. set_property_internal(1, id, key, val);
  999. }
  1000. public void set_property_quaternion(Guid id, string key, Quaternion val)
  1001. {
  1002. assert(has_object(id));
  1003. assert(is_valid_key(key));
  1004. assert(is_valid_value(val));
  1005. if (_undo_redo != null) {
  1006. Gee.HashMap<string, Value?> ob = get_data(id);
  1007. if (ob.has_key(key) && ob[key] != null)
  1008. _undo_redo._undo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, (Quaternion)ob[key]);
  1009. else
  1010. _undo_redo._undo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1011. _undo_redo._redo.clear();
  1012. }
  1013. set_property_internal(1, id, key, val);
  1014. }
  1015. public void set_property(Guid id, string key, Value? val)
  1016. {
  1017. if (val == null)
  1018. set_property_null(id, key);
  1019. if (val.holds(typeof(bool)))
  1020. set_property_bool(id, key, (bool)val);
  1021. else if (val.holds(typeof(double)))
  1022. set_property_double(id, key, (double)val);
  1023. else if (val.holds(typeof(string)))
  1024. set_property_string(id, key, (string)val);
  1025. else if (val.holds(typeof(Guid)))
  1026. set_property_guid(id, key, (Guid)val);
  1027. else if (val.holds(typeof(Vector3)))
  1028. set_property_vector3(id, key, (Vector3)val);
  1029. else if (val.holds(typeof(Quaternion)))
  1030. set_property_quaternion(id, key, (Quaternion)val);
  1031. else
  1032. assert(false);
  1033. }
  1034. public void add_to_set(Guid id, string key, Guid item_id)
  1035. {
  1036. assert(has_object(id));
  1037. assert(is_valid_key(key));
  1038. assert(item_id != GUID_ZERO);
  1039. assert(has_object(item_id));
  1040. if (_undo_redo != null) {
  1041. _undo_redo._undo.write_remove_from_set_action(Action.REMOVE_FROM_SET, id, key, item_id);
  1042. _undo_redo._redo.clear();
  1043. }
  1044. add_to_set_internal(1, id, key, item_id);
  1045. }
  1046. public void remove_from_set(Guid id, string key, Guid item_id)
  1047. {
  1048. assert(has_object(id));
  1049. assert(is_valid_key(key));
  1050. assert(item_id != GUID_ZERO);
  1051. if (_undo_redo != null) {
  1052. _undo_redo._undo.write_add_to_set_action(Action.ADD_TO_SET, id, key, item_id);
  1053. _undo_redo._redo.clear();
  1054. }
  1055. remove_from_set_internal(1, id, key, item_id);
  1056. }
  1057. public bool has_object(Guid id)
  1058. {
  1059. return id == GUID_ZERO || _data.has_key(id);
  1060. }
  1061. public bool has_property(Guid id, string key)
  1062. {
  1063. return get_property(id, key) != null;
  1064. }
  1065. public Value? get_property(Guid id, string key, Value? val = null)
  1066. {
  1067. assert(has_object(id));
  1068. assert(is_valid_key(key));
  1069. Gee.HashMap<string, Value?> ob = get_data(id);
  1070. Value? value = (ob.has_key(key) ? ob[key] : val);
  1071. if (_debug_getters)
  1072. logi("get_property %s %s %s".printf(id.to_string(), key, value_to_string(value)));
  1073. return value;
  1074. }
  1075. public bool get_property_bool(Guid id, string key, bool deffault = false)
  1076. {
  1077. return (bool)get_property(id, key, deffault);
  1078. }
  1079. public double get_property_double(Guid id, string key, double deffault = 0.0)
  1080. {
  1081. return (double)get_property(id, key, deffault);
  1082. }
  1083. public string get_property_string(Guid id, string key, string deffault = "")
  1084. {
  1085. return (string)get_property(id, key, deffault);
  1086. }
  1087. public Guid get_property_guid(Guid id, string key, Guid deffault = GUID_ZERO)
  1088. {
  1089. return (Guid)get_property(id, key, deffault);
  1090. }
  1091. public Vector3 get_property_vector3(Guid id, string key, Vector3 deffault = VECTOR3_ZERO)
  1092. {
  1093. return (Vector3)get_property(id, key, deffault);
  1094. }
  1095. public Quaternion get_property_quaternion(Guid id, string key, Quaternion deffault = QUATERNION_IDENTITY)
  1096. {
  1097. return (Quaternion)get_property(id, key, deffault);
  1098. }
  1099. public Gee.HashSet<Guid?> get_property_set(Guid id, string key, Gee.HashSet<Guid?> deffault)
  1100. {
  1101. assert(has_object(id));
  1102. assert(is_valid_key(key));
  1103. Gee.HashMap<string, Value?> ob = get_data(id);
  1104. Gee.HashSet<Guid?> value;
  1105. if (ob.has_key(key))
  1106. value = ob[key] as Gee.HashSet<Guid?>;
  1107. else
  1108. value = deffault;
  1109. if (_debug_getters)
  1110. logi("get_property %s %s %s".printf(id.to_string(), key, value_to_string(value)));
  1111. return value;
  1112. }
  1113. public Gee.HashMap<string, Value?> get_object(Guid id)
  1114. {
  1115. return (Gee.HashMap<string, Value?>)get_data(GUID_ZERO)[id.to_string()];
  1116. }
  1117. public string[] get_keys(Guid id)
  1118. {
  1119. Gee.HashMap<string, Value?> data = get_data(id);
  1120. return data.keys.to_array();
  1121. }
  1122. public void add_restore_point(int id, Guid?[] data, uint32 flags = 0u)
  1123. {
  1124. if (_debug)
  1125. logi("add_restore_point %d, undo size = %u".printf(id, _undo_redo._undo.size()));
  1126. if (_undo_redo != null) {
  1127. _undo_redo._undo.write_restore_point(id, flags, data);
  1128. _undo_redo._redo.clear();
  1129. restore_point_added(id, data, flags);
  1130. }
  1131. }
  1132. /// Duplicates the object specified by id and assign new_id to the duplicated object.
  1133. public void duplicate(Guid id, Guid new_id, Database? dest = null)
  1134. {
  1135. assert(id != GUID_ZERO);
  1136. assert(new_id != GUID_ZERO);
  1137. assert(id != new_id);
  1138. assert(has_object(id));
  1139. if (dest == null)
  1140. dest = this;
  1141. dest.create(new_id, object_type(id));
  1142. Gee.HashMap<string, Value?> ob = get_data(id);
  1143. string[] keys = ob.keys.to_array();
  1144. foreach (string key in keys) {
  1145. Value? val = ob[key];
  1146. if (val.holds(typeof(Gee.HashSet))) {
  1147. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)val;
  1148. foreach (Guid j in hs) {
  1149. Guid x = Guid.new_guid();
  1150. duplicate(j, x, dest);
  1151. dest.add_to_set(new_id, key, x);
  1152. }
  1153. } else {
  1154. if (ob[key] == null)
  1155. dest.set_property_null(new_id, key);
  1156. else if (ob[key].holds(typeof(bool)))
  1157. dest.set_property_bool(new_id, key, (bool)ob[key]);
  1158. else if (ob[key].holds(typeof(double)))
  1159. dest.set_property_double(new_id, key, (double)ob[key]);
  1160. else if (ob[key].holds(typeof(string)))
  1161. dest.set_property_string(new_id, key, (string)ob[key]);
  1162. else if (ob[key].holds(typeof(Guid)))
  1163. dest.set_property_guid(new_id, key, (Guid)ob[key]);
  1164. else if (ob[key].holds(typeof(Vector3)))
  1165. dest.set_property_vector3(new_id, key, (Vector3)ob[key]);
  1166. else if (ob[key].holds(typeof(Quaternion)))
  1167. dest.set_property_quaternion(new_id, key, (Quaternion)ob[key]);
  1168. else
  1169. assert(false);
  1170. }
  1171. }
  1172. }
  1173. /// Copies the database to db under the given new_key.
  1174. public void copy_to(Database db, string new_key)
  1175. {
  1176. assert(db != null);
  1177. assert(is_valid_key(new_key));
  1178. copy_deep(db, GUID_ZERO, new_key);
  1179. }
  1180. public void copy_deep(Database db, Guid id, string new_key)
  1181. {
  1182. Gee.HashMap<string, Value?> ob = get_data(id);
  1183. string[] keys = ob.keys.to_array();
  1184. foreach (string key in keys) {
  1185. Value? value = ob[key];
  1186. if (value.holds(typeof(Gee.HashSet))) {
  1187. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  1188. foreach (Guid j in hs) {
  1189. db.create(j, object_type(j));
  1190. copy_deep(db, j, "");
  1191. db.add_to_set(id, new_key + (new_key == "" ? "" : ".") + key, j);
  1192. }
  1193. } else {
  1194. if (!db.has_object(id))
  1195. db.create(id, object_type(id));
  1196. string kk = new_key + (new_key == "" ? "" : ".") + key;
  1197. if (ob[key] == null)
  1198. db.set_property_null(id, kk);
  1199. if (ob[key].holds(typeof(bool)))
  1200. db.set_property_bool(id, kk, (bool)ob[key]);
  1201. if (ob[key].holds(typeof(double)))
  1202. db.set_property_double(id, kk, (double)ob[key]);
  1203. if (ob[key].holds(typeof(string)))
  1204. db.set_property_string(id, kk, (string)ob[key]);
  1205. if (ob[key].holds(typeof(Guid)))
  1206. db.set_property_guid(id, kk, (Guid)ob[key]);
  1207. if (ob[key].holds(typeof(Vector3)))
  1208. db.set_property_vector3(id, kk, (Vector3)ob[key]);
  1209. if (ob[key].holds(typeof(Quaternion)))
  1210. db.set_property_quaternion(id, kk, (Quaternion)ob[key]);
  1211. }
  1212. }
  1213. }
  1214. // Tries to read a restore point @a rp from the @a stack and returns
  1215. // 0 if successful.
  1216. private int try_read_restore_point(ref RestorePoint rp, Stack stack)
  1217. {
  1218. if (stack.size() < sizeof(Action) + sizeof(RestorePointHeader))
  1219. return -1;
  1220. rp = stack.read_restore_point();
  1221. if (stack.size() < rp.header.size) {
  1222. // The restore point has been overwritten.
  1223. stack.clear();
  1224. return -1;
  1225. }
  1226. return 0;
  1227. }
  1228. // Un-does the last action and returns its ID, or -1 if there is no
  1229. // action to undo.
  1230. public int undo()
  1231. {
  1232. if (_undo_redo == null)
  1233. return -1;
  1234. RestorePoint rp = {};
  1235. if (try_read_restore_point(ref rp, _undo_redo._undo) != 0)
  1236. return -1;
  1237. undo_or_redo(_undo_redo._undo, _undo_redo._redo, rp.header.size);
  1238. undo_redo(true, rp.header.id, rp.data);
  1239. _undo_redo._redo.write_restore_point(rp.header.id, rp.header.flags, rp.data);
  1240. return (int)rp.header.id;
  1241. }
  1242. // Re-does the last action and returns its ID, or -1 if there is no
  1243. // action to redo.
  1244. public int redo()
  1245. {
  1246. if (_undo_redo == null)
  1247. return -1;
  1248. RestorePoint rp = {};
  1249. if (try_read_restore_point(ref rp, _undo_redo._redo) != 0)
  1250. return -1;
  1251. undo_or_redo(_undo_redo._redo, _undo_redo._undo, rp.header.size);
  1252. undo_redo(false, rp.header.id, rp.data);
  1253. _undo_redo._undo.write_restore_point(rp.header.id, rp.header.flags, rp.data);
  1254. return (int)rp.header.id;
  1255. }
  1256. private void undo_or_redo(Stack undo, Stack redo, uint32 restore_point_size)
  1257. {
  1258. assert(undo.size() >= restore_point_size);
  1259. int dir = undo == _undo_redo._undo ? -1 : 1;
  1260. // Read up to restore_point_size bytes.
  1261. uint32 undo_size_start = undo.size();
  1262. while (undo_size_start - undo.size() < restore_point_size) {
  1263. Action action = (Action)undo.read_uint32();
  1264. if (action == Action.CREATE) {
  1265. Guid id = undo.read_guid();
  1266. string obj_type = undo.read_string();
  1267. redo.write_destroy_action(Action.DESTROY, id, obj_type);
  1268. create_internal(dir, id);
  1269. set_object_type(id, obj_type);
  1270. } else if (action == Action.DESTROY) {
  1271. Guid id = undo.read_guid();
  1272. string obj_type = undo.read_string();
  1273. redo.write_create_action(Action.CREATE, id, obj_type);
  1274. destroy_internal(dir, id);
  1275. } else if (action == Action.SET_PROPERTY_NULL) {
  1276. Guid id = undo.read_guid();
  1277. string key = undo.read_string();
  1278. if (has_property(id, key)) {
  1279. if (get_data(id)[key].holds(typeof(bool)))
  1280. redo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, get_property_bool(id, key));
  1281. if (get_data(id)[key].holds(typeof(double)))
  1282. redo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, get_property_double(id, key));
  1283. if (get_data(id)[key].holds(typeof(string)))
  1284. redo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, get_property_string(id, key));
  1285. if (get_data(id)[key].holds(typeof(Guid)))
  1286. redo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, get_property_guid(id, key));
  1287. if (get_data(id)[key].holds(typeof(Vector3)))
  1288. redo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, get_property_vector3(id, key));
  1289. if (get_data(id)[key].holds(typeof(Quaternion)))
  1290. redo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, get_property_quaternion(id, key));
  1291. } else {
  1292. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1293. }
  1294. set_property_internal(dir, id, key, null);
  1295. } else if (action == Action.SET_PROPERTY_BOOL) {
  1296. Guid id = undo.read_guid();
  1297. string key = undo.read_string();
  1298. bool val = undo.read_bool();
  1299. if (has_property(id, key))
  1300. redo.write_set_property_bool_action(Action.SET_PROPERTY_BOOL, id, key, get_property_bool(id, key));
  1301. else
  1302. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1303. set_property_internal(dir, id, key, val);
  1304. } else if (action == Action.SET_PROPERTY_DOUBLE) {
  1305. Guid id = undo.read_guid();
  1306. string key = undo.read_string();
  1307. double val = undo.read_double();
  1308. if (has_property(id, key))
  1309. redo.write_set_property_double_action(Action.SET_PROPERTY_DOUBLE, id, key, get_property_double(id, key));
  1310. else
  1311. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1312. set_property_internal(dir, id, key, val);
  1313. } else if (action == Action.SET_PROPERTY_STRING) {
  1314. Guid id = undo.read_guid();
  1315. string key = undo.read_string();
  1316. string val = undo.read_string();
  1317. if (has_property(id, key))
  1318. redo.write_set_property_string_action(Action.SET_PROPERTY_STRING, id, key, get_property_string(id, key));
  1319. else
  1320. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1321. set_property_internal(dir, id, key, val);
  1322. } else if (action == Action.SET_PROPERTY_GUID) {
  1323. Guid id = undo.read_guid();
  1324. string key = undo.read_string();
  1325. Guid val = undo.read_guid();
  1326. if (has_property(id, key))
  1327. redo.write_set_property_guid_action(Action.SET_PROPERTY_GUID, id, key, get_property_guid(id, key));
  1328. else
  1329. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1330. set_property_internal(dir, id, key, val);
  1331. } else if (action == Action.SET_PROPERTY_VECTOR3) {
  1332. Guid id = undo.read_guid();
  1333. string key = undo.read_string();
  1334. Vector3 val = undo.read_vector3();
  1335. if (has_property(id, key))
  1336. redo.write_set_property_vector3_action(Action.SET_PROPERTY_VECTOR3, id, key, get_property_vector3(id, key));
  1337. else
  1338. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1339. set_property_internal(dir, id, key, val);
  1340. } else if (action == Action.SET_PROPERTY_QUATERNION) {
  1341. Guid id = undo.read_guid();
  1342. string key = undo.read_string();
  1343. Quaternion val = undo.read_quaternion();
  1344. if (has_property(id, key))
  1345. redo.write_set_property_quaternion_action(Action.SET_PROPERTY_QUATERNION, id, key, get_property_quaternion(id, key));
  1346. else
  1347. redo.write_set_property_null_action(Action.SET_PROPERTY_NULL, id, key);
  1348. set_property_internal(dir, id, key, val);
  1349. } else if (action == Action.ADD_TO_SET) {
  1350. Guid id = undo.read_guid();
  1351. string key = undo.read_string();
  1352. Guid item_id = undo.read_guid();
  1353. redo.write_remove_from_set_action(Action.REMOVE_FROM_SET, id, key, item_id);
  1354. add_to_set_internal(dir, id, key, item_id);
  1355. } else if (action == Action.REMOVE_FROM_SET) {
  1356. Guid id = undo.read_guid();
  1357. string key = undo.read_string();
  1358. Guid item_id = undo.read_guid();
  1359. redo.write_add_to_set_action(Action.ADD_TO_SET, id, key, item_id);
  1360. remove_from_set_internal(dir, id, key, item_id);
  1361. }
  1362. }
  1363. }
  1364. // Creates a new object @a type with the specified @a properties and returns its ID.
  1365. public StringId64 create_object_type(string type
  1366. , PropertyDefinition[] properties
  1367. , double ui_order = 0.0
  1368. , ObjectTypeFlags flags = ObjectTypeFlags.NONE
  1369. , string? user_data = null
  1370. )
  1371. {
  1372. StringId64 type_hash = StringId64(type);
  1373. assert(!_object_definitions.has_key(type_hash));
  1374. assert(properties.length > 0);
  1375. ObjectTypeInfo info = {};
  1376. info.properties = { _property_definitions.length, _property_definitions.length + properties.length };
  1377. info.name = type;
  1378. info.ui_order = ui_order;
  1379. info.flags = flags;
  1380. info.user_data = user_data;
  1381. _object_definitions[type_hash] = info;
  1382. foreach (PropertyDefinition def in properties) {
  1383. // Generate labels if missing.
  1384. if (def.label == null) {
  1385. int ld = def.name.last_index_of_char('.');
  1386. string label = ld == -1 ? def.name : def.name.substring(ld + 1);
  1387. def.label = camel_case(label);
  1388. }
  1389. if (def.enum_labels.length == 0) {
  1390. string[] labels = new string[def.enum_values.length];
  1391. for (int i = 0; i < def.enum_values.length; ++i)
  1392. labels[i] = camel_case(def.enum_values[i]);
  1393. def.enum_labels = labels;
  1394. }
  1395. // Assign default/min/max values.
  1396. switch (def.type) {
  1397. case PropertyType.BOOL:
  1398. if (def.deffault == null)
  1399. def.deffault = false;
  1400. assert(def.deffault.holds(typeof(bool)));
  1401. break;
  1402. case PropertyType.DOUBLE:
  1403. if (def.deffault == null)
  1404. def.deffault = 0.0;
  1405. if (def.min == null)
  1406. def.min = double.MIN;
  1407. if (def.max == null)
  1408. def.max = double.MAX;
  1409. assert(def.deffault.holds(typeof(double)));
  1410. assert(def.min.holds(typeof(double)));
  1411. assert(def.max.holds(typeof(double)));
  1412. break;
  1413. case PropertyType.STRING:
  1414. if (def.deffault == null) {
  1415. if (def.enum_values.length > 0)
  1416. def.deffault = def.enum_values[0];
  1417. else
  1418. def.deffault = "";
  1419. }
  1420. assert(def.deffault.holds(typeof(string)));
  1421. break;
  1422. case PropertyType.GUID:
  1423. break;
  1424. case PropertyType.VECTOR3:
  1425. if (def.deffault == null)
  1426. def.deffault = VECTOR3_ZERO;
  1427. if (def.min == null)
  1428. def.min = VECTOR3_MIN;
  1429. if (def.max == null)
  1430. def.max = VECTOR3_MAX;
  1431. assert(def.deffault.holds(typeof(Vector3)));
  1432. assert(def.min.holds(typeof(Vector3)));
  1433. assert(def.max.holds(typeof(Vector3)));
  1434. break;
  1435. case PropertyType.QUATERNION:
  1436. if (def.deffault == null)
  1437. def.deffault = QUATERNION_IDENTITY;
  1438. assert(def.deffault.holds(typeof(Quaternion)));
  1439. break;
  1440. default:
  1441. case PropertyType.OBJECTS_SET:
  1442. break;
  1443. case PropertyType.NULL:
  1444. assert(false);
  1445. break;
  1446. }
  1447. _property_definitions += def;
  1448. }
  1449. object_type_added(info);
  1450. return type_hash;
  1451. }
  1452. // Returns the array of properties (i.e. its definition) of the object @a type.
  1453. public unowned PropertyDefinition[] object_definition(StringId64 type)
  1454. {
  1455. assert(_object_definitions.has_key(type));
  1456. PropertiesSlice ps = _object_definitions[type].properties;
  1457. return _property_definitions[ps.start : ps.end];
  1458. }
  1459. // Returns the name of the object @id. If the object has no name set, it returns
  1460. // OBJECT_NAME_UNNAMED.
  1461. public string object_name(Guid id)
  1462. {
  1463. string name = get_property_string(id, "editor.name", OBJECT_NAME_UNNAMED);
  1464. if (name == OBJECT_NAME_UNNAMED)
  1465. return get_property_string(id, "name", OBJECT_NAME_UNNAMED);
  1466. return name;
  1467. }
  1468. // Sets the @a name of the object @a id.
  1469. public void set_object_name(Guid id, string name)
  1470. {
  1471. set_property_string(id, "editor.name", name);
  1472. }
  1473. // Returns whether the object @a type exists (i.e. has been created with create_object_type()).
  1474. public bool has_object_type(StringId64 type)
  1475. {
  1476. return _object_definitions.has_key(type);
  1477. }
  1478. public string object_type_name(StringId64 type)
  1479. {
  1480. return _object_definitions[type].name;
  1481. }
  1482. public uint object_type_flags(StringId64 type)
  1483. {
  1484. return _object_definitions[type].flags;
  1485. }
  1486. public ObjectTypeInfo object_type_info(StringId64 type)
  1487. {
  1488. return _object_definitions[type];
  1489. }
  1490. }
  1491. } /* namespace Crown */