@@ -12,6 +12,189 @@ static func get_json_data(value):
1212 return null
1313
1414
15+ # Pass a dictionary { 'key' : 'value' } to format it in a APIs usable .fields
16+ # Field Path3D using the "dot" (`.`) notation are supported:
17+ # ex. { "PATH.TO.SUBKEY" : "VALUE" } ==> { "PATH" : { "TO" : { "SUBKEY" : "VALUE" } } }
18+ static func dict2fields (dict : Dictionary ) -> Dictionary :
19+ var fields = {}
20+ var var_type : String = ""
21+ for field in dict .keys ():
22+ var field_value = dict [field ]
23+ if field is String and "." in field :
24+ var keys : Array = field .split ("." )
25+ field = keys .pop_front ()
26+ keys .reverse ()
27+ for key in keys :
28+ field_value = { key : field_value }
29+
30+ match typeof (field_value ):
31+ TYPE_NIL : var_type = "nullValue"
32+ TYPE_BOOL : var_type = "booleanValue"
33+ TYPE_INT : var_type = "integerValue"
34+ TYPE_FLOAT : var_type = "doubleValue"
35+ TYPE_STRING : var_type = "stringValue"
36+ TYPE_DICTIONARY :
37+ if is_field_timestamp (field_value ):
38+ var_type = "timestampValue"
39+ field_value = dict2timestamp (field_value )
40+ else :
41+ var_type = "mapValue"
42+ field_value = dict2fields (field_value )
43+ TYPE_ARRAY :
44+ var_type = "arrayValue"
45+ field_value = {"values" : array2fields (field_value )}
46+
47+ if fields .has (field ) and fields [field ].has ("mapValue" ) and field_value .has ("fields" ):
48+ for key in field_value ["fields" ].keys ():
49+ fields [field ]["mapValue" ]["fields" ][key ] = field_value ["fields" ][key ]
50+ else :
51+ fields [field ] = { var_type : field_value }
52+
53+ return {'fields' : fields }
54+
55+ static func from_firebase_type (value : Variant ) -> Variant :
56+ if value == null :
57+ return null
58+
59+ if value .has ("mapValue" ):
60+ value = _from_firebase_type_recursive (value .values ()[0 ].fields )
61+ elif value .has ("timestampValue" ):
62+ value = Time .get_datetime_dict_from_datetime_string (value .values ()[0 ], false )
63+ else :
64+ value = value .values ()[0 ]
65+
66+ return value
67+
68+ static func _from_firebase_type_recursive (value : Variant ) -> Variant :
69+ if value == null :
70+ return null
71+
72+ if value .has ("mapValue" ) or value .has ("timestampValue" ):
73+ value = _from_firebase_type_recursive (value .value ()[0 ].fields )
74+ else :
75+ value = value .values ()[0 ]
76+
77+ return value
78+
79+ static func to_firebase_type (value : Variant ) -> Dictionary :
80+ var var_type : String = ""
81+
82+ match typeof (value ):
83+ TYPE_NIL : var_type = "nullValue"
84+ TYPE_BOOL : var_type = "booleanValue"
85+ TYPE_INT : var_type = "integerValue"
86+ TYPE_FLOAT : var_type = "doubleValue"
87+ TYPE_STRING : var_type = "stringValue"
88+ TYPE_DICTIONARY :
89+ if is_field_timestamp (value ):
90+ var_type = "timestampValue"
91+ value = dict2timestamp (value )
92+ else :
93+ var_type = "mapValue"
94+ value = dict2fields (value )
95+ TYPE_ARRAY :
96+ var_type = "arrayValue"
97+ value = {"values" : array2fields (value )}
98+
99+ return { var_type : value }
100+
101+ # Pass the .fields inside a Firestore Document to print out the Dictionary { 'key' : 'value' }
102+ static func fields2dict (doc ) -> Dictionary :
103+ var dict = {}
104+ if doc .has ("fields" ):
105+ var fields = doc ["fields" ]
106+ print (fields )
107+ for field in fields .keys ():
108+ if fields [field ].has ("mapValue" ):
109+ dict [field ] = (fields2dict (fields [field ].mapValue ))
110+ elif fields [field ].has ("timestampValue" ):
111+ dict [field ] = timestamp2dict (fields [field ].timestampValue )
112+ elif fields [field ].has ("arrayValue" ):
113+ dict [field ] = fields2array (fields [field ].arrayValue )
114+ elif fields [field ].has ("integerValue" ):
115+ dict [field ] = fields [field ].values ()[0 ] as int
116+ elif fields [field ].has ("doubleValue" ):
117+ dict [field ] = fields [field ].values ()[0 ] as float
118+ elif fields [field ].has ("booleanValue" ):
119+ dict [field ] = fields [field ].values ()[0 ] as bool
120+ elif fields [field ].has ("nullValue" ):
121+ dict [field ] = null
122+ else :
123+ dict [field ] = fields [field ].values ()[0 ]
124+ return dict
125+
126+ # Pass an Array to parse it to a Firebase arrayValue
127+ static func array2fields (array : Array ) -> Array :
128+ var fields : Array = []
129+ var var_type : String = ""
130+ for field in array :
131+ match typeof (field ):
132+ TYPE_DICTIONARY :
133+ if is_field_timestamp (field ):
134+ var_type = "timestampValue"
135+ field = dict2timestamp (field )
136+ else :
137+ var_type = "mapValue"
138+ field = dict2fields (field )
139+ TYPE_NIL : var_type = "nullValue"
140+ TYPE_BOOL : var_type = "booleanValue"
141+ TYPE_INT : var_type = "integerValue"
142+ TYPE_FLOAT : var_type = "doubleValue"
143+ TYPE_STRING : var_type = "stringValue"
144+ TYPE_ARRAY : var_type = "arrayValue"
145+ _ : var_type = "FieldTransform"
146+ fields .append ({ var_type : field })
147+ return fields
148+
149+ # Pass a Firebase arrayValue Dictionary to convert it back to an Array
150+ static func fields2array (array : Dictionary ) -> Array :
151+ var fields : Array = []
152+ if array .has ("values" ):
153+ for field in array .values :
154+ var item
155+ match field .keys ()[0 ]:
156+ "mapValue" :
157+ item = fields2dict (field .mapValue )
158+ "arrayValue" :
159+ item = fields2array (field .arrayValue )
160+ "integerValue" :
161+ item = field .values ()[0 ] as int
162+ "doubleValue" :
163+ item = field .values ()[0 ] as float
164+ "booleanValue" :
165+ item = field .values ()[0 ] as bool
166+ "timestampValue" :
167+ item = timestamp2dict (field .timestampValue )
168+ "nullValue" :
169+ item = null
170+ _ :
171+ item = field .values ()[0 ]
172+ fields .append (item )
173+ return fields
174+
175+ # Converts a gdscript Dictionary (most likely obtained with Time.get_datetime_dict_from_system()) to a Firebase Timestamp
176+ static func dict2timestamp (dict : Dictionary ) -> String :
177+ # dict.erase('weekday')
178+ # dict.erase('dst')
179+ # var dict_values : Array = dict.values()
180+ var time = Time .get_datetime_string_from_datetime_dict (dict , false )
181+ return time
182+ # return "%04d-%02d-%02dT%02d:%02d:%02d.00Z" % dict_values
183+
184+ # Converts a Firebase Timestamp back to a gdscript Dictionary
185+ static func timestamp2dict (timestamp : String ) -> Dictionary :
186+ return Time .get_datetime_dict_from_datetime_string (timestamp , false )
187+ # var datetime : Dictionary = {year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0}
188+ # var dict : PackedStringArray = timestamp.split("T")[0].split("-")
189+ # dict.append_array(timestamp.split("T")[1].split(":"))
190+ # for value in dict.size():
191+ # datetime[datetime.keys()[value]] = int(dict[value])
192+ # return datetime
193+
194+ static func is_field_timestamp (field : Dictionary ) -> bool :
195+ return field .has_all (['year' ,'month' ,'day' ,'hour' ,'minute' ,'second' ])
196+
197+
15198# HTTPRequeust seems to have an issue in Web exports where the body returns empty
16199# This appears to be caused by the gzip compression being unsupported, so we
17200# disable it when web export is detected.
@@ -133,3 +316,11 @@ class ObservableDictionary extends RefCounted:
133316 func _set (property : StringName , value : Variant ) -> bool :
134317 update (property , value )
135318 return true
319+
320+ class AwaitDetachable extends Node2D :
321+ var awaiter : Signal
322+
323+ func _init (freeable_node , await_signal : Signal ) -> void :
324+ awaiter = await_signal
325+ add_child (freeable_node )
326+ awaiter .connect (queue_free )
0 commit comments