Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.x #330

Draft
wants to merge 61 commits into
base: main
Choose a base branch
from
Draft

4.x #330

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c237837
Remove GUT and custom Docs plugin
WolfgangSenff Feb 4, 2023
130d724
Remove remaining GUT references
WolfgangSenff Feb 4, 2023
5fb709a
Initial commit - DO NOT USE
WolfgangSenff Feb 4, 2023
1c52793
Fix remaining issues
WolfgangSenff Feb 6, 2023
5f3e8d1
fixes
fenix-hub May 17, 2023
79bb6dd
fixes
fenix-hub May 17, 2023
3d2d487
Merge pull request #340 from GodotNuts/dev/4.x
WolfgangSenff May 18, 2023
dfadfcd
Update firestore.gd
WolfgangSenff Jun 8, 2023
b8bd1fc
Update README.md
WolfgangSenff Jun 8, 2023
67923d8
Remove persistence
WolfgangSenff Jun 8, 2023
e9075de
Fixes an issue with HTTPRequest on web exports
bsarsgard Jul 4, 2023
57508d4
Fix whitespace
bsarsgard Jul 4, 2023
37cd3f8
Move logic into static utility class
bsarsgard Jul 4, 2023
12f56be
Merge pull request #346 from bsarsgard/4.x
WolfgangSenff Jul 4, 2023
b04b131
Fixed error OAuth 2 fb login
NIKHIL0VERMA Jul 26, 2023
43ba257
Update auth.gd
NIKHIL0VERMA Jul 31, 2023
2067fd6
prevent hanging await and return relative value
Zeedinstein Sep 24, 2023
0bc1b2f
Merge pull request #358 from Zeedinstein/prevent-hanging-await
WolfgangSenff Sep 24, 2023
b198bd8
Update for once functionality
WolfgangSenff Dec 8, 2023
9313ef9
Merge pull request #352 from NIKHIL0VERMA/4.x
WolfgangSenff Jan 13, 2024
c943727
Add 'Web' to OS.get_name() for HTML and UWP exports
BearDooks Jan 18, 2024
a9445fb
Replaced OS.has_feature('JavaScript') with OS.has_feature('web'), as …
kyboon Jan 27, 2024
00a0749
Merge pull request #380 from kyboon/4.x
WolfgangSenff Jan 27, 2024
c2b0c73
Fix not login_succeeded if manual refresh
WolfgangSenff Mar 18, 2024
2e2c302
Add needs login method
WolfgangSenff Mar 19, 2024
226bdd5
Allow exported env file
WolfgangSenff Mar 19, 2024
9b43b87
fix: storage path now accepts space characters
Vitorgus Mar 22, 2024
440fae3
Added user-friendly descriptions of action codes for errors in Firest…
decadentpig Mar 26, 2024
b8e9328
Merge pull request #388 from Vitorgus/fix_space_in_storage_path_godot_4
WolfgangSenff Mar 26, 2024
4f3852f
Ensure task description exists in map before printing
decadentpig Mar 26, 2024
33173d9
Merge pull request #389 from decadentpig/4.x
WolfgangSenff Mar 26, 2024
5317bb4
Update .gitattributes
WolfgangSenff Mar 26, 2024
e23e28b
Updates from the test harness (#390)
WolfgangSenff Mar 26, 2024
1dcd987
Fix bug in task map def
WolfgangSenff Mar 26, 2024
5ae050f
Fix slight bug in EnvPath
WolfgangSenff Mar 26, 2024
30c3029
Fix attempt to export env file
WolfgangSenff Mar 27, 2024
9a2d596
Remove auth file after successful account deletion (#397)
matth3wdsouza May 1, 2024
569ad20
Refactor for realtime database, remote config, Firestore fixes
WolfgangSenff May 25, 2024
4783eba
Merge pull request #399 from GodotNuts/Refactor
WolfgangSenff May 25, 2024
49e608a
Fix tabs
WolfgangSenff May 30, 2024
908eb9c
Fix completed.emit issue
WolfgangSenff Jun 1, 2024
7dca318
Fix other tabs, ugh.
WolfgangSenff Jun 1, 2024
9f0586e
Fix last tabs forever hopefully.
WolfgangSenff Jun 2, 2024
19bc818
Massive Firestore refactor
WolfgangSenff Jun 4, 2024
eb1f83f
Merge pull request #400 from GodotNuts/massive-firestore-refactor
WolfgangSenff Jun 4, 2024
b307f50
Update storage to make StorageTask fully transparent
WolfgangSenff Jun 10, 2024
40e7b5c
Merge pull request #403 from GodotNuts/refactor-storage-remove-task
WolfgangSenff Jun 10, 2024
24a576f
Re-add fields2dict
WolfgangSenff Jun 10, 2024
c27bf9e
Update plugin version finally
WolfgangSenff Jun 10, 2024
c7f33ad
Merge pull request #404 from GodotNuts/fix-utilities-issue
WolfgangSenff Jun 10, 2024
04916a0
fix: add arrayValue conversion to from_firebase_type
auntiebirdie Jun 19, 2024
091aa11
Merge pull request #408 from auntiebirdie/add-arrayvalue-from-firebas…
WolfgangSenff Jun 19, 2024
fa306a5
Update so fields have correct types
WolfgangSenff Jun 27, 2024
ea1276e
fix: check that request result has fields property
theSlyest Jul 18, 2024
78db57d
Merge pull request #417 from theSlyest/fix_request_with_empty_fields_…
WolfgangSenff Jul 18, 2024
024ef50
Fix null/empty doc issue
WolfgangSenff Jul 18, 2024
6d32fbe
Add ability to do aggregation queries
WolfgangSenff Jul 19, 2024
7631558
Fix typo in docs
WolfgangSenff Jul 19, 2024
46dc6f7
Fix whitespace changes
WolfgangSenff Jul 19, 2024
fc5238d
Merge pull request #420 from GodotNuts/add-agg-queries
WolfgangSenff Jul 19, 2024
49e6a6f
Fix indentation issue causing null ref
WolfgangSenff Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@
/LICENSE export-ignore
/README.md export-ignore
/CODE_OF_CONDUCT.md export-ignore

* text=auto eol=lf
16 changes: 0 additions & 16 deletions .gutconfig.json

This file was deleted.

10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ [email protected]

The following individuals and many more have contributed significantly to this project. If you would like to support this project's further development, consider supporting them.

- [Kyle Szklenski](https://github.com/WolfgangSenff) (creator) ([buy me a coffee](https://ko-fi.com/kyleszklenski))
- [Nicolò Santilio](https://github.com/fenix-hub) (creator)
- [Chuck Lindblom](https://github.com/BearDooks) (creator)
- [SIsilicon](https://github.com/SISilicon) (creator)
- [Luke Hollenback](https://github.com/lukehollenback) ([buy me a coffee](https://ko-fi.com/lukehollenback))
- [Kyle Szklenski](https://github.com/WolfgangSenff) (creator - original, Database, Functions, several features) ([buy me a coffee](https://ko-fi.com/kyleszklenski))
- [Nicolò Santilio](https://github.com/fenix-hub) (creator - Firestore, several features)
- [Chuck Lindblom](https://github.com/BearDooks) (creator - several features across the board)
- [SIsilicon](https://github.com/SISilicon) (creator - Firestore, a few other features)
- [Luke Hollenback](https://github.com/lukehollenback) (unit testing) ([buy me a coffee](https://ko-fi.com/lukehollenback))

## :arrow_down: Cloning
SSH:
Expand Down
346 changes: 346 additions & 0 deletions addons/godot-firebase/Utilities.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
extends Node
class_name Utilities

static func get_json_data(value):
if value is PackedByteArray:
value = value.get_string_from_utf8()
var json = JSON.new()
var json_parse_result = json.parse(value)
if json_parse_result == OK:
return json.data

return null


# Pass a dictionary { 'key' : 'value' } to format it in a APIs usable .fields
# Field Path3D using the "dot" (`.`) notation are supported:
# ex. { "PATH.TO.SUBKEY" : "VALUE" } ==> { "PATH" : { "TO" : { "SUBKEY" : "VALUE" } } }
static func dict2fields(dict : Dictionary) -> Dictionary:
var fields = {}
var var_type : String = ""
for field in dict.keys():
var field_value = dict[field]
if field is String and "." in field:
var keys: Array = field.split(".")
field = keys.pop_front()
keys.reverse()
for key in keys:
field_value = { key : field_value }

match typeof(field_value):
TYPE_NIL: var_type = "nullValue"
TYPE_BOOL: var_type = "booleanValue"
TYPE_INT: var_type = "integerValue"
TYPE_FLOAT: var_type = "doubleValue"
TYPE_STRING: var_type = "stringValue"
TYPE_DICTIONARY:
if is_field_timestamp(field_value):
var_type = "timestampValue"
field_value = dict2timestamp(field_value)
else:
var_type = "mapValue"
field_value = dict2fields(field_value)
TYPE_ARRAY:
var_type = "arrayValue"
field_value = {"values": array2fields(field_value)}

if fields.has(field) and fields[field].has("mapValue") and field_value.has("fields"):
for key in field_value["fields"].keys():
fields[field]["mapValue"]["fields"][key] = field_value["fields"][key]
else:
fields[field] = { var_type : field_value }

return {'fields' : fields}


class FirebaseTypeConverter extends RefCounted:
var converters = {
"nullValue": _to_null,
"booleanValue": _to_bool,
"integerValue": _to_int,
"doubleValue": _to_float
}

func convert_value(type, value):
if converters.has(type):
return converters[type].call(value)

return value

func _to_null(value):
return null

func _to_bool(value):
return bool(value)

func _to_int(value):
return int(value)

func _to_float(value):
return float(value)

static func from_firebase_type(value):
if value == null:
return null

if value.has("mapValue"):
value = fields2dict(value.values()[0])
elif value.has("arrayValue"):
value = fields2array(value.values()[0])
elif value.has("timestampValue"):
value = Time.get_datetime_dict_from_datetime_string(value.values()[0], false)
else:
var converter = FirebaseTypeConverter.new()
value = converter.convert_value(value.keys()[0], value.values()[0])

return value


static func to_firebase_type(value : Variant) -> Dictionary:
var var_type : String = ""

match typeof(value):
TYPE_NIL: var_type = "nullValue"
TYPE_BOOL: var_type = "booleanValue"
TYPE_INT: var_type = "integerValue"
TYPE_FLOAT: var_type = "doubleValue"
TYPE_STRING: var_type = "stringValue"
TYPE_DICTIONARY:
if is_field_timestamp(value):
var_type = "timestampValue"
value = dict2timestamp(value)
else:
var_type = "mapValue"
value = dict2fields(value)
TYPE_ARRAY:
var_type = "arrayValue"
value = {"values": array2fields(value)}

return { var_type : value }

# Pass the .fields inside a Firestore Document to print out the Dictionary { 'key' : 'value' }
static func fields2dict(doc) -> Dictionary:
var dict = {}
if doc.has("fields"):
var fields = doc["fields"]

for field in fields.keys():
if fields[field].has("mapValue"):
dict[field] = (fields2dict(fields[field].mapValue))
elif fields[field].has("timestampValue"):
dict[field] = timestamp2dict(fields[field].timestampValue)
elif fields[field].has("arrayValue"):
dict[field] = fields2array(fields[field].arrayValue)
elif fields[field].has("integerValue"):
dict[field] = fields[field].values()[0] as int
elif fields[field].has("doubleValue"):
dict[field] = fields[field].values()[0] as float
elif fields[field].has("booleanValue"):
dict[field] = fields[field].values()[0] as bool
elif fields[field].has("nullValue"):
dict[field] = null
else:
dict[field] = fields[field].values()[0]
return dict

# Pass an Array to parse it to a Firebase arrayValue
static func array2fields(array : Array) -> Array:
var fields : Array = []
var var_type : String = ""
for field in array:
match typeof(field):
TYPE_DICTIONARY:
if is_field_timestamp(field):
var_type = "timestampValue"
field = dict2timestamp(field)
else:
var_type = "mapValue"
field = dict2fields(field)
TYPE_NIL: var_type = "nullValue"
TYPE_BOOL: var_type = "booleanValue"
TYPE_INT: var_type = "integerValue"
TYPE_FLOAT: var_type = "doubleValue"
TYPE_STRING: var_type = "stringValue"
TYPE_ARRAY: var_type = "arrayValue"
_: var_type = "FieldTransform"
fields.append({ var_type : field })
return fields

# Pass a Firebase arrayValue Dictionary to convert it back to an Array
static func fields2array(array : Dictionary) -> Array:
var fields : Array = []
if array.has("values"):
for field in array.values:
var item
match field.keys()[0]:
"mapValue":
item = fields2dict(field.mapValue)
"arrayValue":
item = fields2array(field.arrayValue)
"integerValue":
item = field.values()[0] as int
"doubleValue":
item = field.values()[0] as float
"booleanValue":
item = field.values()[0] as bool
"timestampValue":
item = timestamp2dict(field.timestampValue)
"nullValue":
item = null
_:
item = field.values()[0]
fields.append(item)
return fields

# Converts a gdscript Dictionary (most likely obtained with Time.get_datetime_dict_from_system()) to a Firebase Timestamp
static func dict2timestamp(dict : Dictionary) -> String:
#dict.erase('weekday')
#dict.erase('dst')
#var dict_values : Array = dict.values()
var time = Time.get_datetime_string_from_datetime_dict(dict, false)
return time
#return "%04d-%02d-%02dT%02d:%02d:%02d.00Z" % dict_values

# Converts a Firebase Timestamp back to a gdscript Dictionary
static func timestamp2dict(timestamp : String) -> Dictionary:
return Time.get_datetime_dict_from_datetime_string(timestamp, false)
#var datetime : Dictionary = {year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0}
#var dict : PackedStringArray = timestamp.split("T")[0].split("-")
#dict.append_array(timestamp.split("T")[1].split(":"))
#for value in dict.size():
#datetime[datetime.keys()[value]] = int(dict[value])
#return datetime

static func is_field_timestamp(field : Dictionary) -> bool:
return field.has_all(['year','month','day','hour','minute','second'])


# HTTPRequeust seems to have an issue in Web exports where the body returns empty
# This appears to be caused by the gzip compression being unsupported, so we
# disable it when web export is detected.
static func fix_http_request(http_request):
if is_web():
http_request.accept_gzip = false

static func is_web() -> bool:
return OS.get_name() in ["HTML5", "Web"]


class MultiSignal extends RefCounted:
signal completed(with_signal)
signal all_completed()

var _has_signaled := false
var _early_exit := false

var signal_count := 0

func _init(sigs : Array[Signal], early_exit := true, should_oneshot := true) -> void:
_early_exit = early_exit
for sig in sigs:
add_signal(sig, should_oneshot)

func add_signal(sig : Signal, should_oneshot) -> void:
signal_count += 1
sig.connect(
func():
if not _has_signaled and _early_exit:
completed.emit(sig)
_has_signaled = true
elif not _early_exit:
completed.emit(sig)
signal_count -= 1
if signal_count <= 0: # Not sure how it could be less than
all_completed.emit()
, CONNECT_ONE_SHOT if should_oneshot else CONNECT_REFERENCE_COUNTED
)

class SignalReducer extends RefCounted: # No need for a node, as this deals strictly with signals, which can be on any object.
signal completed

var awaiters : Array[Signal] = []

var reducers = {
0 : func(): completed.emit(),
1 : func(p): completed.emit(),
2 : func(p1, p2): completed.emit(),
3 : func(p1, p2, p3): completed.emit(),
4 : func(p1, p2, p3, p4): completed.emit()
}

func add_signal(sig : Signal, param_count : int = 0) -> void:
assert(param_count < 5, "Too many parameters to reduce, just add more!")
sig.connect(reducers[param_count], CONNECT_ONE_SHOT) # May wish to not just one-shot, but instead track all of them firing

class SignalReducerWithResult extends RefCounted: # No need for a node, as this deals strictly with signals, which can be on any object.
signal completed(result)

var awaiters : Array[Signal] = []

var reducers = {
0 : func(): completed.emit(),
1 : func(p): completed.emit({1 : p}),
2 : func(p1, p2): completed.emit({ 1 : p1, 2 : p2 }),
3 : func(p1, p2, p3): completed.emit({ 1 : p1, 2 : p2, 3 : p3 }),
4 : func(p1, p2, p3, p4): completed.emit({ 1 : p1, 2 : p2, 3 : p3, 4 : p4 })
}

func add_signal(sig : Signal, param_count : int = 0) -> void:
assert(param_count < 5, "Too many parameters to reduce, just add more!")
sig.connect(reducers[param_count], CONNECT_ONE_SHOT) # May wish to not just one-shot, but instead track all of them firing

class ObservableDictionary extends RefCounted:
signal keys_changed()

var _internal : Dictionary
var is_notifying := true

func _init(copy : Dictionary = {}) -> void:
_internal = copy

func add(key : Variant, value : Variant) -> void:
_internal[key] = value
if is_notifying:
keys_changed.emit()

func update(key : Variant, value : Variant) -> void:
_internal[key] = value
if is_notifying:
keys_changed.emit()

func has(key : Variant) -> bool:
return _internal.has(key)

func keys():
return _internal.keys()

func values():
return _internal.values()

func erase(key : Variant) -> bool:
var result = _internal.erase(key)
if is_notifying:
keys_changed.emit()

return result

func get_value(key : Variant) -> Variant:
return _internal[key]

func _get(property: StringName) -> Variant:
if _internal.has(property):
return _internal[property]

return false

func _set(property: StringName, value: Variant) -> bool:
update(property, value)
return true

class AwaitDetachable extends Node2D:
var awaiter : Signal

func _init(freeable_node, await_signal : Signal) -> void:
awaiter = await_signal
add_child(freeable_node)
awaiter.connect(queue_free)
Loading
Loading