database.vala 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154
  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. BOOL,
  10. DOUBLE,
  11. STRING,
  12. VECTOR3,
  13. QUATERNION,
  14. RESOURCE,
  15. REFERENCE,
  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. public bool not_serialized;
  46. }
  47. public struct Resource
  48. {
  49. public string? name;
  50. }
  51. public enum UndoRedoAction
  52. {
  53. RESTORE_POINT = int.MAX;
  54. }
  55. public struct RestorePointHeader
  56. {
  57. public uint32 id;
  58. public uint32 flags;
  59. public uint32 size;
  60. public uint32 num_guids;
  61. }
  62. public struct RestorePoint
  63. {
  64. public RestorePointHeader header;
  65. public Guid?[] data;
  66. }
  67. public class Stack
  68. {
  69. public uint32 _capacity;
  70. public uint8[] _data;
  71. public uint32 _read; // Position of the read/write head.
  72. public uint32 _size; // Size of the data written in the stack.
  73. public uint32 _last_write_restore_point_size; // Size when write_restore_point() was last called.
  74. ///
  75. public Stack(uint32 capacity)
  76. {
  77. assert(capacity > 0);
  78. _capacity = capacity;
  79. _data = new uint8[_capacity];
  80. clear();
  81. }
  82. ///
  83. public uint32 size()
  84. {
  85. return _size;
  86. }
  87. ///
  88. public void clear()
  89. {
  90. _read = 0;
  91. _size = 0;
  92. _last_write_restore_point_size = 0;
  93. }
  94. // Copies @a data into @a destination.
  95. public void copy_data(uint8* destination, void* data, ulong len)
  96. {
  97. uint8* source = (uint8*)data;
  98. for (ulong ii = 0; ii < len; ++ii)
  99. destination[ii] = source[ii];
  100. }
  101. // Writes @a data into the current page.
  102. public void write_data_internal(uint8* data, uint32 len)
  103. {
  104. assert(data != null);
  105. uint32 bytes_left = len;
  106. uint32 bytes_avail;
  107. // Write the data that wraps around.
  108. while (bytes_left > (bytes_avail = _capacity - _read)) {
  109. copy_data(&_data[_read]
  110. , ((uint8*)data) + (len - bytes_left)
  111. , bytes_avail
  112. );
  113. _read = (_read + bytes_avail) % _capacity;
  114. _size = uint32.min(_capacity, _size + bytes_avail);
  115. bytes_left -= bytes_avail;
  116. }
  117. // Write the remaining data.
  118. copy_data(&_data[_read]
  119. , ((uint8*)data) + (len - bytes_left)
  120. , bytes_left
  121. );
  122. _read += bytes_left;
  123. _size = uint32.min(_capacity, _size + bytes_left);
  124. _last_write_restore_point_size += len;
  125. }
  126. // Wrapper to avoid casting sizeof() manually.
  127. public void write_data(void* data, ulong len)
  128. {
  129. write_data_internal((uint8*)data, (uint32)len);
  130. }
  131. public void read_data_internal(uint8* data, uint32 len)
  132. {
  133. assert(data != null);
  134. uint32 bytes_left = len;
  135. // Read the data that wraps around.
  136. while (bytes_left > _read) {
  137. copy_data(data + bytes_left - _read
  138. , &_data[0]
  139. , _read
  140. );
  141. bytes_left -= _read;
  142. _size -= _read;
  143. assert(_size <= _capacity);
  144. _read = _capacity;
  145. }
  146. // Read the remaining data.
  147. copy_data(data
  148. , &_data[_read - bytes_left]
  149. , bytes_left
  150. );
  151. _read -= bytes_left;
  152. _size -= bytes_left;
  153. assert(_size <= _capacity);
  154. }
  155. // Wrapper to avoid casting sizeof() manually.
  156. public void read_data(void* data, ulong size)
  157. {
  158. read_data_internal((uint8*)data, (uint32)size);
  159. }
  160. public void write_bool(bool a)
  161. {
  162. write_data(&a, sizeof(bool));
  163. }
  164. public void write_uint32(uint32 a)
  165. {
  166. write_data(&a, sizeof(uint32));
  167. }
  168. public void write_double(double a)
  169. {
  170. write_data(&a, sizeof(double));
  171. }
  172. public void write_string(string str)
  173. {
  174. uint32 len = str.length;
  175. write_data(&str.data[0], len);
  176. write_data(&len, sizeof(uint32));
  177. }
  178. public void write_guid(Guid a)
  179. {
  180. write_data(&a, sizeof(Guid));
  181. }
  182. public void write_vector3(Vector3 a)
  183. {
  184. write_data(&a, sizeof(Vector3));
  185. }
  186. public void write_quaternion(Quaternion a)
  187. {
  188. write_data(&a, sizeof(Quaternion));
  189. }
  190. public bool read_bool()
  191. {
  192. bool a = false;
  193. read_data(&a, sizeof(bool));
  194. return a;
  195. }
  196. public int read_int()
  197. {
  198. int a = 0;
  199. read_data(&a, sizeof(int));
  200. return a;
  201. }
  202. public uint32 read_uint32()
  203. {
  204. uint32 a = 0;
  205. read_data(&a, sizeof(uint32));
  206. return a;
  207. }
  208. public double read_double()
  209. {
  210. double a = 0;
  211. read_data(&a, sizeof(double));
  212. return a;
  213. }
  214. public Guid read_guid()
  215. {
  216. Guid a = GUID_ZERO;
  217. read_data(&a, sizeof(Guid));
  218. return a;
  219. }
  220. public Vector3 read_vector3()
  221. {
  222. Vector3 a = VECTOR3_ZERO;
  223. read_data(&a, sizeof(Vector3));
  224. return a;
  225. }
  226. public Quaternion read_quaternion()
  227. {
  228. Quaternion a = QUATERNION_IDENTITY;
  229. read_data(&a, sizeof(Quaternion));
  230. return a;
  231. }
  232. public string read_string()
  233. {
  234. uint32 len = 0;
  235. read_data(&len, sizeof(uint32));
  236. uint8[] str = new uint8[len + 1];
  237. read_data(str, len);
  238. str[len] = '\0';
  239. return (string)str;
  240. }
  241. public Resource read_resource()
  242. {
  243. string name = read_string();
  244. Resource resource = { name == "" ? null : name };
  245. return resource;
  246. }
  247. public void write_create_action(uint32 action, Guid id, string type)
  248. {
  249. write_string(type);
  250. write_guid(id);
  251. write_uint32(action);
  252. }
  253. public void write_destroy_action(uint32 action, Guid id, string type)
  254. {
  255. write_string(type);
  256. write_guid(id);
  257. write_uint32(action);
  258. }
  259. public void write_set_null_action(uint32 action, Guid id, string key)
  260. {
  261. // No value to push
  262. write_string(key);
  263. write_guid(id);
  264. write_uint32(action);
  265. }
  266. public void write_set_bool_action(uint32 action, Guid id, string key, bool val)
  267. {
  268. write_bool(val);
  269. write_string(key);
  270. write_guid(id);
  271. write_uint32(action);
  272. }
  273. public void write_set_double_action(uint32 action, Guid id, string key, double val)
  274. {
  275. write_double(val);
  276. write_string(key);
  277. write_guid(id);
  278. write_uint32(action);
  279. }
  280. public void write_set_string_action(uint32 action, Guid id, string key, string val)
  281. {
  282. write_string(val);
  283. write_string(key);
  284. write_guid(id);
  285. write_uint32(action);
  286. }
  287. public void write_set_vector3_action(uint32 action, Guid id, string key, Vector3 val)
  288. {
  289. write_vector3(val);
  290. write_string(key);
  291. write_guid(id);
  292. write_uint32(action);
  293. }
  294. public void write_set_quaternion_action(uint32 action, Guid id, string key, Quaternion val)
  295. {
  296. write_quaternion(val);
  297. write_string(key);
  298. write_guid(id);
  299. write_uint32(action);
  300. }
  301. public void write_set_resource_action(uint32 action, Guid id, string key, Resource val)
  302. {
  303. write_string(val.name == null ? "" : val.name);
  304. write_string(key);
  305. write_guid(id);
  306. write_uint32(action);
  307. }
  308. public void write_set_reference_action(uint32 action, Guid id, string key, Guid val)
  309. {
  310. write_guid(val);
  311. write_string(key);
  312. write_guid(id);
  313. write_uint32(action);
  314. }
  315. public void write_add_to_set_action(uint32 action, Guid id, string key, Guid item_id)
  316. {
  317. write_guid(item_id);
  318. write_string(key);
  319. write_guid(id);
  320. write_uint32(action);
  321. }
  322. public void write_remove_from_set_action(uint32 action, Guid id, string key, Guid item_id)
  323. {
  324. write_guid(item_id);
  325. write_string(key);
  326. write_guid(id);
  327. write_uint32(action);
  328. }
  329. public void write_restore_point(uint32 id, uint32 flags, Guid?[] data)
  330. {
  331. uint32 size = _last_write_restore_point_size;
  332. uint32 num_guids = data.length;
  333. for (uint32 i = 0; i < num_guids; ++i)
  334. write_guid(data[num_guids - 1 - i]);
  335. write_uint32(num_guids);
  336. write_uint32(size);
  337. write_uint32(flags);
  338. write_uint32(id);
  339. write_uint32(UndoRedoAction.RESTORE_POINT);
  340. _last_write_restore_point_size = 0;
  341. }
  342. public RestorePoint read_restore_point()
  343. {
  344. uint32 t = read_uint32();
  345. assert(t == UndoRedoAction.RESTORE_POINT);
  346. uint32 id = read_uint32();
  347. uint32 flags = read_uint32();
  348. uint32 size = read_uint32();
  349. uint32 num_guids = read_uint32();
  350. Guid?[] ids = new Guid?[num_guids];
  351. for (uint32 i = 0; i < num_guids; ++i)
  352. ids[i] = read_guid();
  353. RestorePointHeader rph = { id, flags, size, num_guids };
  354. return { rph, ids };
  355. }
  356. }
  357. public class UndoRedo
  358. {
  359. public Stack _undo;
  360. public Stack _redo;
  361. public int _distance_from_last_sync;
  362. ///
  363. public UndoRedo(uint32 undo_redo_size = 0)
  364. {
  365. uint32 size = uint32.max(1024, undo_redo_size);
  366. _undo = new Stack(size);
  367. _redo = new Stack(size);
  368. reset();
  369. }
  370. public void reset()
  371. {
  372. _undo.clear();
  373. _redo.clear();
  374. _distance_from_last_sync = 0;
  375. }
  376. }
  377. const string OBJECT_NAME_UNNAMED = "Unnamed";
  378. public enum ObjectTypeFlags
  379. {
  380. NONE = 0,
  381. UNIT_COMPONENT = 1 << 0,
  382. }
  383. public struct PropertiesSlice
  384. {
  385. int start; // Index of first property.
  386. int end; // Index of last property + 1.
  387. }
  388. public delegate void Aspect(out string name, Database database, Guid id);
  389. [Compact]
  390. public struct AspectData
  391. {
  392. public unowned Aspect callback;
  393. }
  394. public struct ObjectTypeInfo
  395. {
  396. PropertiesSlice properties;
  397. string name;
  398. string ui_name;
  399. double ui_order;
  400. ObjectTypeFlags flags;
  401. string? user_data;
  402. Gee.HashMap<StringId64?, AspectData?> aspects;
  403. }
  404. public class Database
  405. {
  406. public static bool _debug = false;
  407. public static bool _debug_getters = false;
  408. public enum Action
  409. {
  410. CREATE,
  411. DESTROY,
  412. SET_NULL,
  413. SET_BOOL,
  414. SET_DOUBLE,
  415. SET_STRING,
  416. SET_VECTOR3,
  417. SET_QUATERNION,
  418. SET_RESOURCE,
  419. SET_REFERENCE,
  420. ADD_TO_SET,
  421. REMOVE_FROM_SET
  422. }
  423. // Data
  424. private PropertyDefinition[] _property_definitions;
  425. public Gee.HashMap<StringId64?, ObjectTypeInfo?> _object_definitions;
  426. public Gee.HashMap<Guid?, Gee.HashMap<string, Value?>> _data;
  427. public UndoRedo? _undo_redo;
  428. public Project _project;
  429. // The number of changes to the database since the last successful state
  430. // synchronization (load(), save() etc.). If it is less than 0, the changes
  431. // came from undo(), otherwise they came from redo() or from regular calls to
  432. // create(), destroy(), set_*() etc. A value of 0 means there were no changes.
  433. // Signals
  434. public signal void object_type_added(ObjectTypeInfo info);
  435. public signal void objects_created(Guid?[] object_ids, uint32 flags);
  436. public signal void objects_destroyed(Guid?[] object_ids, uint32 flags);
  437. public signal void objects_changed(Guid?[] object_ids, uint32 flags);
  438. public Database(Project project, UndoRedo? undo_redo = null)
  439. {
  440. _property_definitions = new PropertyDefinition[0];
  441. _object_definitions = new Gee.HashMap<StringId64?, ObjectTypeInfo?>(StringId64.hash_func, StringId64.equal_func);
  442. _data = new Gee.HashMap<Guid?, Gee.HashMap<string, Value?>>(Guid.hash_func, Guid.equal_func);
  443. _project = project;
  444. _undo_redo = undo_redo;
  445. reset();
  446. }
  447. /// Resets database to clean state.
  448. public void reset()
  449. {
  450. _data.clear();
  451. if (_undo_redo != null)
  452. _undo_redo.reset();
  453. // This is a special field which stores all objects
  454. _data[GUID_ZERO] = new Gee.HashMap<string, Value?>();
  455. }
  456. /// Returns whether the database has been changed since last call to Save().
  457. public bool changed()
  458. {
  459. return _undo_redo != null
  460. ? _undo_redo._distance_from_last_sync != 0
  461. : false
  462. ;
  463. }
  464. /// Saves database to path without marking it as not changed.
  465. public int dump(string path, Guid id)
  466. {
  467. try {
  468. Hashtable json = encode(id);
  469. SJSON.save(json, path);
  470. return 0;
  471. } catch (JsonWriteError e) {
  472. return -1;
  473. }
  474. }
  475. /// Saves database to path.
  476. public int save(string path, Guid id)
  477. {
  478. int err = dump(path, id);
  479. if (err == 0) {
  480. if (_undo_redo != null)
  481. _undo_redo._distance_from_last_sync = 0;
  482. }
  483. return err;
  484. }
  485. public UndoRedo disable_undo()
  486. {
  487. var undo = _undo_redo;
  488. _undo_redo = null;
  489. return undo;
  490. }
  491. public void restore_undo(UndoRedo undo_redo)
  492. {
  493. _undo_redo = undo_redo;
  494. }
  495. // See: add_from_path().
  496. public int add_from_file(out Guid object_id, FileStream? fs, string resource_path)
  497. {
  498. UndoRedo undo_redo = disable_undo();
  499. try {
  500. Hashtable json = SJSON.load_from_file(fs);
  501. // Parse the object's ID or generate a new one if none is found.
  502. if (json.has_key("id"))
  503. object_id = Guid.parse((string)json["id"]);
  504. else if (json.has_key("_guid"))
  505. object_id = Guid.parse((string)json["_guid"]);
  506. else
  507. object_id = Guid.new_guid();
  508. string type = ResourceId.type(resource_path);
  509. StringId64 type_hash = StringId64(type);
  510. _data[object_id] = new Gee.HashMap<string, Value?>();
  511. set_type(object_id, type);
  512. set_owner(object_id, GUID_ZERO);
  513. set_alive(object_id, true);
  514. if (has_type(type_hash))
  515. _init_object(object_id, object_definition(type_hash));
  516. decode_object(object_id, GUID_ZERO, "", json);
  517. // Create a mapping between the path and the object it has been loaded into.
  518. set(0, GUID_ZERO, resource_path, object_id);
  519. restore_undo(undo_redo);
  520. return 0;
  521. } catch (JsonSyntaxError e) {
  522. object_id = GUID_ZERO;
  523. restore_undo(undo_redo);
  524. return -1;
  525. }
  526. }
  527. // Adds the object stored at @a path to the database.
  528. // This makes it possible to load multiple objects from distinct
  529. // paths in the same database. @a resource_path is used as a key in the
  530. // database to refer to the object that has been loaded. This is useful when
  531. // you do not have the object ID but only its path, as it is often the case
  532. // since resources use paths and not IDs to reference each other.
  533. public int add_from_path(out Guid object_id, string path, string resource_path)
  534. {
  535. object_id = GUID_ZERO;
  536. FileStream fs = FileStream.open(path, "rb");
  537. if (fs == null)
  538. return 1;
  539. return add_from_file(out object_id, fs, resource_path);
  540. }
  541. public int add_from_resource_path(out Guid object_id, string resource_path)
  542. {
  543. // If the resource is already loaded.
  544. if (has_property(GUID_ZERO, resource_path)) {
  545. object_id = get_reference(GUID_ZERO, resource_path);
  546. return 0;
  547. }
  548. string path = _project.absolute_path(resource_path);
  549. return add_from_path(out object_id, path, resource_path);
  550. }
  551. /// Loads the database with the object stored at @a path.
  552. public int load_from_file(out Guid object_id, FileStream fs, string resource_path)
  553. {
  554. reset();
  555. return add_from_file(out object_id, fs, resource_path);
  556. }
  557. /// Loads the database with the object stored at @a path.
  558. public int load_from_path(out Guid object_id, string path, string resource_path)
  559. {
  560. reset();
  561. return add_from_path(out object_id, path, resource_path);
  562. }
  563. /// Encodes the object @a id into SJSON object.
  564. public Hashtable encode(Guid id)
  565. {
  566. return encode_object(id, get_data(id));
  567. }
  568. public static bool is_valid_value(Value? value)
  569. {
  570. return value == null
  571. || value.holds(typeof(bool))
  572. || value.holds(typeof(double))
  573. || value.holds(typeof(string))
  574. || value.holds(typeof(Vector3))
  575. || value.holds(typeof(Quaternion))
  576. || value.holds(typeof(Resource))
  577. || value.holds(typeof(Guid))
  578. ;
  579. }
  580. public static bool is_valid_key(string key)
  581. {
  582. return key.length > 0
  583. && !key.has_prefix(".")
  584. && !key.has_suffix(".")
  585. ;
  586. }
  587. public static string debug_string(Value? value)
  588. {
  589. if (value == null)
  590. return "null";
  591. if (value.holds(typeof(bool)))
  592. return ((bool)value).to_string();
  593. if (value.holds(typeof(double)))
  594. return ((double)value).to_string();
  595. if (value.holds(typeof(string)))
  596. return ((string)value).to_string();
  597. if (value.holds(typeof(Vector3)))
  598. return ((Vector3)value).to_string();
  599. if (value.holds(typeof(Quaternion)))
  600. return ((Quaternion)value).to_string();
  601. if (value.holds(typeof(Resource)))
  602. return ((Resource)value).name == null ? "(None)" : ((Resource)value).name;
  603. if (value.holds(typeof(Guid)))
  604. return ((Guid)value).to_debug_string();
  605. if (value.holds(typeof(Gee.HashSet)))
  606. return "Set<Guid>";
  607. return "<invalid>";
  608. }
  609. public void decode_object_compat(Guid id, Guid owner_id, string db_key, Hashtable json)
  610. {
  611. string old_db = db_key;
  612. string k = db_key;
  613. string[] keys = json.keys.to_array();
  614. foreach (string key in keys) {
  615. // ID is filled by decode_set().
  616. if (key == "id"
  617. || key == "_guid"
  618. || key == "_alive"
  619. || key == "prefab"
  620. )
  621. continue;
  622. Value? val = json[key];
  623. k += k == "" ? key : ("." + key);
  624. if (val.holds(typeof(Hashtable))) {
  625. Hashtable ht = (Hashtable)val;
  626. decode_object(id, owner_id, k, ht);
  627. } else if (val.holds(typeof(Gee.ArrayList))) {
  628. Gee.ArrayList<Value?> arr = (Gee.ArrayList<Value?>)val;
  629. if (arr.size > 0
  630. && arr[0].holds(typeof(double))
  631. && k != "frames" // sprite_animation
  632. )
  633. set(0, id, k, decode_value(val));
  634. else
  635. decode_set(id, key, arr);
  636. } else {
  637. set(0, id, k, decode_value(val));
  638. }
  639. k = old_db;
  640. }
  641. }
  642. public void decode_object_from_properties(Guid id, Guid owner_id, PropertyDefinition[]? properties, Hashtable json)
  643. {
  644. foreach (PropertyDefinition def in properties) {
  645. // Find table and key to read from.
  646. string[] keys = def.name.split(".");
  647. string key = keys[keys.length - 1];
  648. Hashtable input = json;
  649. if (keys.length > 1) {
  650. for (int i = 0; i < keys.length - 1; ++i) {
  651. string f = keys[i];
  652. if (input.has_key(f)) {
  653. input = (Hashtable)input[f];
  654. continue;
  655. }
  656. }
  657. }
  658. if (!input.has_key(key))
  659. continue;
  660. // Read property.
  661. if (def.type == PropertyType.OBJECTS_SET) {
  662. decode_set(id, def.name, (Gee.ArrayList<Value?>)input[key]);
  663. } else if (def.type == PropertyType.RESOURCE) {
  664. Resource res = { null };
  665. if (input.has_key(key)) {
  666. Value? val = input[key];
  667. if (val.holds(typeof(string)))
  668. res.name = (string)input[key];
  669. }
  670. set(0, id, def.name, res);
  671. } else {
  672. set(0, id, def.name, decode_value(input[key]));
  673. }
  674. }
  675. }
  676. public void decode_object(Guid id, Guid owner_id, string db_key, Hashtable json)
  677. {
  678. string? type = null;
  679. // The "type" key defines object type only if it appears
  680. // in the root of a JSON object (k == "").
  681. if (db_key == "" && !has_property(id, "_type")) {
  682. if (json.has_key("_type"))
  683. type = (string)json["_type"];
  684. else if (json.has_key("type"))
  685. type = (string)json["type"];
  686. assert(type != null);
  687. set_type(id, type);
  688. StringId64 type_hash = StringId64(type);
  689. if (has_type(type_hash))
  690. _init_object(id, object_definition(type_hash));
  691. } else {
  692. type = object_type(id);
  693. }
  694. assert(type != null);
  695. PropertyDefinition[]? properties = object_definition(StringId64(type));
  696. decode_object_from_properties(id, owner_id, properties, json);
  697. if (type == OBJECT_TYPE_UNIT)
  698. decode_object_compat(id, owner_id, db_key, json);
  699. }
  700. public void decode_set(Guid owner_id, string key, Gee.ArrayList<Value?> json)
  701. {
  702. // Set should be created even if it is empty.
  703. create_empty_set(owner_id, key);
  704. for (int i = 0; i < json.size; ++i) {
  705. Hashtable obj;
  706. string owner_type = object_type(owner_id);
  707. obj = (Hashtable)json[i];
  708. // Decode object ID.
  709. Guid obj_id;
  710. if (obj.has_key("id") && owner_type != OBJECT_TYPE_FONT)
  711. obj_id = Guid.parse((string)obj["id"]);
  712. else if (obj.has_key("_guid"))
  713. obj_id = Guid.parse((string)obj["_guid"]);
  714. else
  715. obj_id = Guid.new_guid();
  716. _data[obj_id] = new Gee.HashMap<string, Value?>();
  717. set_owner(obj_id, owner_id);
  718. set_alive(obj_id, true);
  719. decode_object(obj_id, owner_id, "", obj);
  720. assert(has_property(obj_id, "_type"));
  721. add_to_set_internal(0, owner_id, key, obj_id);
  722. }
  723. }
  724. public Value? decode_value(Value? value)
  725. {
  726. if (value.holds(typeof(Gee.ArrayList))) {
  727. Gee.ArrayList<Value?> al = (Gee.ArrayList<Value?>)value;
  728. if (al.size == 1)
  729. return Vector3((double)al[0], 0.0, 0.0);
  730. else if (al.size == 2)
  731. return Vector3((double)al[0], (double)al[1], 0.0);
  732. else if (al.size == 3)
  733. return Vector3((double)al[0], (double)al[1], (double)al[2]);
  734. else if (al.size == 4)
  735. return Quaternion((double)al[0], (double)al[1], (double)al[2], (double)al[3]);
  736. else
  737. return Vector3(0.0, 0.0, 0.0);
  738. } else if (value.holds(typeof(string))) {
  739. Guid id;
  740. if (Guid.try_parse((string)value, out id))
  741. return id;
  742. return value;
  743. } else if (value == null
  744. || value.holds(typeof(bool))
  745. || value.holds(typeof(double))) {
  746. return value;
  747. } else {
  748. return null;
  749. }
  750. }
  751. public Hashtable encode_object_compat(Guid id, Gee.HashMap<string, Value?> db)
  752. {
  753. Hashtable obj = new Hashtable();
  754. if (id != GUID_ZERO)
  755. obj["_guid"] = id.to_string();
  756. string[] keys = db.keys.to_array();
  757. foreach (string key in keys) {
  758. // Since null-key is equivalent to non-existent key, skip serialization.
  759. if (db[key] == null || key == "_owner" || key == "_alive")
  760. continue;
  761. string[] foo = key.split(".");
  762. Hashtable x = obj;
  763. if (foo.length > 1) {
  764. for (int i = 0; i < foo.length - 1; ++i) {
  765. string f = foo[i];
  766. if (x.has_key(f)) {
  767. x = (Hashtable)x[f];
  768. continue;
  769. }
  770. Hashtable y = new Hashtable();
  771. x.set(f, y);
  772. x = y;
  773. }
  774. }
  775. x.set(foo[foo.length - 1], encode_value(db[key]));
  776. }
  777. return obj;
  778. }
  779. public Hashtable encode_object(Guid id, Gee.HashMap<string, Value?> db)
  780. {
  781. assert(is_alive(id));
  782. string type = object_type(id);
  783. PropertyDefinition[]? properties = object_definition(StringId64(type));
  784. if (type == OBJECT_TYPE_UNIT || properties == null)
  785. return encode_object_compat(id, db);
  786. Hashtable obj = new Hashtable();
  787. if (id != GUID_ZERO) {
  788. obj["_guid"] = id.to_string();
  789. obj["_type"] = type;
  790. }
  791. foreach (PropertyDefinition def in properties) {
  792. if (def.not_serialized)
  793. continue;
  794. // Since null-key is equivalent to non-existent key, skip serialization.
  795. if (db[def.name] == null)
  796. continue;
  797. string[] foo = def.name.split(".");
  798. Hashtable x = obj;
  799. if (foo.length > 1) {
  800. for (int i = 0; i < foo.length - 1; ++i) {
  801. string f = foo[i];
  802. if (x.has_key(f)) {
  803. x = (Hashtable)x[f];
  804. continue;
  805. }
  806. Hashtable y = new Hashtable();
  807. x.set(f, y);
  808. x = y;
  809. }
  810. }
  811. x.set(foo[foo.length - 1], encode_value(db[def.name]));
  812. }
  813. return obj;
  814. }
  815. public Value? encode_value(Value? value)
  816. {
  817. assert(is_valid_value(value) || value.holds(typeof(Gee.HashSet)));
  818. if (value.holds(typeof(Vector3))) {
  819. Vector3 v = (Vector3)value;
  820. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  821. arr.add(v.x);
  822. arr.add(v.y);
  823. arr.add(v.z);
  824. return arr;
  825. } else if (value.holds(typeof(Quaternion))) {
  826. Quaternion q = (Quaternion)value;
  827. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  828. arr.add(q.x);
  829. arr.add(q.y);
  830. arr.add(q.z);
  831. arr.add(q.w);
  832. return arr;
  833. } else if (value.holds(typeof(Resource))) {
  834. Resource res = (Resource)value;
  835. if (res.name == null)
  836. return null;
  837. else
  838. return res.name;
  839. } else if (value.holds(typeof(Guid))) {
  840. Guid id = (Guid)value;
  841. return id.to_string();
  842. } else if (value.holds(typeof(Gee.HashSet))) {
  843. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  844. Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  845. foreach (Guid id in hs) {
  846. if (!is_alive(id))
  847. continue;
  848. arr.add(encode_object(id, get_data(id)));
  849. }
  850. return arr;
  851. } else {
  852. return value;
  853. }
  854. }
  855. public Gee.HashMap<string, Value?> get_data(Guid id)
  856. {
  857. assert(has_object(id));
  858. return _data[id];
  859. }
  860. public void set(int dir, Guid id, string key, Value? value)
  861. {
  862. assert(has_object(id));
  863. assert(is_valid_key(key));
  864. assert(is_valid_value(value));
  865. if (_debug)
  866. logi("set_property %s %s %s".printf(debug_string(id), key, debug_string(value)));
  867. Gee.HashMap<string, Value?> ob = get_data(id);
  868. ob[key] = value;
  869. if (_undo_redo != null)
  870. _undo_redo._distance_from_last_sync += dir;
  871. }
  872. public void create_empty_set(Guid id, string key)
  873. {
  874. assert(has_object(id));
  875. assert(is_valid_key(key));
  876. Gee.HashMap<string, Value?> ob = get_data(id);
  877. ob[key] = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  878. }
  879. public void add_to_set_internal(int dir, Guid id, string key, Guid item_id)
  880. {
  881. assert(has_object(id));
  882. assert(is_valid_key(key));
  883. assert(item_id != GUID_ZERO);
  884. assert(has_object(item_id));
  885. if (_debug)
  886. logi("add_to_set %s %s %s".printf(debug_string(id), key, debug_string(item_id)));
  887. Gee.HashMap<string, Value?> ob = get_data(id);
  888. if (!ob.has_key(key)) {
  889. Gee.HashSet<Guid?> hs = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  890. hs.add(item_id);
  891. ob[key] = hs;
  892. } else {
  893. ((Gee.HashSet<Guid?>)ob[key]).add(item_id);
  894. }
  895. get_data(item_id)["_owner"] = id;
  896. if (_undo_redo != null)
  897. _undo_redo._distance_from_last_sync += dir;
  898. }
  899. public void remove_from_set_internal(int dir, Guid id, string key, Guid item_id)
  900. {
  901. assert(has_object(id));
  902. assert(is_valid_key(key));
  903. assert(item_id != GUID_ZERO);
  904. if (_debug)
  905. logi("remove_from_set %s %s %s".printf(debug_string(id), key, debug_string(item_id)));
  906. Gee.HashMap<string, Value?> ob = get_data(id);
  907. ((Gee.HashSet<Guid?>)ob[key]).remove(item_id);
  908. set_owner(id, GUID_ZERO);
  909. if (_undo_redo != null)
  910. _undo_redo._distance_from_last_sync += dir;
  911. }
  912. // Returns the type of the object @a id.
  913. public string object_type(Guid id)
  914. {
  915. assert(has_object(id));
  916. if (id == GUID_ZERO)
  917. return "database";
  918. else
  919. return (string)get_data(id)["_type"];
  920. }
  921. // Returns the owner of @a id.
  922. public Guid owner(Guid id)
  923. {
  924. assert(has_object(id));
  925. return (Guid)get_data(id)["_owner"];
  926. }
  927. // Sets the @a type of the object @a id.
  928. // This is called automatically when loading data or when new objects are created via create().
  929. // It can occasionally be called manually after loading legacy data with no type information
  930. // stored inside objects.
  931. public void set_type(Guid id, string type)
  932. {
  933. assert(has_object(id));
  934. get_data(id)["_type"] = type;
  935. }
  936. public void set_owner(Guid id, Guid owner_id)
  937. {
  938. assert(has_object(id));
  939. assert(has_object(owner_id));
  940. get_data(id)["_owner"] = owner_id;
  941. }
  942. public void set_alive(Guid id, bool alive)
  943. {
  944. assert(has_object(id));
  945. get_data(id)["_alive"] = alive;
  946. }
  947. public bool is_alive(Guid id)
  948. {
  949. assert(has_object(id));
  950. return id == GUID_ZERO || (bool)get_data(id)["_alive"];
  951. }
  952. public void _init_object(Guid id, PropertyDefinition[] properties)
  953. {
  954. foreach (PropertyDefinition def in properties) {
  955. switch (def.type) {
  956. case PropertyType.BOOL:
  957. set_bool(id, def.name, (bool)def.deffault);
  958. break;
  959. case PropertyType.DOUBLE:
  960. set_double(id, def.name, (double)def.deffault);
  961. break;
  962. case PropertyType.STRING:
  963. set_string(id, def.name, (string)def.deffault);
  964. break;
  965. case PropertyType.VECTOR3:
  966. set_vector3(id, def.name, (Vector3)def.deffault);
  967. break;
  968. case PropertyType.QUATERNION:
  969. set_quaternion(id, def.name, (Quaternion)def.deffault);
  970. break;
  971. case PropertyType.RESOURCE:
  972. set_resource(id, def.name, (string?)def.deffault);
  973. break;
  974. case PropertyType.REFERENCE:
  975. set_reference(id, def.name, (Guid)def.deffault);
  976. break;
  977. case PropertyType.OBJECTS_SET:
  978. create_empty_set(id, def.name);
  979. break;
  980. default:
  981. assert(false);
  982. break;
  983. }
  984. }
  985. }
  986. public void create(Guid id, string type)
  987. {
  988. assert(id != GUID_ZERO);
  989. assert(!has_object(id));
  990. if (_debug)
  991. logi("create %s".printf(debug_string(id)));
  992. if (_undo_redo != null) {
  993. _undo_redo._distance_from_last_sync += 1;
  994. _undo_redo._undo.write_destroy_action(Action.DESTROY, id, type);
  995. _undo_redo._redo.clear();
  996. }
  997. _data[id] = new Gee.HashMap<string, Value?>();
  998. set_alive(id, true);
  999. set_type(id, type);
  1000. StringId64 type_hash = StringId64(type);
  1001. if (has_type(type_hash))
  1002. _init_object(id, object_definition(type_hash));
  1003. }
  1004. public void destroy(Guid id)
  1005. {
  1006. assert(id != GUID_ZERO);
  1007. assert(has_object(id));
  1008. string obj_type = object_type(id);
  1009. Gee.HashMap<string, Value?> o = get_data(id);
  1010. string[] keys = o.keys.to_array();
  1011. foreach (string key in keys) {
  1012. Value? value = o[key];
  1013. if (value.holds(typeof(Gee.HashSet))) {
  1014. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  1015. Guid?[] ids = hs.to_array();
  1016. foreach (Guid item_id in ids) {
  1017. if (is_alive(item_id))
  1018. destroy(item_id);
  1019. }
  1020. }
  1021. }
  1022. set_alive(id, false);
  1023. if (_undo_redo != null) {
  1024. _undo_redo._distance_from_last_sync += 1;
  1025. _undo_redo._undo.write_create_action(Action.CREATE, id, obj_type);
  1026. _undo_redo._redo.clear();
  1027. }
  1028. if (_debug)
  1029. logi("destroy %s".printf(debug_string(id)));
  1030. }
  1031. public void set_null(Guid id, string key)
  1032. {
  1033. assert(has_object(id));
  1034. assert(is_valid_key(key));
  1035. assert(is_valid_value(null));
  1036. if (_undo_redo != null) {
  1037. Gee.HashMap<string, Value?> ob = get_data(id);
  1038. if (ob.has_key(key) && ob[key] != null) {
  1039. if (ob[key].holds(typeof(bool)))
  1040. _undo_redo._undo.write_set_bool_action(Action.SET_BOOL, id, key, (bool)ob[key]);
  1041. if (ob[key].holds(typeof(double)))
  1042. _undo_redo._undo.write_set_double_action(Action.SET_DOUBLE, id, key, (double)ob[key]);
  1043. if (ob[key].holds(typeof(string)))
  1044. _undo_redo._undo.write_set_string_action(Action.SET_STRING, id, key, (string)ob[key]);
  1045. if (ob[key].holds(typeof(Vector3)))
  1046. _undo_redo._undo.write_set_vector3_action(Action.SET_VECTOR3, id, key, (Vector3)ob[key]);
  1047. if (ob[key].holds(typeof(Quaternion)))
  1048. _undo_redo._undo.write_set_quaternion_action(Action.SET_QUATERNION, id, key, (Quaternion)ob[key]);
  1049. if (ob[key].holds(typeof(Resource)))
  1050. _undo_redo._undo.write_set_resource_action(Action.SET_RESOURCE, id, key, (Resource)ob[key]);
  1051. if (ob[key].holds(typeof(Guid)))
  1052. _undo_redo._undo.write_set_reference_action(Action.SET_REFERENCE, id, key, (Guid)ob[key]);
  1053. } else {
  1054. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1055. }
  1056. _undo_redo._redo.clear();
  1057. }
  1058. set(1, id, key, null);
  1059. }
  1060. public void set_bool(Guid id, string key, bool val)
  1061. {
  1062. assert(has_object(id));
  1063. assert(is_valid_key(key));
  1064. assert(is_valid_value(val));
  1065. if (_undo_redo != null) {
  1066. Gee.HashMap<string, Value?> ob = get_data(id);
  1067. if (ob.has_key(key) && ob[key] != null)
  1068. _undo_redo._undo.write_set_bool_action(Action.SET_BOOL, id, key, (bool)ob[key]);
  1069. else
  1070. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1071. _undo_redo._redo.clear();
  1072. }
  1073. set(1, id, key, val);
  1074. }
  1075. public void set_double(Guid id, string key, double val)
  1076. {
  1077. assert(has_object(id));
  1078. assert(is_valid_key(key));
  1079. assert(is_valid_value(val));
  1080. if (_undo_redo != null) {
  1081. Gee.HashMap<string, Value?> ob = get_data(id);
  1082. if (ob.has_key(key) && ob[key] != null)
  1083. _undo_redo._undo.write_set_double_action(Action.SET_DOUBLE, id, key, (double)ob[key]);
  1084. else
  1085. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1086. _undo_redo._redo.clear();
  1087. }
  1088. set(1, id, key, val);
  1089. }
  1090. public void set_string(Guid id, string key, string val)
  1091. {
  1092. assert(has_object(id));
  1093. assert(is_valid_key(key));
  1094. assert(is_valid_value(val));
  1095. if (_undo_redo != null) {
  1096. Gee.HashMap<string, Value?> ob = get_data(id);
  1097. if (ob.has_key(key) && ob[key] != null)
  1098. _undo_redo._undo.write_set_string_action(Action.SET_STRING, id, key, (string)ob[key]);
  1099. else
  1100. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1101. _undo_redo._redo.clear();
  1102. }
  1103. set(1, id, key, val);
  1104. }
  1105. public void set_vector3(Guid id, string key, Vector3 val)
  1106. {
  1107. assert(has_object(id));
  1108. assert(is_valid_key(key));
  1109. assert(is_valid_value(val));
  1110. if (_undo_redo != null) {
  1111. Gee.HashMap<string, Value?> ob = get_data(id);
  1112. if (ob.has_key(key) && ob[key] != null)
  1113. _undo_redo._undo.write_set_vector3_action(Action.SET_VECTOR3, id, key, (Vector3)ob[key]);
  1114. else
  1115. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1116. _undo_redo._redo.clear();
  1117. }
  1118. set(1, id, key, val);
  1119. }
  1120. public void set_quaternion(Guid id, string key, Quaternion val)
  1121. {
  1122. assert(has_object(id));
  1123. assert(is_valid_key(key));
  1124. assert(is_valid_value(val));
  1125. if (_undo_redo != null) {
  1126. Gee.HashMap<string, Value?> ob = get_data(id);
  1127. if (ob.has_key(key) && ob[key] != null)
  1128. _undo_redo._undo.write_set_quaternion_action(Action.SET_QUATERNION, id, key, (Quaternion)ob[key]);
  1129. else
  1130. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1131. _undo_redo._redo.clear();
  1132. }
  1133. set(1, id, key, val);
  1134. }
  1135. public void set_resource(Guid id, string key, string? val)
  1136. {
  1137. assert(has_object(id));
  1138. assert(is_valid_key(key));
  1139. assert(is_valid_value(val));
  1140. if (_undo_redo != null) {
  1141. Gee.HashMap<string, Value?> ob = get_data(id);
  1142. if (ob.has_key(key) && ob[key] != null)
  1143. _undo_redo._undo.write_set_resource_action(Action.SET_RESOURCE, id, key, { ((Resource)ob[key]).name });
  1144. else
  1145. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1146. _undo_redo._redo.clear();
  1147. }
  1148. Resource res = { val };
  1149. set(1, id, key, res);
  1150. }
  1151. public void set_reference(Guid id, string key, Guid val)
  1152. {
  1153. assert(has_object(id));
  1154. assert(is_valid_key(key));
  1155. assert(is_valid_value(val));
  1156. if (_undo_redo != null) {
  1157. Gee.HashMap<string, Value?> ob = get_data(id);
  1158. if (ob.has_key(key) && ob[key] != null)
  1159. _undo_redo._undo.write_set_reference_action(Action.SET_REFERENCE, id, key, (Guid)ob[key]);
  1160. else
  1161. _undo_redo._undo.write_set_null_action(Action.SET_NULL, id, key);
  1162. _undo_redo._redo.clear();
  1163. }
  1164. set(1, id, key, val);
  1165. }
  1166. public void set_property(Guid id, string key, Value? val)
  1167. {
  1168. if (val == null)
  1169. set_null(id, key);
  1170. if (val.holds(typeof(bool)))
  1171. set_bool(id, key, (bool)val);
  1172. else if (val.holds(typeof(double)))
  1173. set_double(id, key, (double)val);
  1174. else if (val.holds(typeof(string)))
  1175. set_string(id, key, (string)val);
  1176. else if (val.holds(typeof(Vector3)))
  1177. set_vector3(id, key, (Vector3)val);
  1178. else if (val.holds(typeof(Quaternion)))
  1179. set_quaternion(id, key, (Quaternion)val);
  1180. else if (val.holds(typeof(Resource)))
  1181. set_resource(id, key, ((Resource)val).name);
  1182. else if (val.holds(typeof(Guid)))
  1183. set_reference(id, key, (Guid)val);
  1184. else
  1185. assert(false);
  1186. }
  1187. public void add_to_set(Guid id, string key, Guid item_id)
  1188. {
  1189. assert(has_object(id));
  1190. assert(is_valid_key(key));
  1191. assert(item_id != GUID_ZERO);
  1192. assert(has_object(item_id));
  1193. if (_undo_redo != null) {
  1194. _undo_redo._undo.write_remove_from_set_action(Action.REMOVE_FROM_SET, id, key, item_id);
  1195. _undo_redo._redo.clear();
  1196. }
  1197. add_to_set_internal(1, id, key, item_id);
  1198. }
  1199. public void remove_from_set(Guid id, string key, Guid item_id)
  1200. {
  1201. assert(has_object(id));
  1202. assert(is_valid_key(key));
  1203. assert(item_id != GUID_ZERO);
  1204. if (_undo_redo != null) {
  1205. _undo_redo._undo.write_add_to_set_action(Action.ADD_TO_SET, id, key, item_id);
  1206. _undo_redo._redo.clear();
  1207. }
  1208. remove_from_set_internal(1, id, key, item_id);
  1209. }
  1210. public bool has_object(Guid id)
  1211. {
  1212. return id == GUID_ZERO || _data.has_key(id);
  1213. }
  1214. public bool has_property(Guid id, string key)
  1215. {
  1216. return get_property(id, key) != null;
  1217. }
  1218. public Value? get_property(Guid id, string key, Value? val = null)
  1219. {
  1220. assert(has_object(id));
  1221. assert(is_valid_key(key));
  1222. Gee.HashMap<string, Value?> ob = get_data(id);
  1223. Value? value = (ob.has_key(key) ? ob[key] : val);
  1224. if (_debug_getters)
  1225. logi("get_property %s %s %s".printf(debug_string(id), key, debug_string(value)));
  1226. return value;
  1227. }
  1228. public bool get_bool(Guid id, string key, bool deffault = false)
  1229. {
  1230. return (bool)get_property(id, key, deffault);
  1231. }
  1232. public double get_double(Guid id, string key, double deffault = 0.0)
  1233. {
  1234. return (double)get_property(id, key, deffault);
  1235. }
  1236. public string get_string(Guid id, string key, string deffault = "")
  1237. {
  1238. return (string)get_property(id, key, deffault);
  1239. }
  1240. public Vector3 get_vector3(Guid id, string key, Vector3 deffault = VECTOR3_ZERO)
  1241. {
  1242. return (Vector3)get_property(id, key, deffault);
  1243. }
  1244. public Quaternion get_quaternion(Guid id, string key, Quaternion deffault = QUATERNION_IDENTITY)
  1245. {
  1246. return (Quaternion)get_property(id, key, deffault);
  1247. }
  1248. public string? get_resource(Guid id, string key, string? deffault = null)
  1249. {
  1250. Resource deffault_res = { deffault };
  1251. Value? val = get_property(id, key, deffault_res);
  1252. assert(val == null || val.holds(typeof(Crown.Resource)));
  1253. return ((Resource)val).name;
  1254. }
  1255. public Guid get_reference(Guid id, string key, Guid deffault = GUID_ZERO)
  1256. {
  1257. return (Guid)get_property(id, key, deffault);
  1258. }
  1259. public Gee.HashSet<Guid?> get_set(Guid id, string key, Gee.HashSet<Guid?> deffault = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func))
  1260. {
  1261. assert(has_object(id));
  1262. assert(is_valid_key(key));
  1263. Gee.HashMap<string, Value?> ob = get_data(id);
  1264. Gee.HashSet<Guid?> value;
  1265. if (ob.has_key(key)) {
  1266. Gee.HashSet<Guid?> objects = (Gee.HashSet<Guid?>)ob[key];
  1267. value = new Gee.HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  1268. foreach (var obj in objects) {
  1269. if (is_alive(obj))
  1270. value.add(obj);
  1271. }
  1272. } else {
  1273. value = deffault;
  1274. }
  1275. if (_debug_getters)
  1276. logi("get_property %s %s %s".printf(debug_string(id), key, debug_string(value)));
  1277. return value;
  1278. }
  1279. public Gee.HashMap<string, Value?> get_object(Guid id)
  1280. {
  1281. return (Gee.HashMap<string, Value?>)get_data(GUID_ZERO)[id.to_string()];
  1282. }
  1283. public string[] get_keys(Guid id)
  1284. {
  1285. Gee.HashMap<string, Value?> data = get_data(id);
  1286. return data.keys.to_array();
  1287. }
  1288. public void add_restore_point(int id, Guid?[] data, uint32 flags = 0u)
  1289. {
  1290. if (_debug)
  1291. logi("add_restore_point %d, undo size = %u".printf(id, _undo_redo._undo.size()));
  1292. if (_undo_redo != null) {
  1293. _undo_redo._undo.write_restore_point(id, flags, data);
  1294. _undo_redo._redo.clear();
  1295. switch (id) {
  1296. case ActionType.CREATE_OBJECTS:
  1297. objects_created(data, flags);
  1298. break;
  1299. case ActionType.DESTROY_OBJECTS:
  1300. objects_destroyed(data, flags);
  1301. break;
  1302. case ActionType.CHANGE_OBJECTS:
  1303. objects_changed(data, flags);
  1304. break;
  1305. default:
  1306. logw("Unknown action type %d".printf(id));
  1307. break;
  1308. }
  1309. }
  1310. }
  1311. /// Duplicates the object specified by id and assign new_id to the duplicated object.
  1312. public void duplicate(Guid id, Guid new_id, Database? dest = null)
  1313. {
  1314. assert(id != GUID_ZERO);
  1315. assert(new_id != GUID_ZERO);
  1316. assert(id != new_id);
  1317. assert(has_object(id));
  1318. if (dest == null)
  1319. dest = this;
  1320. dest.create(new_id, object_type(id));
  1321. Gee.HashMap<string, Value?> ob = get_data(id);
  1322. string[] keys = ob.keys.to_array();
  1323. foreach (string key in keys) {
  1324. Value? val = ob[key];
  1325. if (val.holds(typeof(Gee.HashSet))) {
  1326. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)val;
  1327. foreach (Guid j in hs) {
  1328. Guid x = Guid.new_guid();
  1329. duplicate(j, x, dest);
  1330. dest.add_to_set(new_id, key, x);
  1331. }
  1332. } else {
  1333. if (ob[key] == null)
  1334. dest.set_null(new_id, key);
  1335. else if (ob[key].holds(typeof(bool)))
  1336. dest.set_bool(new_id, key, (bool)ob[key]);
  1337. else if (ob[key].holds(typeof(double)))
  1338. dest.set_double(new_id, key, (double)ob[key]);
  1339. else if (ob[key].holds(typeof(string)))
  1340. dest.set_string(new_id, key, (string)ob[key]);
  1341. else if (ob[key].holds(typeof(Vector3)))
  1342. dest.set_vector3(new_id, key, (Vector3)ob[key]);
  1343. else if (ob[key].holds(typeof(Quaternion)))
  1344. dest.set_quaternion(new_id, key, (Quaternion)ob[key]);
  1345. else if (ob[key].holds(typeof(Resource)))
  1346. dest.set_resource(new_id, key, ((Resource)ob[key]).name);
  1347. else if (ob[key].holds(typeof(Guid)))
  1348. dest.set_reference(new_id, key, (Guid)ob[key]);
  1349. else
  1350. assert(false);
  1351. }
  1352. }
  1353. }
  1354. public void duplicate_and_add_to_set(Guid id, Guid new_id)
  1355. {
  1356. duplicate(id, new_id);
  1357. Guid owner_id = owner(id);
  1358. if (owner_id == GUID_ZERO)
  1359. return;
  1360. PropertyDefinition[]? properties = object_definition(StringId64(object_type(owner_id)));
  1361. foreach (PropertyDefinition def in properties) {
  1362. if (def.type != PropertyType.OBJECTS_SET)
  1363. continue;
  1364. Gee.HashSet<Guid?> objects = get_set(owner_id, def.name);
  1365. if (objects.contains(id)) {
  1366. add_to_set(owner_id, def.name, new_id);
  1367. break;
  1368. }
  1369. }
  1370. }
  1371. /// Copies the database to db under the given new_key.
  1372. public void copy_to(Database db, string new_key)
  1373. {
  1374. assert(db != null);
  1375. assert(is_valid_key(new_key));
  1376. copy_deep(db, GUID_ZERO, new_key);
  1377. }
  1378. public void copy_deep(Database db, Guid id, string new_key)
  1379. {
  1380. Gee.HashMap<string, Value?> ob = get_data(id);
  1381. string[] keys = ob.keys.to_array();
  1382. foreach (string key in keys) {
  1383. Value? value = ob[key];
  1384. if (value.holds(typeof(Gee.HashSet))) {
  1385. Gee.HashSet<Guid?> hs = (Gee.HashSet<Guid?>)value;
  1386. foreach (Guid j in hs) {
  1387. db.create(j, object_type(j));
  1388. copy_deep(db, j, "");
  1389. db.add_to_set(id, new_key + (new_key == "" ? "" : ".") + key, j);
  1390. }
  1391. } else {
  1392. if (!db.has_object(id))
  1393. db.create(id, object_type(id));
  1394. string kk = new_key + (new_key == "" ? "" : ".") + key;
  1395. if (ob[key] == null)
  1396. db.set_null(id, kk);
  1397. if (ob[key].holds(typeof(bool)))
  1398. db.set_bool(id, kk, (bool)ob[key]);
  1399. if (ob[key].holds(typeof(double)))
  1400. db.set_double(id, kk, (double)ob[key]);
  1401. if (ob[key].holds(typeof(string)))
  1402. db.set_string(id, kk, (string)ob[key]);
  1403. if (ob[key].holds(typeof(Vector3)))
  1404. db.set_vector3(id, kk, (Vector3)ob[key]);
  1405. if (ob[key].holds(typeof(Quaternion)))
  1406. db.set_quaternion(id, kk, (Quaternion)ob[key]);
  1407. if (ob[key].holds(typeof(Resource)))
  1408. db.set_resource(id, kk, ((Resource)ob[key]).name);
  1409. if (ob[key].holds(typeof(Guid)))
  1410. db.set_reference(id, kk, (Guid)ob[key]);
  1411. }
  1412. }
  1413. }
  1414. // Tries to read a restore point @a rp from the @a stack and returns
  1415. // 0 if successful.
  1416. public int try_read_restore_point(ref RestorePoint rp, Stack stack)
  1417. {
  1418. if (stack.size() < sizeof(Action) + sizeof(RestorePointHeader))
  1419. return -1;
  1420. rp = stack.read_restore_point();
  1421. if (stack.size() < rp.header.size) {
  1422. // The restore point has been overwritten.
  1423. stack.clear();
  1424. return -1;
  1425. }
  1426. return 0;
  1427. }
  1428. // Un-does the last action and returns its ID, or -1 if there is no
  1429. // action to undo.
  1430. public int undo()
  1431. {
  1432. if (_undo_redo == null)
  1433. return -1;
  1434. RestorePoint rp = {};
  1435. if (try_read_restore_point(ref rp, _undo_redo._undo) != 0)
  1436. return -1;
  1437. undo_or_redo(_undo_redo._undo, _undo_redo._redo, rp.header.size);
  1438. switch (rp.header.id) {
  1439. case ActionType.CREATE_OBJECTS:
  1440. objects_destroyed(rp.data, 0u);
  1441. break;
  1442. case ActionType.DESTROY_OBJECTS:
  1443. objects_created(rp.data, 0u);
  1444. break;
  1445. case ActionType.CHANGE_OBJECTS:
  1446. objects_changed(rp.data, 0u);
  1447. break;
  1448. default:
  1449. logw("Unknown action type %u".printf(rp.header.id));
  1450. break;
  1451. }
  1452. _undo_redo._redo.write_restore_point(rp.header.id, rp.header.flags, rp.data);
  1453. return (int)rp.header.id;
  1454. }
  1455. // Re-does the last action and returns its ID, or -1 if there is no
  1456. // action to redo.
  1457. public int redo()
  1458. {
  1459. if (_undo_redo == null)
  1460. return -1;
  1461. RestorePoint rp = {};
  1462. if (try_read_restore_point(ref rp, _undo_redo._redo) != 0)
  1463. return -1;
  1464. undo_or_redo(_undo_redo._redo, _undo_redo._undo, rp.header.size);
  1465. switch (rp.header.id) {
  1466. case ActionType.CREATE_OBJECTS:
  1467. objects_created(rp.data, 0u);
  1468. break;
  1469. case ActionType.DESTROY_OBJECTS:
  1470. objects_destroyed(rp.data, 0u);
  1471. break;
  1472. case ActionType.CHANGE_OBJECTS:
  1473. objects_changed(rp.data, 0u);
  1474. break;
  1475. default:
  1476. logw("Unknown action type %u".printf(rp.header.id));
  1477. break;
  1478. }
  1479. _undo_redo._undo.write_restore_point(rp.header.id, rp.header.flags, rp.data);
  1480. return (int)rp.header.id;
  1481. }
  1482. public void undo_or_redo(Stack undo, Stack redo, uint32 restore_point_size)
  1483. {
  1484. assert(undo.size() >= restore_point_size);
  1485. int dir = undo == _undo_redo._undo ? -1 : 1;
  1486. // Read up to restore_point_size bytes.
  1487. uint32 undo_size_start = undo.size();
  1488. while (undo_size_start - undo.size() < restore_point_size) {
  1489. Action action = (Action)undo.read_uint32();
  1490. if (action == Action.CREATE) {
  1491. Guid id = undo.read_guid();
  1492. string obj_type = undo.read_string();
  1493. set_alive(id, true);
  1494. _undo_redo._distance_from_last_sync += dir;
  1495. redo.write_destroy_action(Action.DESTROY, id, obj_type);
  1496. } else if (action == Action.DESTROY) {
  1497. Guid id = undo.read_guid();
  1498. string obj_type = undo.read_string();
  1499. set_alive(id, false);
  1500. _undo_redo._distance_from_last_sync += dir;
  1501. redo.write_create_action(Action.CREATE, id, obj_type);
  1502. } else if (action == Action.SET_NULL) {
  1503. Guid id = undo.read_guid();
  1504. string key = undo.read_string();
  1505. if (has_property(id, key)) {
  1506. if (get_data(id)[key].holds(typeof(bool)))
  1507. redo.write_set_bool_action(Action.SET_BOOL, id, key, get_bool(id, key));
  1508. if (get_data(id)[key].holds(typeof(double)))
  1509. redo.write_set_double_action(Action.SET_DOUBLE, id, key, get_double(id, key));
  1510. if (get_data(id)[key].holds(typeof(string)))
  1511. redo.write_set_string_action(Action.SET_STRING, id, key, get_string(id, key));
  1512. if (get_data(id)[key].holds(typeof(Vector3)))
  1513. redo.write_set_vector3_action(Action.SET_VECTOR3, id, key, get_vector3(id, key));
  1514. if (get_data(id)[key].holds(typeof(Quaternion)))
  1515. redo.write_set_quaternion_action(Action.SET_QUATERNION, id, key, get_quaternion(id, key));
  1516. if (get_data(id)[key].holds(typeof(Resource)))
  1517. redo.write_set_resource_action(Action.SET_RESOURCE, id, key, { get_resource(id, key) });
  1518. if (get_data(id)[key].holds(typeof(Guid)))
  1519. redo.write_set_reference_action(Action.SET_REFERENCE, id, key, get_reference(id, key));
  1520. } else {
  1521. redo.write_set_null_action(Action.SET_NULL, id, key);
  1522. }
  1523. set(dir, id, key, null);
  1524. } else if (action == Action.SET_BOOL) {
  1525. Guid id = undo.read_guid();
  1526. string key = undo.read_string();
  1527. bool val = undo.read_bool();
  1528. if (has_property(id, key))
  1529. redo.write_set_bool_action(Action.SET_BOOL, id, key, get_bool(id, key));
  1530. else
  1531. redo.write_set_null_action(Action.SET_NULL, id, key);
  1532. set(dir, id, key, val);
  1533. } else if (action == Action.SET_DOUBLE) {
  1534. Guid id = undo.read_guid();
  1535. string key = undo.read_string();
  1536. double val = undo.read_double();
  1537. if (has_property(id, key))
  1538. redo.write_set_double_action(Action.SET_DOUBLE, id, key, get_double(id, key));
  1539. else
  1540. redo.write_set_null_action(Action.SET_NULL, id, key);
  1541. set(dir, id, key, val);
  1542. } else if (action == Action.SET_STRING) {
  1543. Guid id = undo.read_guid();
  1544. string key = undo.read_string();
  1545. string val = undo.read_string();
  1546. if (has_property(id, key))
  1547. redo.write_set_string_action(Action.SET_STRING, id, key, get_string(id, key));
  1548. else
  1549. redo.write_set_null_action(Action.SET_NULL, id, key);
  1550. set(dir, id, key, val);
  1551. } else if (action == Action.SET_VECTOR3) {
  1552. Guid id = undo.read_guid();
  1553. string key = undo.read_string();
  1554. Vector3 val = undo.read_vector3();
  1555. if (has_property(id, key))
  1556. redo.write_set_vector3_action(Action.SET_VECTOR3, id, key, get_vector3(id, key));
  1557. else
  1558. redo.write_set_null_action(Action.SET_NULL, id, key);
  1559. set(dir, id, key, val);
  1560. } else if (action == Action.SET_QUATERNION) {
  1561. Guid id = undo.read_guid();
  1562. string key = undo.read_string();
  1563. Quaternion val = undo.read_quaternion();
  1564. if (has_property(id, key))
  1565. redo.write_set_quaternion_action(Action.SET_QUATERNION, id, key, get_quaternion(id, key));
  1566. else
  1567. redo.write_set_null_action(Action.SET_NULL, id, key);
  1568. set(dir, id, key, val);
  1569. } else if (action == Action.SET_RESOURCE) {
  1570. Guid id = undo.read_guid();
  1571. string key = undo.read_string();
  1572. Resource val = undo.read_resource();
  1573. if (has_property(id, key))
  1574. redo.write_set_resource_action(Action.SET_RESOURCE, id, key, { get_resource(id, key) });
  1575. else
  1576. redo.write_set_null_action(Action.SET_NULL, id, key);
  1577. set(dir, id, key, val);
  1578. } else if (action == Action.SET_REFERENCE) {
  1579. Guid id = undo.read_guid();
  1580. string key = undo.read_string();
  1581. Guid val = undo.read_guid();
  1582. if (has_property(id, key))
  1583. redo.write_set_reference_action(Action.SET_REFERENCE, id, key, get_reference(id, key));
  1584. else
  1585. redo.write_set_null_action(Action.SET_NULL, id, key);
  1586. set(dir, id, key, val);
  1587. } else if (action == Action.ADD_TO_SET) {
  1588. Guid id = undo.read_guid();
  1589. string key = undo.read_string();
  1590. Guid item_id = undo.read_guid();
  1591. redo.write_remove_from_set_action(Action.REMOVE_FROM_SET, id, key, item_id);
  1592. add_to_set_internal(dir, id, key, item_id);
  1593. } else if (action == Action.REMOVE_FROM_SET) {
  1594. Guid id = undo.read_guid();
  1595. string key = undo.read_string();
  1596. Guid item_id = undo.read_guid();
  1597. redo.write_add_to_set_action(Action.ADD_TO_SET, id, key, item_id);
  1598. remove_from_set_internal(dir, id, key, item_id);
  1599. }
  1600. }
  1601. }
  1602. public void add_properties(PropertyDefinition[] properties)
  1603. {
  1604. foreach (PropertyDefinition def in properties) {
  1605. // Generate labels if missing.
  1606. if (def.label == null) {
  1607. int ld = def.name.last_index_of_char('.');
  1608. string label = ld == -1 ? def.name : def.name.substring(ld + 1);
  1609. def.label = camel_case(label);
  1610. }
  1611. if (def.enum_labels.length == 0) {
  1612. string[] labels = new string[def.enum_values.length];
  1613. for (int i = 0; i < def.enum_values.length; ++i)
  1614. labels[i] = camel_case(def.enum_values[i]);
  1615. def.enum_labels = labels;
  1616. }
  1617. // Assign default/min/max values.
  1618. switch (def.type) {
  1619. case PropertyType.BOOL:
  1620. if (def.deffault == null)
  1621. def.deffault = false;
  1622. assert(def.deffault.holds(typeof(bool)));
  1623. break;
  1624. case PropertyType.DOUBLE:
  1625. if (def.deffault == null)
  1626. def.deffault = 0.0;
  1627. if (def.min == null)
  1628. def.min = -double.MAX;
  1629. if (def.max == null)
  1630. def.max = double.MAX;
  1631. assert(def.deffault.holds(typeof(double)));
  1632. assert(def.min.holds(typeof(double)));
  1633. assert(def.max.holds(typeof(double)));
  1634. break;
  1635. case PropertyType.STRING:
  1636. if (def.deffault == null) {
  1637. if (def.enum_values.length > 0)
  1638. def.deffault = def.enum_values[0];
  1639. else
  1640. def.deffault = "";
  1641. }
  1642. assert(def.enum_property == null && def.enum_callback == null || def.enum_property != null && def.enum_callback != null && def.enum_values.length == 0);
  1643. assert(def.deffault.holds(typeof(string)));
  1644. break;
  1645. case PropertyType.VECTOR3:
  1646. if (def.deffault == null)
  1647. def.deffault = VECTOR3_ZERO;
  1648. if (def.min == null)
  1649. def.min = VECTOR3_MIN;
  1650. if (def.max == null)
  1651. def.max = VECTOR3_MAX;
  1652. assert(def.deffault.holds(typeof(Vector3)));
  1653. assert(def.min.holds(typeof(Vector3)));
  1654. assert(def.max.holds(typeof(Vector3)));
  1655. break;
  1656. case PropertyType.QUATERNION:
  1657. if (def.deffault == null)
  1658. def.deffault = QUATERNION_IDENTITY;
  1659. assert(def.deffault.holds(typeof(Quaternion)));
  1660. break;
  1661. case PropertyType.RESOURCE:
  1662. if (def.deffault == null)
  1663. def.deffault = (string?)null;
  1664. assert(def.resource_type != null);
  1665. assert(def.deffault.holds(typeof(string?)));
  1666. break;
  1667. case PropertyType.REFERENCE:
  1668. def.deffault = GUID_ZERO;
  1669. assert(def.deffault.holds(typeof(Guid)));
  1670. assert(def.object_type._id != 0);
  1671. break;
  1672. case PropertyType.OBJECTS_SET:
  1673. assert(def.object_type._id != 0);
  1674. break;
  1675. default:
  1676. assert(false);
  1677. break;
  1678. }
  1679. _property_definitions += def;
  1680. }
  1681. }
  1682. // Creates a new object @a type with the specified @a properties and returns its ID.
  1683. public StringId64 create_object_type(string type
  1684. , PropertyDefinition[] properties
  1685. , double ui_order = 0.0
  1686. , ObjectTypeFlags flags = ObjectTypeFlags.NONE
  1687. , string? user_data = null
  1688. )
  1689. {
  1690. StringId64 type_hash = StringId64(type);
  1691. assert(!_object_definitions.has_key(type_hash));
  1692. assert(properties.length > 0);
  1693. int first_property = _property_definitions.length;
  1694. int num_properties = first_property + properties.length;
  1695. add_properties(properties);
  1696. ObjectTypeInfo info = {};
  1697. info.properties = { first_property, num_properties };
  1698. info.name = type;
  1699. info.ui_name = camel_case(type);
  1700. info.ui_order = ui_order;
  1701. info.flags = flags;
  1702. info.user_data = user_data;
  1703. info.aspects = new Gee.HashMap<StringId64?, AspectData?>(StringId64.hash_func, StringId64.equal_func);
  1704. _object_definitions[type_hash] = info;
  1705. object_type_added(info);
  1706. return type_hash;
  1707. }
  1708. // Returns the array of properties (i.e. its definition) of the object @a type.
  1709. public unowned PropertyDefinition[]? object_definition(StringId64 type)
  1710. {
  1711. if (!_object_definitions.has_key(type))
  1712. return null;
  1713. PropertiesSlice ps = _object_definitions[type].properties;
  1714. return _property_definitions[ps.start : ps.end];
  1715. }
  1716. // Returns the name of the object @id. If the object has no name set, it returns
  1717. // OBJECT_NAME_UNNAMED.
  1718. public string name(Guid id)
  1719. {
  1720. string name = get_string(id, "editor.name", OBJECT_NAME_UNNAMED);
  1721. if (name == OBJECT_NAME_UNNAMED)
  1722. return get_string(id, "name", OBJECT_NAME_UNNAMED);
  1723. return name;
  1724. }
  1725. // Sets the @a name of the object @a id.
  1726. public void set_name(Guid id, string name)
  1727. {
  1728. set_string(id, "editor.name", name);
  1729. }
  1730. // Returns whether the object @a type exists (i.e. has been created with create_object_type()).
  1731. public bool has_type(StringId64 type)
  1732. {
  1733. return _object_definitions.has_key(type);
  1734. }
  1735. public string type_name(StringId64 type)
  1736. {
  1737. return _object_definitions[type].name;
  1738. }
  1739. public uint type_flags(StringId64 type)
  1740. {
  1741. return _object_definitions[type].flags;
  1742. }
  1743. public ObjectTypeInfo type_info(StringId64 type)
  1744. {
  1745. return _object_definitions[type];
  1746. }
  1747. public Guid?[] all_objects_of_type(StringId64 type)
  1748. {
  1749. Gee.ArrayList<Guid?> all = new Gee.ArrayList<Guid?>();
  1750. foreach (var item in _data) {
  1751. Guid id = item.key;
  1752. if (id != GUID_ZERO
  1753. && StringId64(object_type(id)) == type
  1754. && is_alive(id)) {
  1755. all.add(id);
  1756. }
  1757. }
  1758. return all.to_array();
  1759. }
  1760. public bool is_subobject_of(Guid subobject_id, Guid object_id, string set_name)
  1761. {
  1762. assert(has_object(subobject_id));
  1763. assert(has_object(object_id));
  1764. if (!has_property(object_id, set_name))
  1765. return false;
  1766. return get_set(object_id, set_name).contains(subobject_id);
  1767. }
  1768. public bool find_property(ref uint32 property_index, StringId64 object_type, PropertyType type, string name)
  1769. {
  1770. PropertyDefinition[]? properties = object_definition(object_type);
  1771. if (properties == null)
  1772. return false;
  1773. for (int i = 0; i < properties.length; ++i) {
  1774. PropertyDefinition def = properties[i];
  1775. if (def.type == type && def.name == name) {
  1776. property_index = i;
  1777. return true;
  1778. }
  1779. }
  1780. return false;
  1781. }
  1782. public void set_aspect(StringId64 object_type, StringId64 aspect, Aspect callback)
  1783. {
  1784. ObjectTypeInfo info = type_info(object_type);
  1785. AspectData data = AspectData();
  1786. data.callback = callback;
  1787. info.aspects[aspect] = data;
  1788. assert(info.aspects.has_key(aspect));
  1789. assert(get_aspect(object_type, aspect) == callback);
  1790. }
  1791. public unowned Aspect? get_aspect(StringId64 object_type, StringId64 aspect)
  1792. {
  1793. ObjectTypeInfo info = type_info(object_type);
  1794. if (info.aspects.has_key(aspect))
  1795. return info.aspects[aspect].callback;
  1796. return null;
  1797. }
  1798. }
  1799. public void default_name_aspect(out string name, Database database, Guid id)
  1800. {
  1801. name = database.name(id);
  1802. StringId64 object_type = StringId64(database.object_type(id));
  1803. PropertyDefinition[]? properties = database.object_definition(object_type);
  1804. uint32 name_index = 0;
  1805. if (database.find_property(ref name_index, object_type, PropertyType.STRING, "name"))
  1806. name = database.get_string(id, properties[name_index].name);
  1807. else if (database.find_property(ref name_index, object_type, PropertyType.STRING, "editor.name"))
  1808. name = database.get_string(id, properties[name_index].name);
  1809. else
  1810. name = OBJECT_NAME_UNNAMED;
  1811. }
  1812. } /* namespace Crown */