#include "ikarus/objects/entity.h" #include #include #include #include #include #include #include #include #include IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, nullptr, "failed to create entity: {}", IkarusErrorInfo_Database_QueryFailed, project->db->transact([name](auto * db) -> cppbase::Result { TRY(db->execute("INSERT INTO `objects`(`type`) VALUES(?, ?, ?)", IkarusObjectType_Entity)); auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Entity); TRY(db->execute("INSERT INTO `entities`(`id`, `name`) VALUES(?)", id, name)); return cppbase::ok(id); }) ); return project->get_entity(id); } void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_TRYRV_OR_FAIL( , "unable to delete entity: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute("DELETE FROM `objects` WHERE `id` == ?", entity->id) ); entity->project->uncache(entity); } IkarusId ikarus_entity_get_id(IkarusEntity const * entity, IkarusErrorData * error_out) { return ikarus::util::object_get_id(entity, error_out); } IkarusProject * ikarus_entity_get_project(IkarusEntity const * entity, IkarusErrorData * error_out) { return ikarus::util::object_get_project(entity, error_out); } char const * ikarus_entity_get_name(IkarusEntity const * entity, IkarusErrorData * error_out) { return ikarus::util::object_get_name(entity, error_out); } void ikarus_entity_set_name(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) { ikarus::util::object_set_name(entity, name, error_out); } bool ikarus_entity_is_linked_to_blueprint( IkarusEntity const * entity, struct IkarusBlueprint const * blueprint, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, false); IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); IKARUS_FAIL_IF_NULL(blueprint, false); IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, false); IKARUS_VTRYRV_OR_FAIL( auto const ret, false, "unable to check whether entity is linked to blueprint", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( "SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)", entity->id, blueprint->id ) ) return ret; } void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_FAIL_IF_NULL(blueprint, ); IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, ); IKARUS_TRYRV_OR_FAIL( , "unable to link entity to blueprint: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute( "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) VALUES(?, ?) ON CONFLICT(`entity`, `blueprint`) DO NOTHING", entity->id, blueprint->id ) ); } void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_FAIL_IF_NULL(blueprint, ); IKARUS_FAIL_IF_OBJECT_MISSING(blueprint, ); IKARUS_TRYRV_OR_FAIL( , "unable to unlink entity from blueprint: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db ->execute("DELETE FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?", entity->id, blueprint->id) ); } void ikarus_entity_get_linked_blueprints( IkarusEntity const * entity, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_FAIL_IF_NULL(blueprints_out, ); if (blueprints_out_size == 0) { return; } IkarusId ids[blueprints_out_size]; IKARUS_TRYRV_OR_FAIL( , "unable to fetch entity linked blueprints from database: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_many_buffered( "SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = ?", ids, blueprints_out_size, entity->id ) ) for (size_t i = 0; i < blueprints_out_size; ++i) { blueprints_out[i] = entity->project->get_blueprint(ids[i]); } } size_t ikarus_entity_get_linked_blueprint_count(IkarusEntity const * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, 0); IKARUS_FAIL_IF_OBJECT_MISSING(entity, 0); IKARUS_VTRYRV_OR_FAIL( auto const ret, 0, "unable to fetch entity linked blueprint count from database: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id) ); return ret; } bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, false); IKARUS_FAIL_IF_OBJECT_MISSING(entity, false); IKARUS_FAIL_IF_NULL(property, false); IKARUS_FAIL_IF_OBJECT_MISSING(property, false); IKARUS_VTRYRV_OR_FAIL( auto const ret, false, "unable to check whether entity has property: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one( "SELECT EXISTS(SELECT 1 FROM `entity_properties` WHERE `entity` = ? AND `property` = ?)", entity->id, property->id ) ) return ret; } void ikarus_entity_get_properties( IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_FAIL_IF_NULL(properties_out, ); if (properties_out_size == 0) { return; } std::tuple ids_and_types[properties_out_size]; IKARUS_TRYRV_OR_FAIL( , "unable to fetch entity properties from database: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_many_buffered( "SELECT `property`, `type` FROM `properties` WHERE `source` = ?", ids_and_types, properties_out_size, entity->id ) ) for (size_t i = 0; i < properties_out_size; ++i) { auto [id, type] = ids_and_types[i]; properties_out[i] = entity->project->get_property(id, type); } } size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, 0); IKARUS_FAIL_IF_OBJECT_MISSING(entity, 0); IKARUS_VTRYRV_OR_FAIL( size_t const ret, 0, "unable to fetch entity property count from database: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->query_one("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", entity->id) ); return ret; } struct IkarusEntityPropertyValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(entity, nullptr); IKARUS_FAIL_IF_OBJECT_MISSING(entity, nullptr); IKARUS_FAIL_IF_NULL(property, nullptr); IKARUS_FAIL_IF_OBJECT_MISSING(property, nullptr); auto * value = fetch_value_from_db( entity->project, error_out, "SELECT IFNULL((SELECT `value` FROM `values` WHERE `entity` = ? AND `property` = ?), (SELECT `default_value` FROM `properties` WHERE `id` = ?))", entity->id, property->id, property->id ); IKARUS_FAIL_IF_ERROR(nullptr); return new IkarusEntityPropertyValue{ .entity = entity, .property = property, .value = value, }; } void ikarus_entity_set_value( IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusEntityPropertyValue * value, IkarusErrorData * error_out ) { IKARUS_FAIL_IF_NULL(entity, ); IKARUS_FAIL_IF_OBJECT_MISSING(entity, ); IKARUS_FAIL_IF_NULL(property, ); IKARUS_FAIL_IF_OBJECT_MISSING(property, ); IKARUS_FAIL_IF_NULL(value, ); auto value_json_str = boost::json::serialize(value->value->to_json()); IKARUS_TRYRV_OR_FAIL( , "unable to set entity property value: {}", IkarusErrorInfo_Database_QueryFailed, entity->project->db->execute( "INSERT INTO `values`(`entity`, `property`, `value`) VALUES(?, ?, ?) ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = ?", entity->id, property->id, value_json_str, value_json_str ) ); }