Add addon database

This commit is contained in:
VAILLANT Jeremy
2021-05-13 15:39:31 +02:00
parent 5791514d43
commit e8edc134e1
66 changed files with 4953 additions and 0 deletions
@@ -0,0 +1,38 @@
"""
class GDDBConstants
"""
class_name GDDBConstants
tool
extends Node
# GDDB signature
const c_gddb_signature = "GDDB_ver"
# GDDB ver
const c_gddb_ver = "2.0"
# invalid id to initialize integer properties
const c_invalid_id = -1
# maximum database name length
const c_max_db_name_len = 16
# maximum table name length
const c_max_table_name_len = 16
# characters that should not be part of the database name
const c_invalid_characters = "`~!@#$%^&*()=+[]{}\\|;:'\",<.>/?"
# addon main path
const c_addon_main_path = "res://addons/godot_db_manager/"
# maximum tables list width
const c_max_tables_list_width = 400.0
# minimum cell width
const c_min_cell_width = 150.0
# maximum cell width
const c_max_cell_width = 400.0
@@ -0,0 +1,96 @@
"""
class GDDBGlobals
"""
class_name GDDBGlobals
tool
extends Node
# a flag that tells if the tool's interface is active or not
var m_is_interface_active : bool = false
# sets the active flag
func set_interface_active(active : bool) -> void :
m_is_interface_active = active
# returns true if the tool's interface is active
func is_interface_active() -> bool :
return m_is_interface_active
# returns the name of the data type
func get_data_name(data_type : int) -> String :
if(data_type == gddb_types.e_prop_type_bool):
return "Bool"
elif(data_type == gddb_types.e_prop_type_int):
return "Integer"
elif(data_type == gddb_types.e_prop_type_float):
return "Float"
elif(data_type == gddb_types.e_prop_type_string):
return "String"
elif(data_type == gddb_types.e_prop_type_resource):
return "Resource"
print("GDDBGlobals::get_data_name(" + str(data_type) + ")")
return "Unknown data type"
# returns the name of the data filter
func get_data_filter_name(data_filter_type : int) -> String :
if(data_filter_type == gddb_types.e_data_filter_equal):
return "Equal"
elif(data_filter_type == gddb_types.e_data_filter_not_equal):
return "Not equal"
elif(data_filter_type == gddb_types.e_data_filter_less):
return "Less"
elif(data_filter_type == gddb_types.e_data_filter_greater):
return "Greater"
elif(data_filter_type == gddb_types.e_data_filter_lequal):
return "Less or equal"
elif(data_filter_type == gddb_types.e_data_filter_gequal):
return "Greater or equal"
print("GDDBGlobals::get_data_filter_name(" + str(data_filter_type) + ")")
return "Unknown data filter type"
# checks the name of the database
func check_db_name(db_name : String) -> bool :
for idx in range(0, db_name.length()):
for jdx in range(0, gddb_constants.c_invalid_characters.length()):
if(db_name[idx] == gddb_constants.c_invalid_characters[jdx]):
return false
return true
# returns a json from a row from a table
func get_json_from_row(table : Object, row_idx : int) -> String :
var json = "{"
var row = table.get_data_at_row_idx(row_idx)
for jdx in range(0, row.size()):
var prop = table.get_prop_at(jdx)
json += "\"" + prop.get_prop_name() + "\":"
json += "\"" + row[jdx].get_data() + "\""
if(jdx < row.size() - 1):
json += ", "
json += "}"
return json
# returns the digits count from a number
func get_digits_count(number : int) -> int :
if(number == 0):
return 1
var digits_count = 0
while(number > 0):
number /= 10
digits_count += 1
return digits_count
# replace special characters in a string to handle properly saving into database
func handle_string(text : String) -> String :
var string = ""
for idx in range(0, text.length()):
if(text[idx] == "\n"):
string += "\\n"
else:
string += text[idx]
return string
+45
View File
@@ -0,0 +1,45 @@
"""
class GDDBTypes
"""
class_name GDDBTypes
tool
extends Node
# Database loading errors
enum {
e_db_invalid_file = -11,
e_db_invalid_ver = -10,
e_db_valid = 0
}
# Property types
enum {
e_prop_type_bool = 0
e_prop_type_int = 1,
e_prop_type_float = 2,
e_prop_type_string = 3,
# TODO: insert more data types here and increase e_prop_type_resource
e_prop_type_resource = 4,
e_prop_types_count
}
# Data filters
enum {
e_data_filter_equal = 0,
e_data_filter_not_equal = 1,
e_data_filter_less = 2,
e_data_filter_greater = 3,
e_data_filter_lequal = 4, # less or equal
e_data_filter_gequal = 5 # greater or equal
}
# new table dlg type
enum {
e_new_dlg_type_new = 0,
e_new_dlg_type_edit = 1
}
+323
View File
@@ -0,0 +1,323 @@
"""
GDDatabase class
"""
class_name GDDatabase
extends Object
# database types
enum {
e_db_type_json = 0, # JSON database
e_db_type_binary = 1 # binary database - to be designed / implemented
}
# database type
var m_db_type : int = e_db_type_json
# the id of the database
var m_db_id : int = gddb_constants.c_invalid_id
# the name of the database
var m_db_name : String = ""
# tables in the database
var m_tables : Array = []
# the path of the database
var m_db_filepath : String = ""
# if the database is not saved, it is dirty
var m_is_dirty : bool = false
# sets the id of the database
func set_db_id(db_id : int) -> void :
m_db_id = db_id
# returns the database id
func get_db_id() -> int :
return m_db_id
# set the name of the database
# the name of the database should not contain special characters
func set_db_name(db_name : String) -> bool :
if(!gddb_globals.check_db_name(db_name)):
print("ERROR: GDDatabase::set_db_name(" + db_name + ") - the name of the database contains invalid characters")
return false
# print("GDDatabase::set_db_name(" + db_name + ")")
m_db_name = db_name
return true
# returns the name of the database
func get_db_name() -> String :
return m_db_name
# sets the database file path
func set_db_filepath(filepath : String) -> void :
m_db_filepath = filepath
# returns the path of the database
func get_db_filepath() -> String :
return m_db_filepath
# checks if a table with the name "table_name" can be added into database
func can_add_table(table_name : String, table_id : int = -1) -> bool :
# print("GDDatabase::can_add_table(" + table_name + ", " + str(table_id) + ")")
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_name() == table_name):
if(m_tables[idx].get_table_id() == table_id):
continue
print("WARNING: GDDatabase::can_add_table(" + table_name + ", " + str(table_id) + ") - table already exists")
return false
return true
# adds a new table
# returns the table id; if already exists, it will fire an warning on output console and returns an invalid id
func add_table(table_name : String) -> int :
if(!can_add_table(table_name)):
print("WARNING: GDDatabase::add_table(" + table_name + ") - cannot add table")
return gddb_constants.c_invalid_id
# print("GDDatabase::add_table(" + table_name + ") to \"" + m_db_name + "\" database")
var table_id = generate_new_table_id()
var table = load(gddb_constants.c_addon_main_path + "core/db_table.gd").new()
table.set_table_id(table_id)
table.set_table_name(table_name)
table.set_parent_database(self)
m_tables.push_back(table)
return table_id
# edits a table name
func edit_table_name(table_name : String, table_id : int) -> bool :
if(!can_add_table(table_name, table_id)):
return false
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_id() == table_id):
m_tables[idx].set_table_name(table_name)
break
return true
# deletes a table at index
func delete_table_at(idx : int) -> void :
if(idx < 0 || idx > m_tables.size() - 1):
print("GDDatabase::delete_table_at(" + str(idx) + ") - index out of bounds")
return
m_tables[idx].clear()
m_tables[idx].free()
m_tables.remove(idx)
# deletes a table by id
func delete_table_by_id(table_id: int) -> void :
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_id() == table_id):
# print("GDDatabase::delete_table_by_id(" + str(table_id) + ")")
m_tables[idx].clear()
m_tables[idx].free()
m_tables.remove(idx)
return
print("ERROR: GDDatabase::delete_table_by_id(" + str(table_id) + ") - cannot erase table; id not found")
# deletes a table by name
func delete_table_by_name(table_name: String) -> void :
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_name() == table_name):
# print("GDDatabase::delete_table_by_name(" + table_name + ")")
m_tables[idx].clear()
m_tables[idx].free()
m_tables.remove(idx)
return
print("ERROR: GDDatabase::delete_table_by_name(" + table_name + ") - cannot erase table; name not found")
# generates a new table id
func generate_new_table_id() -> int :
if(m_tables.size() == 0):
return 0
return m_tables[m_tables.size()-1].get_table_id() + 1
# returns the count of the tables in the database
func get_tables_count() -> int :
return m_tables.size()
# returns true if the table exists in the database, false otherwise
# this is equivalent with (get_table_by_name(table name) != null) function, but without firing the error in case the table doesn't exist
func is_table_exists(table_name : String) -> bool :
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_name() == table_name):
return true
return false
# returns a table by an index or null if the index is invalid
func get_table_at(idx: int) -> Object :
if(idx < 0 || idx >= m_tables.size()):
print("ERROR: GDDatabase::get_table_at(" + str(idx) + ") - cannot obtain table with index")
return null
return m_tables[idx]
# returns a table by its id or null if a table with id doesn's exist
func get_table_by_id(table_id: int) -> Object :
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_id() == table_id):
return m_tables[idx]
print("ERROR: GDDatabase::get_table_by_id(" + str(table_id) + ") - cannot obtain table with id")
return null
# returns a table by its name or null if the name of the table doesn's exist
func get_table_by_name(table_name: String) -> Object :
for idx in range(0, m_tables.size()):
if(m_tables[idx].get_table_name() == table_name):
return m_tables[idx]
print("ERROR: GDDatabase::get_table_by_id(" + table_name + ") - cannot obtain table with name")
return null
# deletes all the tables
func clear() -> void :
# print("GDDatabase::clear()")
m_db_name = ""
for idx in range(0, m_tables.size()):
m_tables[idx].clear()
m_tables.clear()
# sets the database dirty; it is not saved
func set_dirty(dirty : bool) -> void :
m_is_dirty = dirty
# returns true if a database is dirty (should be saved), false otherwise
func is_dirty() -> bool :
return m_is_dirty
# serialization
func save_db() -> void :
if(m_db_name.empty()):
print("ERROR: GDDatabase::save_db() - current database doesn't have a name")
return
if(m_db_filepath.empty()):
print("ERROR: GDDatabase::save_db() - current database doesn't have a path file")
return
# print("GDDatabase::save_db() - " + m_db_name + " to: " + m_db_filepath)
var text = "{"
text += "\n\t\"" + gddb_constants.c_gddb_signature + "\":\"" + gddb_constants.c_gddb_ver + "\","
text += "\n\t\"db_name\":\"" + m_db_name + "\","
text += "\n\t\"tables\":["
for idx in range(0, m_tables.size()):
text += "\n\t\t{"
text += "\n\t\t\t\"table_name\":\"" + m_tables[idx].get_table_name() + "\","
text += "\n\t\t\t\"props\":["
for jdx in range(0, m_tables[idx].get_props_count()):
var db_prop = m_tables[idx].get_prop_at(jdx)
text += "\n\t\t\t\t\t{"
text += "\"name\":\"" + str(db_prop.get_prop_name()) + "\","
var prop_type = db_prop.get_prop_type()
if(prop_type < gddb_types.e_prop_types_count):
text += "\"type\":\"" + str(prop_type) + "\","
else:
# print("GDDatabase::save_db() - prop_type: " + str(prop_type))
var table_id = prop_type - gddb_types.e_prop_types_count
var table = get_table_by_id(table_id)
if(null == table):
print("GDDatabase::save_db() - table not found with id: " + str(table_id))
text += "\"type\":\"" + "table" + "\","
text += "\"table_name\":\"" + table.get_table_name() + "\","
text += "\"auto_increment\":\"" + str(int(db_prop.has_autoincrement())) + "\""
text += "}"
if(jdx < m_tables[idx].get_props_count() - 1):
text += ","
text += "\n\t\t\t],"
text += "\n\t\t\t\"data\":["
for jdx in range(0, m_tables[idx].get_data_size()):
#var the_data = m_tables[idx].get_data_at(jdx)
#print("getting data at " + str(jdx) + " : " + the_data)
text += "\"" + m_tables[idx].get_data_at(jdx) + "\""
if(jdx < m_tables[idx].get_data_size() - 1):
text += ","
text += "]" # end of data
text += "\n\t\t}" # end of table
if(idx < m_tables.size() - 1):
text += ","
text += "\n\t]\n}"
var save_file = File.new()
save_file.open(m_db_filepath, File.WRITE)
save_file.store_string(text)
save_file.close()
set_dirty(false)
# deserialization
func load_db() -> int :
var file = File.new()
file.open(get_db_filepath(), File.READ)
var content = file.get_as_text()
file.close()
var dictionary = JSON.parse(content).result
# check the signature
if(!dictionary.has(gddb_constants.c_gddb_signature)):
print("GDDatabase::load_db() - invalid database file")
return gddb_types.e_db_invalid_file
var gddb_signature = dictionary[gddb_constants.c_gddb_signature]
if(gddb_signature != gddb_constants.c_gddb_ver):
print("GDDatabase::load_db() - invalid database version")
return gddb_types.e_db_invalid_ver
clear()
m_db_name = dictionary["db_name"]
var tables = dictionary["tables"]
for idx in range(0, tables.size()):
var table_id = add_table(tables[idx]["table_name"])
var table = get_table_by_id(table_id)
var props_count = tables[idx]["props"].size()
if(props_count == 0):
continue
for jdx in range(0, props_count):
var prop_type = tables[idx]["props"][jdx]["type"]
var prop_id = -1
if(prop_type == "table"):
var table_name = tables[idx]["props"][jdx]["table_name"]
prop_id = table.add_table_prop(tables[idx]["props"][jdx]["name"], table_name)
else:
prop_id = table.add_prop(prop_type.to_int(), tables[idx]["props"][jdx]["name"])
var prop = table.get_prop_by_id(prop_id)
var enable_autoincrement = tables[idx]["props"][jdx]["auto_increment"].to_int()
prop.enable_autoincrement(bool(enable_autoincrement))
var data_count = tables[idx]["data"].size()
#print("********* set data to db - begin")
for jdx in range(0, data_count / props_count):
var row_data = []
for kdx in range(0, props_count):
var cell_data = tables[idx]["data"][jdx * props_count + kdx]
# print("cell_data: " + cell_data)
row_data.push_back(cell_data)
#print("row_data: " + str(row_data))
table.add_row(row_data)
#print("********* set data to db - end")
# link custom data to tables
for idx in range(0, m_tables.size()):
m_tables[idx].link_tables_props()
return gddb_types.e_db_valid
# dumps the database
func dump() -> String :
var dump_text = "\nDatabase dump. id: " + str(m_db_id) + ", name: " + m_db_name + ", filepath: " + m_db_filepath
dump_text += "\n------------------------------------------------------------------------------------\n"
for idx in range(0, m_tables.size()):
dump_text += "\n" + m_tables[idx].dump()
return dump_text
+40
View File
@@ -0,0 +1,40 @@
"""
GDDBData class
"""
class_name GDDBData
extends Object
var m_prop_id : int = -1
var m_row_idx : int = -1
var m_data : String = ""
# sets the property id
func set_prop_id(prop_id : int) -> void :
m_prop_id = prop_id
# returns the property id
func get_prop_id() -> int :
return m_prop_id
# sets the row index
func set_row_idx(row_idx : int) -> void :
m_row_idx = row_idx
# returns the row index
func get_row_idx() -> int :
return m_row_idx
# sets the data
func set_data(data : String) -> void :
m_data = data
# returns the data
func get_data() -> String :
return m_data
# dumps the data
func dump() -> String :
var dump_text = "prop_id: " + str(m_prop_id) + ", row_idx: " + str(m_row_idx) + ", data: " + m_data
return dump_text
+141
View File
@@ -0,0 +1,141 @@
"""
GDDBMan class
"""
class_name GDDBMan
extends Object
var m_databases = []
# adds a database
func add_database(db_name : String) -> int :
if(!can_add_db(db_name)):
print("ERROR: GDDBMan::add_database(" + db_name + ") already exists")
return gddb_constants.c_invalid_id
# print("GDDBMan::add_database(" + db_name + ")")
var db_id = generate_new_db_id()
var db = load(gddb_constants.c_addon_main_path + "core/database.gd").new()
db.set_db_id(db_id)
db.set_db_name(db_name)
m_databases.push_back(db)
return db_id
# loads a database from a file
func load_database(filepath : String) -> int :
var db_id = generate_new_db_id()
var db = load(gddb_constants.c_addon_main_path + "core/database.gd").new()
db.set_db_id(db_id)
db.set_db_filepath(filepath)
if(db.load_db() == gddb_types.e_db_invalid_file):
db.free()
return gddb_types.e_db_invalid_file
elif(db.load_db() == gddb_types.e_db_invalid_ver):
db.free()
return gddb_types.e_db_invalid_ver
m_databases.push_back(db)
return db_id
# erases a database at index
# it does not erase the database file
func erase_db_at(idx : int) -> void :
if(idx < 0 || idx > m_databases.size() - 1):
print("ERROR: GDDBMan::erase_db_at(" + str(idx) + ") - index out of bounds")
m_databases[idx].clear()
m_databases[idx].free()
m_databases.remove(idx)
# erases a database by id
# it does not erase the database file
func erase_db_by_id(db_id : int) -> void :
var db_found = false
for idx in range(0, m_databases.size()):
if(m_databases[idx].get_db_id() == db_id):
m_databases[idx].clear()
m_databases[idx].free()
m_databases.remove(idx)
db_found = true
break
if(!db_found):
print("ERROR: GDDBMan::erase_db_by_id(" + str(db_id) + ") - database not found")
# erases a database by name
# it does not erase the database file
func erase_db_by_name(db_name : String) -> void :
var db_found = false
for idx in range(0, m_databases.size()):
if(m_databases[idx].get_db_name() == db_name):
m_databases[idx].clear()
m_databases[idx].free()
m_databases.remove(idx)
db_found = true
break
if(!db_found):
print("ERROR: GDDBMan::erase_db_by_id(" + db_name + ") - database not found")
# returns the databases count
func get_databases_count() -> int :
return m_databases.size()
# returns a database at index
func get_db_at(idx : int) -> Object :
if(idx < 0 || idx >= m_databases.size()):
print("ERROR: GDDBMan::get_db_at(" + str(idx) + ") - invalid index")
return null
return m_databases[idx]
# returns a database by an id
func get_db_by_id(db_id : int) -> Object :
for idx in range(0, m_databases.size()):
if(m_databases[idx].get_db_id() == db_id):
return m_databases[idx]
print("ERROR: GDDBMan::get_db_by_id(" + str(db_id) + ") - invalid id")
return null
# returns a database by a name
func get_db_by_name(db_name : String) -> Object :
for idx in range(0, m_databases.size()):
if(m_databases[idx].get_db_name() == db_name):
return m_databases[idx]
return null
# generates a new table id
func generate_new_db_id() -> int :
if(m_databases.size() == 0):
return 0
return m_databases[m_databases.size()-1].get_db_id() + 1
# checks if a database already exists
func can_add_db(db_name : String) -> bool :
for idx in range(0, m_databases.size()):
if(m_databases[idx].get_db_name() == db_name):
print("ERROR: Database with name \"" + db_name + "\" already exists")
return false
return true
# deletes all databases
func clear() -> void :
for idx in range(0, m_databases.size()):
m_databases[idx].clear()
m_databases[idx].free()
m_databases.clear()
# dumps all databases
func dump(to_console : bool = false) -> String :
var dump_text = "\nDatabase manager - dump"
dump_text += "\n===================================================================================="
for idx in range(0, m_databases.size()):
dump_text += m_databases[idx].dump()
dump_text += "===================================================================================="
if(to_console):
print(dump_text)
return dump_text
+71
View File
@@ -0,0 +1,71 @@
"""
GDDBProperty class
"""
class_name GDDBProperty
extends Object
var m_prop_id : int = -1
var m_prop_type : int = gddb_types.e_prop_type_int
var m_custom_type : String = ""
var m_prop_name : String = ""
var m_autoincrement : bool = false
# sets the property id
func set_prop_id(prop_id : int) -> void :
m_prop_id = prop_id
# returns the property id
func get_prop_id() -> int :
return m_prop_id
# sets the property type
func set_prop_type(prop_type : int) -> void :
m_prop_type = prop_type
# returns the property type
func get_prop_type() -> int :
return m_prop_type
# sets the custom type
func set_prop_custom_type(prop_type : String) -> void :
m_custom_type = prop_type
# returns the custom type
func get_prop_custom_type() -> String :
return m_custom_type
# sets the property name
func set_prop_name(prop_name : String) -> void :
m_prop_name = prop_name
# returns the property name
func get_prop_name() -> String :
return m_prop_name
# enables or disables the auto increment property
func enable_autoincrement(enable : bool) -> void :
if(enable && m_prop_type != gddb_types.e_prop_type_int):
if(m_prop_type < gddb_types.e_prop_types_count):
print("ERROR: autoincrement option can be set to integer data type only. Type is: " + gddb_types.get_data_name(m_prop_type))
else:
print("ERROR: autoincrement option can be set to integer data type only. Custom type is: " + m_custom_type)
return
m_autoincrement = enable
# returns if the property has auto increment
func has_autoincrement() -> bool :
return m_autoincrement
# dumps the property
func dump() -> String :
var dump_text = "prop_id: " + str(m_prop_id) + ", prop_name: " + m_prop_name
if(m_prop_type <= gddb_types.e_prop_type_resource):
dump_text += ", prop_type: " + gddb_globals.get_data_name(m_prop_type)
else:
dump_text += ", custom_prop_type: " + m_custom_type
return dump_text
+574
View File
@@ -0,0 +1,574 @@
"""
GDDBTable class
"""
class_name GDDBTable
extends Object
var m_table_id : int = gddb_constants.c_invalid_id
var m_table_name : String = ""
var m_props : Array = []
var m_data : Array = []
var m_rows_count : int = 0
var m_parent_database : Object = null
# sets the table id
func set_table_id(table_id : int) -> void :
# print("GDDBDTable::set_table_id(" + str(table_id) + ")")
m_table_id = table_id
# returns the table id
func get_table_id() -> int :
return m_table_id
# sets the table name
func set_table_name(table_name: String) -> void :
# print("GDDBDTable::set_table_name(" + table_name + ")")
m_table_name = table_name
# returns the table name
func get_table_name() -> String :
return m_table_name
# sets the parent database
func set_parent_database(db : Object) -> void :
# print("GDDBTable::set_parent_database(" + str(db) + ")")
m_parent_database = db
# returns parent database
func get_parent_database() -> Object :
return m_parent_database
# adds a property in the table structure as a base type
# returns prop ID
func add_prop(prop_type : int, prop_name : String) -> int :
var prop_id = generate_new_prop_id()
# print("GDDBDTable::add_prop(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ")")
var prop = load(gddb_constants.c_addon_main_path + "core/db_prop.gd").new()
prop.set_prop_id(prop_id)
prop.set_prop_type(prop_type)
prop.set_prop_name(prop_name)
m_props.push_back(prop)
# adding blank data to all existing rows
# TODO: find a way to make this better; this is ugly
if(m_data.size() > 0):
var new_data_array = []
var data_idx = 0
var row_idx = 0
while(true):
var data = m_data[data_idx]
row_idx = data.get_row_idx()
if(data.get_prop_id() + 1 == prop_id):
new_data_array.push_back(data)
var new_data = load(gddb_constants.c_addon_main_path + "core/db_data.gd").new()
new_data.set_prop_id(prop_id)
new_data.set_row_idx(row_idx)
if(prop_type == gddb_types.e_prop_type_bool):
new_data.set_data("0")
elif(prop_type == gddb_types.e_prop_type_int):
if(prop.has_autoincrement()):
new_data.set_data(str(m_rows_count+1))
else:
new_data.set_data("0")
elif(prop_type == gddb_types.e_prop_type_float):
new_data.set_data("0.0")
elif(prop_type == gddb_types.e_prop_type_string):
new_data.set_data("")
elif(prop_type == gddb_types.e_prop_type_resource):
new_data.set_data("res://")
elif(prop_type >= gddb_types.e_prop_types_count):
new_data.set_data(str(-1))
new_data_array.push_back(new_data)
else:
new_data_array.push_back(data)
data_idx += 1
if(data_idx >= m_data.size()):
break
m_data.clear()
for idx in range(0, new_data_array.size()):
m_data.push_back(new_data_array[idx])
return prop_id
# adds a property in the table structure as a table type
# returns prop ID
func add_table_prop(prop_name : String, table_name : String) -> int :
# print("GDDBTable::add_table_prop(" + prop_name + ", " + table_name + ")")
var prop_id = generate_new_prop_id()
# print("GDDBDTable::add_prop(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ")")
var prop = load(gddb_constants.c_addon_main_path + "core/db_prop.gd").new()
prop.set_prop_id(prop_id)
prop.set_prop_name(prop_name)
prop.set_prop_custom_type(table_name)
m_props.push_back(prop)
return prop_id
# links custom properties from tables
func link_tables_props() -> void :
for idx in range(0, m_props.size()):
var custom_prop_type = m_props[idx].get_prop_custom_type()
if(!custom_prop_type.empty()):
var table = m_parent_database.get_table_by_name(custom_prop_type)
m_props[idx].set_prop_type(gddb_types.e_prop_types_count + table.get_table_id())
m_props[idx].set_prop_custom_type("")
# edits a property in the table structure
func edit_prop(prop_id : int, prop_type : int, prop_name: String) -> void :
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_id() == prop_id):
m_props[idx].set_prop_type(prop_type)
m_props[idx].set_prop_name(prop_name)
return
print("ERROR: GDDBDTable::edit_prop(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ") - property not found")
# enables or disables autoincrement on a property
func enable_prop_autoincrement(prop_id : int, enable : bool) -> void :
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_id() == prop_id):
m_props[idx].enable_autoincrement(enable)
break
# deletes a property and all the data from the table have the same property
func delete_prop(prop_id : int) -> void :
# print("db_table::delete_prop(" + str(prop_id) + ")")
var prop_found = false
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_id() == prop_id):
# print("Removing prop with id " + str(prop_id))
m_props[idx].free()
m_props.remove(idx)
prop_found = true
break
if(!prop_found):
print("ERROR: GDDBDTable::delete_prop( " + str(prop_id) + " ) - property not found !")
return
# remove the data
# this is very ugly, but I can't erase a subarray from an array in GDscript :(
# backup what needs to be saved only
var tmp_data = []
for idx in range(0, m_data.size()):
if(m_data[idx].get_prop_id() == prop_id):
continue
tmp_data.push_back(m_data[idx])
# clear and restore data
m_data.clear()
for idx in range(0, tmp_data.size()):
m_data.push_back(tmp_data[idx])
# generates a new table id
func generate_new_prop_id() -> int :
if(m_props.size() == 0):
return 0
return m_props[m_props.size()-1].get_prop_id() + 1
# returns the properties count
func get_props_count() -> int :
return m_props.size()
# returns the property at index or null if the index is out of bounds
func get_prop_at(idx : int) -> Object :
if(idx < 0 || idx >= m_props.size()):
print("ERROR: GDDBDTable::get_prop_id( " + str(idx) + " ) - index out of bounds; max properties: " + str(m_props.size()))
return null
return m_props[idx]
# returns a property by id or null if the id is not found
func get_prop_by_id(prop_id : int) -> Object :
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_id() == prop_id):
return m_props[idx]
print("ERROR: GDDBDTable::get_prop_by_id(" + str(prop_id) + ") - property with id not found")
return null
# returns a property by name
func get_prop_by_name(pror_name : String) -> Object :
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_name() == pror_name):
return m_props[idx]
print("ERROR: GDDBDTable::get_prop_by_name(" + str(pror_name) + ") - property with name not found")
return null
# adds a row with blank data
func add_blank_row() -> void :
# print("GDDBTable::add_blank_row()")
var prop_type = gddb_types.e_prop_type_bool
for idx in range(0, m_props.size()):
var data = load(gddb_constants.c_addon_main_path + "core/db_data.gd").new()
data.set_prop_id(m_props[idx].get_prop_id())
data.set_row_idx(m_rows_count)
prop_type = m_props[idx].get_prop_type()
if(prop_type == gddb_types.e_prop_type_bool):
data.set_data("0")
elif(prop_type == gddb_types.e_prop_type_int):
if(m_props[idx].has_autoincrement()):
data.set_data(str(m_rows_count+1))
else:
data.set_data("0")
elif(prop_type == gddb_types.e_prop_type_float):
data.set_data("0.0")
elif(prop_type == gddb_types.e_prop_type_string):
data.set_data("")
elif(prop_type == gddb_types.e_prop_type_resource):
data.set_data("res://")
elif(prop_type >= gddb_types.e_prop_types_count):
data.set_data(str(-1))
m_data.push_back(data)
m_rows_count += 1
# adds a row with data
func add_row(data_array : Array) -> void :
if(data_array.size() != m_props.size()):
print("ERROR: GDDBDTable::add_row( " + str(data_array) + " ) - cannot add row; properties count = " + str(m_props.size()) + " and data size = " + str(data_array.size()))
return
var prop_type = gddb_types.e_prop_type_bool
var the_data = ""
for idx in range(0, m_props.size()):
var data = load(gddb_constants.c_addon_main_path + "core/db_data.gd").new()
# print("adding data: [" + str(m_props[idx].get_prop_id()) + ", " + data_array[idx] + "]")
# print("setting prop id: " + str(m_props[idx].get_prop_id()))
data.set_prop_id(m_props[idx].get_prop_id())
data.set_row_idx(m_rows_count)
# ignore data if the property has autoincrement option
if(m_props[idx].has_autoincrement()):
data.set_data(str(m_rows_count+1))
m_data.push_back(data)
continue
prop_type = m_props[idx].get_prop_type()
the_data = data_array[idx]
if(prop_type == gddb_types.e_prop_type_bool):
data.set_data(str(the_data))
elif(prop_type == gddb_types.e_prop_type_int):
data.set_data(str(the_data))
elif(prop_type == gddb_types.e_prop_type_float):
data.set_data(str(the_data))
elif(prop_type == gddb_types.e_prop_type_string):
data.set_data(gddb_globals.handle_string(the_data))
elif(prop_type == gddb_types.e_prop_type_resource):
data.set_data(the_data)
else:
print("ERROR: data type doesn't exist - " + str(m_props[idx].get_prop_type()))
m_data.push_back(data)
m_rows_count += 1
# removes a row
func remove_row(row_idx : int) -> void :
for idx in range(m_data.size()-1, 0, -1):
if(m_data[idx].get_row_idx() == row_idx):
m_data[idx].remove(idx)
m_rows_count -= 1
# returns the rows count
func get_rows_count() -> int :
return m_rows_count
# edits the data
func edit_data(prop_id : int, row_idx : int, data : String) -> void :
# print("#1: GDDBTable::edit_data( " + str(prop_id) + ", " + str(row_idx) + ", " + data + " )")
for idx in range(0, m_data.size()):
# print("checking ( " + str(m_data[idx].get_row_idx()) + ", " + str(m_data[idx].get_prop_id()) + " )")
if(m_data[idx].get_row_idx() == row_idx && m_data[idx].get_prop_id() == prop_id):
# print("#2: GDDBTable::edit_data( " + str(prop_id) + ", " + str(row_idx) + ", " + data + " )")
m_data[idx].set_data(data)
return
print("ERROR: GDDBDTable::edit_data(" + str(prop_id) + ", " + str(row_idx) + ", " + data + ") - can't find data to edit")
# edits the data
func edit_data_by_prop_name(prop_name : String, row_idx : int, data : String) -> void :
# print("#1: GDDBTable::edit_data( " + str(prop_id) + ", " + str(row_idx) + ", " + data + " )")
var prop_id = get_prop_by_name(prop_name).get_prop_id()
edit_data(prop_id, row_idx, data)
# returns data count
func get_data_size() -> int :
return m_data.size()
# returns all data
func get_all_data() -> Array :
return m_data
# returns the data at index
func get_data_at(idx : int) -> String :
if(idx < 0 || idx >= m_data.size()):
print("ERROR: GDDBDTable::get_data_at( " + str(idx) + ") - max data size: " + str(m_data.size()))
return ""
return m_data[idx].get_data()
# returns a dictionary containing the data at index
func get_dictionary_at(idx : int) -> Dictionary :
var dict = {}
if(idx < 0 || idx >= m_data.size()):
print("ERROR: GDDBDTable::get_data_at( " + str(idx) + ") - max data size: " + str(m_data.size()))
return dict
var prop_idx = idx % m_props.size()
var data_prop_name = m_props[prop_idx].get_prop_name()
dict[data_prop_name] = m_data[idx].get_data()
return dict
# returns the data by a property id and a row index
func get_data(prop_id : int, row_idx : int) -> String :
for idx in range(m_data.size()-1, 0, -1):
if(m_data[idx].get_row_idx() == row_idx && m_data[idx].get_prop_id() == prop_id):
return m_data[idx].get_data()
print("ERROR: GDDBDTable::get_data(" + str(prop_id) + ", " + str(row_idx) + ")")
return ""
# returns a dictionary containing the data by a property id and a row index
func get_dictionary(prop_id : int, row_idx : int) -> Dictionary :
var dict = {}
for idx in range(m_data.size()-1, 0, -1):
if(m_data[idx].get_row_idx() == row_idx && m_data[idx].get_prop_id() == prop_id):
var prop = get_prop_by_id(prop_id)
var data_prop_name = prop.get_prop_name()
dict[data_prop_name] = m_data[idx].get_data()
return dict
print("ERROR: GDDBDTable::get_data(" + str(prop_id) + ", " + str(row_idx) + ")")
return dict
# returns an array of data at row index
func get_data_at_row_idx(row_idx : int) -> Array :
var data = []
for idx in range(0, m_data.size()):
if(m_data[idx].get_row_idx() == row_idx):
data.push_back(m_data[idx])
if(data.size() == -1):
print("ERROR: GDDBDTable::get_data_at_row_idx(" + str(row_idx) + ") - invalid row index")
return data
# returns a dictionary of data at row index
func get_dictionary_at_row_idx(row_idx : int) -> Dictionary :
var dict = {}
var prop_idx = 0
for idx in range(0, m_data.size()):
if(m_data[idx].get_row_idx() == row_idx):
var data_prop_name = m_props[prop_idx].get_prop_name()
dict[data_prop_name] = m_data[idx].get_data()
prop_idx += 1
if(dict.empty()):
print("ERROR: GDDBDTable::get_data_at_row_idx(" + str(row_idx) + ") - invalid row index")
return dict
# returns an array of data filtered by property id
func get_data_by_prop_id(prop_id : int, data_filter : int = gddb_types.e_data_filter_equal) -> Array :
var data = []
if(data_filter < gddb_types.e_data_filter_equal || data_filter >= gddb_types.e_data_filter_gequal):
print("ERROR: cannot process filter " + str(data_filter))
return data
for idx in range(0, m_data.size()):
if(data_filter == gddb_types.e_data_filter_equal):
if(m_data[idx].get_prop_id() == prop_id):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_not_equal):
if(m_data[idx].get_prop_id() != prop_id):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_less):
if(m_data[idx].get_prop_id() < prop_id):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_greater):
if(m_data[idx].get_prop_id() > prop_id):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_lequal):
if(m_data[idx].get_prop_id() <= prop_id):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_gequal):
if(m_data[idx].get_prop_id() >= prop_id):
data.push_back(m_data[idx])
if(data.size() == 0):
print("ERROR: GDDBDTable::get_data_by_prop_id(" + str(prop_id) + ", " + gddb_globals.get_data_filter_name(data_filter) + ") - filtered data not found")
return data
# returns an array of data by property name
func get_data_by_prop_name(prop_name : String) -> Array :
var prop_id = -1
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_name() == prop_name):
prop_id = m_props[idx].get_prop_id()
break
if(prop_id == -1):
print("ERROR: GDDBDTable::get_data_by_prop_name(" + prop_name + ") - property not found")
return []
return get_data_by_prop_id(prop_id)
# returns an array of data filtered by data
# filters "<", ">", "<=" and ">=" are working for integer and float data types
func get_data_by_data(data_value : String, data_filter : int = gddb_types.e_data_filter_equal) -> Array :
var data = []
if(data_filter < gddb_types.e_data_filter_equal || data_filter >= gddb_types.e_data_filter_gequal):
print("ERROR: cannot process filter " + str(data_filter))
return data
for idx in range(0, m_data.size()):
if(data_filter == gddb_types.e_data_filter_equal):
if(m_data[idx].get_data() == data_value):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_not_equal):
if(m_data[idx].get_data() != data_value):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_less):
if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource):
if(m_data[idx].get_data() < data_value):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_greater):
if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource):
if(m_data[idx].get_data() > data_value):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_lequal):
if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource):
if(m_data[idx].get_data() <= data_value):
data.push_back(m_data[idx])
elif(data_filter == gddb_types.e_data_filter_gequal):
if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource):
if(m_data[idx].get_data() >= data_value):
data.push_back(m_data[idx])
if(data.size() == 0):
print("ERROR: GDDBDTable::get_data_by_data(" + data_value + ", " + gddb_globals.get_data_filter_name(data_filter) + ") - filtered data not found")
return data
# returns an array of data by a property name and a data value
# similar to: select * from users where user_id = 1
func get_data_by_prop_name_and_data(prop_name : String, data_value : String) -> Array :
var the_array = []
var prop_idx = -1
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_name() == prop_name):
prop_idx = idx
break
for idx in range(0, m_rows_count):
var row_data = get_data_at_row_idx(idx)
# print("Comparing row: " + row_data[0].get_data() + ", " + row_data[1].get_data() + ", " + row_data[2].get_data() + ", " + row_data[3].get_data() + ", " + row_data[4].get_data())
if(row_data[prop_idx].get_data() == data_value):
var dict = get_dictionary_at_row_idx(idx)
the_array.push_back(row_data)
return the_array
# returns a dictionary of data by a property name and a data value
# similar to: select * from users where user_id = 1
func get_dictionary_by_prop_name_and_data(prop_name : String, data_value : String) -> Array :
var the_array = []
var prop_idx = -1
for idx in range(0, m_props.size()):
if(m_props[idx].get_prop_name() == prop_name):
prop_idx = idx
break
for idx in range(0, m_rows_count):
var row_data = get_data_at_row_idx(idx)
# print("Comparing row: " + row_data[0].get_data() + ", " + row_data[1].get_data() + ", " + row_data[2].get_data() + ", " + row_data[3].get_data() + ", " + row_data[4].get_data())
if(row_data[prop_idx].get_data() == data_value):
var dict = get_dictionary_at_row_idx(idx)
the_array.push_back(get_dictionary_at_row_idx(idx))
return the_array
# clears the table's structure and data
func clear() -> void :
# clear data
clear_data()
# clear properties
for idx in range(0, m_props.size()):
m_props[idx].free()
m_props.clear()
# clears the table's data
func clear_data() -> void :
for idx in range(0, m_data.size()):
m_data[idx].free()
m_data.clear()
m_rows_count = 0
# dumps the table
func dump() -> String :
var dump_text = "Table dump. id: " + str(m_table_id) + ", name: " + m_table_name + ", props_count: " + str(m_props.size()) + ", rows_count: " + str(m_rows_count)
dump_text += "\n------------------------------------------------------------------------------------\nProperties:"
for idx in range(0, m_props.size()):
dump_text += "\n" + m_props[idx].dump()
dump_text += "\n------------------------------------------------------------------------------------\nData:\n"
for idx in range(0, m_rows_count):
var tmp_text = "row_idx: " + "%" + str(gddb_globals.get_digits_count(m_rows_count)) + "d"
dump_text += tmp_text % idx
var row = get_data_at_row_idx(idx)
for jdx in range(0, row.size()):
dump_text += " | " + row[jdx].get_data()
dump_text += "\n"
return dump_text