Exemplo n.º 1
0
void Client::handle_object_entrance(DatagramIterator &dgi, bool other)
{
    doid_t do_id = dgi.read_doid();
    doid_t parent = dgi.read_doid();
    zone_t zone = dgi.read_zone();
    uint16_t dc_id = dgi.read_uint16();

    // this object is no longer pending
    m_pending_objects.erase(do_id);

    if(m_owned_objects.find(do_id) != m_owned_objects.end() ||
       m_seen_objects.find(do_id) != m_seen_objects.end()) {
        return;
    }

    if(m_visible_objects.find(do_id) == m_visible_objects.end()) {
        VisibleObject obj;
        obj.id = do_id;
        obj.dcc = g_dcf->get_class_by_id(dc_id);
        obj.parent = parent;
        obj.zone = zone;
        m_visible_objects[do_id] = obj;
    }
    m_seen_objects.insert(do_id);

    handle_add_object(do_id, parent, zone, dc_id, dgi, other);
}
Exemplo n.º 2
0
void StateServer::handle_generate(DatagramIterator &dgi, bool has_other)
{
    doid_t do_id = dgi.read_doid();
    doid_t parent_id = dgi.read_doid();
    zone_t zone_id = dgi.read_zone();
    uint16_t dc_id = dgi.read_uint16();

    // Make sure the object id is unique
    if(m_objs.find(do_id) != m_objs.end()) {
        m_log->warning() << "Received generate for already-existing object ID=" << do_id << std::endl;
        return;
    }

    // Make sure the class exists in the file
    const Class *dc_class = g_dcf->get_class_by_id(dc_id);
    if(!dc_class) {
        m_log->error() << "Received create for unknown dclass with class id '" << dc_id << "'\n";
        return;
    }

    // Create the object
    DistributedObject *obj;
    try {
        obj = new DistributedObject(this, do_id, parent_id, zone_id, dc_class, dgi, has_other);
    } catch(const DatagramIteratorEOF&) {
        m_log->error() << "Received truncated generate for "
                       << dc_class->get_name() << "(" << do_id << ")" << std::endl;
        return;
    }
    m_objs[do_id] = obj;
}
Exemplo n.º 3
0
bool DBOperationSet::initialize(channel_t sender, uint16_t msg_type, DatagramIterator &dgi)
{
    m_sender = sender;
    m_type = SET_FIELDS;
    m_doid = dgi.read_doid();

    uint16_t field_count;
    if(msg_type == DBSERVER_OBJECT_SET_FIELDS ||
       msg_type == DBSERVER_OBJECT_DELETE_FIELDS) {
        field_count = dgi.read_uint16();
    } else {
        field_count = 1;
    }

    bool delete_values = false;
    if(msg_type == DBSERVER_OBJECT_DELETE_FIELD ||
       msg_type == DBSERVER_OBJECT_DELETE_FIELDS) {
        delete_values = true;
    }

    if(!populate_set_fields(dgi, field_count, delete_values, false)) {
        on_failure();
        return false;
    }

    return true;
}
Exemplo n.º 4
0
void DBStateServer::handle_get_fields_resp(DatagramIterator& dgi)
{
    uint32_t db_context = dgi.read_uint32();
    if(!is_expected_context(db_context)) {
        return;
    }

    // Get the datagram from the db_context
    DatagramPtr dg = m_context_datagrams[db_context];
    m_context_datagrams.erase(db_context);

    // Check to make sure the datagram is appropriate
    DatagramIterator check_dgi = DatagramIterator(dg);
    uint16_t resp_type = check_dgi.get_msg_type();
    if(resp_type != STATESERVER_OBJECT_GET_FIELDS_RESP) {
        if(resp_type == STATESERVER_OBJECT_GET_FIELD_RESP) {
            m_log->warning() << "Received GetFieldResp, but expecting GetFieldsResp." << std::endl;
        } else if(resp_type == STATESERVER_OBJECT_GET_ALL_RESP) {
            m_log->warning() << "Received GetAllResp, but expecting GetFieldsResp." << std::endl;
        }
        return;
    }

    m_log->trace() << "Received GetFieldResp from database." << std::endl;

    // Add database field payload to response (don't know dclass, so must copy payload).
    if(dgi.read_bool() == true) {
        dgi.read_uint16(); // Discard field count
        dg->add_data(dgi.read_remainder());
    }
    route_datagram(dg);
}
Exemplo n.º 5
0
bool unpack_db_fields(DatagramIterator &dgi, DCClass* dclass,
                      std::unordered_map<DCField*, std::vector<uint8_t> > &required,
                      std::map<DCField*, std::vector<uint8_t> > &ram)
{
	// Unload ram and required fields from database resp
	uint16_t db_field_count = dgi.read_uint16();
	for(uint16_t i = 0; i < db_field_count; ++i)
	{
		uint16_t field_id = dgi.read_uint16();
		DCField *field = dclass->get_field_by_index(field_id);
		if(!field)
		{
			return false;
		}
		if(field->is_ram())
		{
			dgi.unpack_field(field, ram[field]);
		}
		else if(field->is_required())
		{
			dgi.unpack_field(field, required[field]);
		}
		else
		{
			dgi.skip_field(field);
		}
	}

	return true;
}
Exemplo n.º 6
0
void DBStateServer::handle_get_all(channel_t sender, DatagramIterator &dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    if(is_activated_object(r_do_id)) {
        return;
    }

    m_log->trace() << "Received GetAll for inactive object with id " << r_do_id << std::endl;

    // Get context for db query, and remember caller with it
    uint32_t db_context = m_next_context++;

    DatagramPtr resp_dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_ALL_RESP);
    resp_dg->add_uint32(r_context);
    resp_dg->add_doid(r_do_id);
    resp_dg->add_channel(INVALID_CHANNEL); // Location
    m_context_datagrams[db_context] = resp_dg;

    // Cache the do_id --> context in case we get a dbss_activate
    m_inactive_loads[r_do_id].insert(db_context);

    // Send query to database
    DatagramPtr dg = Datagram::create(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_ALL);
    dg->add_uint32(db_context);
    dg->add_doid(r_do_id);
    route_datagram(dg);
}
Exemplo n.º 7
0
bool DBOperationGet::initialize(channel_t sender, uint16_t msg_type, DatagramIterator &dgi)
{
    m_sender = sender;

    m_context = dgi.read_uint32();
    m_doid = dgi.read_doid();

    if(msg_type == DBSERVER_OBJECT_GET_ALL) {
        m_type = GET_OBJECT;
        m_resp_msgtype = DBSERVER_OBJECT_GET_ALL_RESP;
        return true;
    }

    m_type = GET_FIELDS;

    uint16_t field_count = 1;
    if(msg_type == DBSERVER_OBJECT_GET_FIELD) {
        m_resp_msgtype = DBSERVER_OBJECT_GET_FIELD_RESP;
    } else if(msg_type == DBSERVER_OBJECT_GET_FIELDS) {
        field_count = dgi.read_uint16();
        m_resp_msgtype = DBSERVER_OBJECT_GET_FIELDS_RESP;
    }

    return populate_get_fields(dgi, field_count);
}
Exemplo n.º 8
0
void DBStateServer::handle_get_activated(channel_t sender, DatagramIterator& dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    if(m_loading.find(r_do_id) != m_loading.end()) {
        return;
    }

    m_log->trace() << "Received GetActivated for id " << r_do_id << "\n";

    if(m_objs.find(r_do_id) != m_objs.end()) {
        // If object is active return true
        DatagramPtr dg = Datagram::create(sender, r_do_id, DBSS_OBJECT_GET_ACTIVATED_RESP);
        dg->add_uint32(r_context);
        dg->add_doid(r_do_id);
        dg->add_bool(true);
        route_datagram(dg);
    } else {
        // If object isn't active or loading, we can return false
        DatagramPtr dg = Datagram::create(sender, r_do_id, DBSS_OBJECT_GET_ACTIVATED_RESP);
        dg->add_uint32(r_context);
        dg->add_doid(r_do_id);
        dg->add_bool(false);
        route_datagram(dg);
    }
}
Exemplo n.º 9
0
bool DistributedObject::handle_one_update(DatagramIterator &dgi, channel_t sender)
{
    vector<uint8_t> data;
    uint16_t field_id = dgi.read_uint16();
    const Field *field = m_dclass->get_field_by_id(field_id);
    if(!field) {
        m_log->error() << "Received set_field for field: " << field_id
                       << ", not valid for class: " << m_dclass->get_name() << ".\n";
        return false;
    }

    m_log->trace() << "Handling update for '" << field->get_name() << "'.\n";

    dgsize_t field_start = dgi.tell();

    try {
        dgi.unpack_field(field, data);
    } catch(const DatagramIteratorEOF&) {
        m_log->error() << "Received truncated update for " << field->get_name() << ".\n";
        return false;
    }

    const MolecularField *molecular = field->as_molecular();
    if(molecular) {
        dgi.seek(field_start);
        int n = molecular->get_num_fields();
        for(int i = 0; i < n; ++i) {
            vector<uint8_t> field_data;
            const Field *atomic = molecular->get_field(i);
            dgi.unpack_field(atomic, field_data);
            save_field(atomic, field_data);
        }
    } else {
        save_field(field, data);
    }

    unordered_set<channel_t> targets;
    if(field->has_keyword("broadcast")) {
        targets.insert(location_as_channel(m_parent_id, m_zone_id));
    }
    if(field->has_keyword("airecv") && m_ai_channel && m_ai_channel != sender) {
        targets.insert(m_ai_channel);
    }
    if(field->has_keyword("ownrecv") && m_owner_channel && m_owner_channel != sender) {
        targets.insert(m_owner_channel);
    }
    if(targets.size()) { // TODO: Review this for efficiency?
        DatagramPtr dg = Datagram::create(targets, sender, STATESERVER_OBJECT_SET_FIELD);
        dg->add_doid(m_do_id);
        dg->add_uint16(field_id);
        dg->add_data(data);
        route_datagram(dg);
    }
    return true;
}
Exemplo n.º 10
0
void DBStateServer::handle_activate(DatagramIterator &dgi, bool has_other)
{
	uint32_t do_id = dgi.read_uint32();
	uint32_t parent_id = dgi.read_uint32();
	uint32_t zone_id = dgi.read_uint32();

	// Check object is not already active
	if(m_objs.find(do_id) != m_objs.end() || m_loading.find(do_id) != m_loading.end())
	{
		m_log->warning() << "Received activate for already-active object"
		                 << " - id:" << do_id << std::endl;
		return;
	}

	if(!has_other)
	{
		auto load_it = m_inactive_loads.find(do_id);
		if(load_it == m_inactive_loads.end())
		{
			m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id);
		}
		else
		{
			m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id, load_it->second);
		}
		m_loading[do_id]->begin();
	}
	else
	{
		uint16_t dc_id = dgi.read_uint16();

		// Check dclass is valid
		if(dc_id >= g_dcf->get_num_classes())
		{
			m_log->error() << "Received activate_other with unknown dclass"
			               << " - id:" << dc_id << std::endl;
			return;
		}

		DCClass *dclass = g_dcf->get_class(dc_id);
		auto load_it = m_inactive_loads.find(do_id);
		if(load_it == m_inactive_loads.end())
		{
			m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id, dclass, dgi);
		}
		else
		{
			m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id, dclass, dgi, load_it->second);
		}
		m_loading[do_id]->begin();
	}
}
Exemplo n.º 11
0
void DBStateServer::handle_get_field(channel_t sender, DatagramIterator &dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    uint16_t field_id = dgi.read_uint16();
    if(is_activated_object(r_do_id)) {
        return;
    }

    m_log->trace() << "Received GetField for field with id " << field_id
                   << " on inactive object with id " << r_do_id << "\n";

    // Check field is "ram db" or "required"
    const Field* field = g_dcf->get_field_by_id(field_id);
    if(!field || !(field->has_keyword("required") || field->has_keyword("ram"))) {
        DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP);
        dg->add_uint32(r_context);
        dg->add_bool(false);
        route_datagram(dg);
        return;
    }

    if(field->has_keyword("db")) {
        // Get context for db query
        uint32_t db_context = m_next_context++;

        // Prepare reponse datagram
        DatagramPtr dg_resp = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP);
        dg_resp->add_uint32(r_context);
        m_context_datagrams[db_context] = dg_resp;

        // Send query to database
        DatagramPtr dg = Datagram::create(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELD);
        dg->add_uint32(db_context);
        dg->add_doid(r_do_id);
        dg->add_uint16(field_id);
        route_datagram(dg);
    } else if(field->has_default_value()) { // Field is required and not-db
        DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP);
        dg->add_uint32(r_context);
        dg->add_bool(true);
        dg->add_uint16(field_id);
        dg->add_data(field->get_default_value());
        route_datagram(dg);
    } else {
        DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP);
        dg->add_uint32(r_context);
        dg->add_bool(false);
        route_datagram(dg);
    }
}
Exemplo n.º 12
0
bool DBOperationCreate::initialize(channel_t sender, uint16_t, DatagramIterator &dgi)
{
    m_sender = sender;

    m_type = CREATE_OBJECT;
    m_context = dgi.read_uint32();

    uint16_t dclass_id = dgi.read_uint16();
    m_dclass = g_dcf->get_class_by_id(dclass_id);
    if(!m_dclass) {
        m_dbserver->m_log->error() << "Create object request for invalid dclass ID #"
                                   << dclass_id << "\n";
        on_failure();
        return false;
    }

    uint16_t field_count = dgi.read_uint16();
    if(!populate_set_fields(dgi, field_count)) {
        on_failure();
        return false;
    }

    // Make sure that all present fields actually belong to the dclass.
    bool errors = false;
    for(auto it = m_set_fields.begin(); it != m_set_fields.end(); ++it) {
        if(!m_dclass->get_field_by_id(it->first->get_id())) {
            m_dbserver->m_log->warning() << "Attempted to create object "
                                         << m_dclass->get_name()
                                         << " which includes non-belonging field: "
                                         << it->first->get_name() << "\n";
            errors = true;
        }
    }
    if(errors) {
        on_failure();
        return false;
    }

    // Set all non-present fields to defaults (if they exist)
    for(unsigned int i = 0; i < m_dclass->get_num_fields(); ++i) {
        const dclass::Field *field = m_dclass->get_field(i);
        if(field->has_default_value() && field->has_keyword("db")
           && m_set_fields.find(field) == m_set_fields.end()) {
            string val = field->get_default_value();
            m_set_fields[field] = vector<uint8_t>(val.begin(), val.end());
        }
    }

    return true;
}
Exemplo n.º 13
0
bool DBOperation::populate_set_fields(DatagramIterator &dgi, uint16_t field_count,
                                      bool delete_values, bool check_values)
{
    for(uint16_t i = 0; i < field_count; ++i) {
        uint16_t field_id = dgi.read_uint16();
        const Field *field = g_dcf->get_field_by_id(field_id);
        if(!field) {
            m_dbserver->m_log->error() << "Create/modify field request included invalid field #"
                                       << field_id << "\n";
            return false;
        }

        if(field->has_keyword("db")) {
            // Get criteria value
            if(check_values) {
                dgi.unpack_field(field, m_criteria_fields[field]);
            }

            // Get update value
            if(!delete_values) {
                dgi.unpack_field(field, m_set_fields[field]);
            } else if(field->has_default_value()) {
                string val = field->get_default_value();
                m_set_fields[field] = vector<uint8_t>(val.begin(), val.end());
            } else {
                m_set_fields[field]; // Force insertion of blank vector
            }
        } else {
            m_dbserver->m_log->warning() << "Create/modify field request included non-DB field "
                                         << field->get_name() << "\n";

            // Don't read in a non-db field
            if(!delete_values) {
                dgi.skip_field(field);
            }

            // It is not proper to expect a non-db field in criteria.
            if(check_values) {
                return false;
            }
        }
    }

    // A MODIFY_FIELDS request is only valid if we're actually trying to
    // change fields. A CREATE_OBJECT request is valid even without fields.
    return (m_type == CREATE_OBJECT) || !m_set_fields.empty();
}
Exemplo n.º 14
0
void DBStateServer::handle_datagram(DatagramHandle, DatagramIterator &dgi)
{
    channel_t sender = dgi.read_channel();
    uint16_t msgtype = dgi.read_uint16();
    switch(msgtype) {
    case DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS:
        handle_activate(dgi, false);
        break;
    case DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER:
        handle_activate(dgi, true);
        break;
    case DBSS_OBJECT_DELETE_DISK:
        handle_delete_disk(sender, dgi);
        break;
    case STATESERVER_OBJECT_SET_FIELD:
        handle_set_field(dgi);
        break;
    case STATESERVER_OBJECT_SET_FIELDS:
        handle_set_fields(dgi);
        break;
    case STATESERVER_OBJECT_GET_FIELD:
        handle_get_field(sender, dgi);
        break;
    case DBSERVER_OBJECT_GET_FIELD_RESP:
        handle_get_field_resp(dgi);
        break;
    case STATESERVER_OBJECT_GET_FIELDS:
        handle_get_fields(sender, dgi);
        break;
    case DBSERVER_OBJECT_GET_FIELDS_RESP:
        handle_get_fields_resp(dgi);
        break;
    case STATESERVER_OBJECT_GET_ALL:
        handle_get_all(sender, dgi);
        break;
    case DBSERVER_OBJECT_GET_ALL_RESP:
        handle_get_all_resp(dgi);
        break;
    case DBSS_OBJECT_GET_ACTIVATED:
        handle_get_activated(sender, dgi);
        break;
    default:
        m_log->trace() << "Ignoring message of type '" << msgtype << "'.\n";
    }
}
Exemplo n.º 15
0
bool DBOperationDelete::initialize(channel_t sender, uint16_t, DatagramIterator &dgi)
{
    m_sender = sender;

    m_type = DELETE_OBJECT;
    m_doid = dgi.read_doid();

    return true;
}
Exemplo n.º 16
0
void DatabaseServer::handle_datagram(DatagramHandle, DatagramIterator &dgi)
{
    channel_t sender = dgi.read_channel();
    uint16_t msg_type = dgi.read_uint16();

    DBOperation *op;

    switch(msg_type) {
    case DBSERVER_CREATE_OBJECT: {
        op = new DBOperationCreate(this);
    }
    break;
    case DBSERVER_OBJECT_DELETE: {
        op = new DBOperationDelete(this);
    }
    break;
    case DBSERVER_OBJECT_GET_ALL:
    case DBSERVER_OBJECT_GET_FIELD:
    case DBSERVER_OBJECT_GET_FIELDS: {
        op = new DBOperationGet(this);
    }
    break;
    case DBSERVER_OBJECT_SET_FIELD:
    case DBSERVER_OBJECT_SET_FIELDS:
    case DBSERVER_OBJECT_DELETE_FIELD:
    case DBSERVER_OBJECT_DELETE_FIELDS: {
        op = new DBOperationSet(this);
    }
    break;
    case DBSERVER_OBJECT_SET_FIELD_IF_EMPTY:
    case DBSERVER_OBJECT_SET_FIELD_IF_EQUALS:
    case DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS: {
        op = new DBOperationUpdate(this);
    }
    break;
    default:
        m_log->error() << "Recieved unknown MsgType: " << msg_type << endl;
        return;
    };

    if(op->initialize(sender, msg_type, dgi)) {
        handle_operation(op);
    }
}
Exemplo n.º 17
0
void DBStateServer::handle_activate(DatagramIterator &dgi, bool has_other)
{
    doid_t do_id = dgi.read_doid();
    doid_t parent_id = dgi.read_doid();
    zone_t zone_id = dgi.read_zone();

    // Check object is not already active
    if(m_objs.find(do_id) != m_objs.end() || m_loading.find(do_id) != m_loading.end()) {
        m_log->warning() << "Received activate for already-active object with id " << do_id << "\n";
        return;
    }

    if(!has_other) {
        auto load_it = m_inactive_loads.find(do_id);
        if(load_it == m_inactive_loads.end()) {
            m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id);
            m_loading[do_id]->begin();
        } else {
            m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id, load_it->second);
        }
    } else {
        uint16_t dc_id = dgi.read_uint16();

        // Check id is a valid type id
        if(dc_id >= g_dcf->get_num_types()) {
            m_log->error() << "Received activate_other with unknown dclass"
                           " with id " << dc_id << "\n";
            return;
        }

        const Class *dcc = g_dcf->get_class_by_id(dc_id);
        if(!dcc) {
            m_log->error() << "Tried to activate_other with non-class distributed_type '"
                           << g_dcf->get_class_by_id(dc_id)->get_name() << "'\n";
        }
        auto load_it = m_inactive_loads.find(do_id);
        if(load_it == m_inactive_loads.end()) {
            m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id, dcc, dgi);
            m_loading[do_id]->begin();
        } else {
            m_loading[do_id] = new LoadingObject(this, do_id, parent_id, zone_id, dcc, dgi, load_it->second);
        }
    }
}
Exemplo n.º 18
0
void StateServer::handle_datagram(DatagramHandle, DatagramIterator &dgi)
{
    channel_t sender = dgi.read_channel();
    uint16_t msgtype = dgi.read_uint16();
    switch(msgtype) {
    case STATESERVER_CREATE_OBJECT_WITH_REQUIRED: {
        handle_generate(dgi, false);
        break;
    }
    case STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER: {
        handle_generate(dgi, true);
        break;
    }
    case STATESERVER_DELETE_AI_OBJECTS: {
        handle_delete_ai(dgi, sender);
        break;
    }
    default:
        m_log->warning() << "Received unknown message: msgtype=" << msgtype << std::endl;
    }
}
Exemplo n.º 19
0
void DBStateServer::handle_set_fields(DatagramIterator &dgi)
{
    doid_t do_id = dgi.read_doid();
    if(m_loading.find(do_id) != m_loading.end()) {
        // Ignore this message for now, it'll be bounced back to us
        // from the loading object if it succeeds or fails at loading.
        return;
    }

    uint16_t field_count = dgi.read_uint16();

    FieldValues db_fields;
    for(uint16_t i = 0; i < field_count; ++i) {
        uint16_t field_id = dgi.read_uint16();
        const Field* field = g_dcf->get_field_by_id(field_id);
        if(!field) {
            m_log->warning() << "Received invalid field with id " << field_id << " in SetFields.\n";
            return;
        }
        if(field->has_keyword("db")) {
            dgi.unpack_field(field, db_fields[field]);
        } else {
            dgi.skip_field(field);
        }
    }

    if(db_fields.size() > 0) {
        m_log->trace() << "Forwarding SetFields on object with id " << do_id << " to database.\n";

        DatagramPtr dg = Datagram::create(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELDS);
        dg->add_doid(do_id);
        dg->add_uint16(db_fields.size());
        for(const auto& it : db_fields) {
            dg->add_uint16(it.first->get_id());
            dg->add_data(it.second);
        }
        route_datagram(dg);
    }
}
Exemplo n.º 20
0
// build_interest will build an interest from a datagram. It is expected that the datagram
// iterator is positioned such that next item to be read is the interest_id.
void Client::build_interest(DatagramIterator &dgi, bool multiple, Interest &out)
{
    uint16_t interest_id = dgi.read_uint16();
    doid_t parent = dgi.read_doid();

    out.id = interest_id;
    out.parent = parent;

    uint16_t count = 1;
    if(multiple) {
        count = dgi.read_uint16();
    }

    // TODO: We shouldn't have to do this ourselves, figure out where else we're doing
    //       something wrong.
    out.zones.rehash((unsigned int)(ceil(count / out.zones.max_load_factor())));

    for(uint16_t x{}; x < count; ++x) {
        zone_t zone = dgi.read_zone();
        out.zones.insert(out.zones.end(), zone);
    }
}
Exemplo n.º 21
0
bool unpack_db_fields(DatagramIterator &dgi, const Class* dc_class,
                      UnorderedFieldValues &required, FieldValues &ram)
{
    // Unload ram and required fields from database resp
    uint16_t db_field_count = dgi.read_uint16();
    for(uint16_t i = 0; i < db_field_count; ++i) {
        uint16_t field_id = dgi.read_uint16();
        const Field *field = dc_class->get_field_by_id(field_id);
        if(!field) {
            return false;
        }
        if(field->has_keyword("required")) {
            dgi.unpack_field(field, required[field]);
        } else if(field->has_keyword("ram")) {
            dgi.unpack_field(field, ram[field]);
        } else {
            dgi.skip_field(field);
        }
    }

    return true;
}
Exemplo n.º 22
0
void DBStateServer::handle_set_field(DatagramIterator &dgi)
{
    doid_t do_id = dgi.read_doid();
    if(m_loading.find(do_id) != m_loading.end()) {
        // Ignore this message for now, it'll be bounced back to us
        // from the loading object if it succeeds or fails at loading.
        return;
    }

    uint16_t field_id = dgi.read_uint16();

    const Field* field = g_dcf->get_field_by_id(field_id);
    if(field && field->has_keyword("db")) {
        m_log->trace() << "Forwarding SetField for field \"" << field->get_name()
                       << "\" on object with id " << do_id << " to database.\n";

        DatagramPtr dg = Datagram::create(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELD);
        dg->add_doid(do_id);
        dg->add_uint16(field_id);
        dg->add_data(dgi.read_remainder());
        route_datagram(dg);
    }
}
Exemplo n.º 23
0
bool DBOperationUpdate::initialize(channel_t sender, uint16_t msg_type, DatagramIterator &dgi)
{
    m_sender = sender;
    m_type = UPDATE_FIELDS;
    m_context = dgi.read_uint32();
    m_doid = dgi.read_doid();

    uint16_t field_count;
    if(msg_type == DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS) {
        field_count = dgi.read_uint16();
    } else {
        field_count = 1;
    }

    bool check_values = true;
    if(msg_type == DBSERVER_OBJECT_SET_FIELD_IF_EQUALS) {
        m_resp_msgtype = DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP;
    } else if(msg_type == DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS) {
        m_resp_msgtype = DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP;
    } else if(msg_type == DBSERVER_OBJECT_SET_FIELD_IF_EMPTY) {
        m_resp_msgtype = DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP;
        check_values = false;
    }

    if(!populate_set_fields(dgi, field_count, false, check_values)) {
        on_failure();
        return false;
    }

    if(msg_type == DBSERVER_OBJECT_SET_FIELD_IF_EMPTY) {
        // We must satisfy the IF_EMPTY constraint here, because we don't
        // handle it in populate_set_fields.
        m_criteria_fields[m_set_fields.begin()->first]; // Force insertion of blank vector
    }

    return true;
}
Exemplo n.º 24
0
void StateServer::handle_delete_ai(DatagramIterator& dgi, channel_t sender)
{
    channel_t ai_channel = dgi.read_channel();
    std::set <channel_t> targets;
    for(auto it = m_objs.begin(); it != m_objs.end(); ++it) {
        if(it->second && it->second->m_ai_channel == ai_channel && it->second->m_ai_explicitly_set) {
            targets.insert(it->second->m_do_id);
        }
    }

    if(targets.size()) {
        DatagramPtr dg = Datagram::create(targets, sender, STATESERVER_DELETE_AI_OBJECTS);
        dg->add_channel(ai_channel);
        route_datagram(dg);
    }
}
Exemplo n.º 25
0
DistributedObject::DistributedObject(StateServer *stateserver, doid_t do_id, doid_t parent_id,
                                     zone_t zone_id, const Class *dclass, DatagramIterator &dgi,
                                     bool has_other) :
    m_stateserver(stateserver), m_do_id(do_id), m_parent_id(INVALID_DO_ID), m_zone_id(0),
    m_dclass(dclass), m_ai_channel(INVALID_CHANNEL), m_owner_channel(INVALID_CHANNEL),
    m_ai_explicitly_set(false), m_parent_synchronized(false), m_next_context(0)
{
    stringstream name;
    name << dclass->get_name() << "(" << do_id << ")";
    m_log = new LogCategory("object", name.str());
    set_con_name(name.str());

    for(unsigned int i = 0; i < m_dclass->get_num_fields(); ++i) {
        const Field *field = m_dclass->get_field(i);
        if(field->has_keyword("required") && !field->as_molecular()) {
            dgi.unpack_field(field, m_required_fields[field]);
        }
    }

    if(has_other) {
        uint16_t count = dgi.read_uint16();
        for(int i = 0; i < count; ++i) {
            uint16_t field_id = dgi.read_uint16();
            const Field *field = m_dclass->get_field_by_id(field_id);
            if(!field) {
                m_log->error() << "Received unknown field with ID " << field_id 
                               << " within an OTHER section.\n";
                break;
            }

            if(field->has_keyword("ram")) {
                dgi.unpack_field(field, m_ram_fields[field]);
            } else {
                m_log->error() << "Received non-RAM field " << field->get_name()
                               << " within an OTHER section.\n";
                dgi.skip_field(field);
            }
        }
    }

    subscribe_channel(do_id);

    m_log->debug() << "Object created..." << endl;

    dgi.seek_payload(); // Seek back to front of payload, to read sender
    handle_location_change(parent_id, zone_id, dgi.read_channel());
    wake_children();
}
Exemplo n.º 26
0
void DBStateServer::handle_delete_disk(channel_t sender, DatagramIterator& dgi)
{
    doid_t do_id = dgi.read_doid();
    if(m_loading.find(do_id) != m_loading.end()) {
        // Ignore this message for now, it'll be bounced back to us
        // from the loading object if it succeeds or fails at loading.
        return;
    }

    // If object exists broadcast the delete message
    auto obj_keyval = m_objs.find(do_id);
    if(obj_keyval != m_objs.end()) {
        DistributedObject* obj = obj_keyval->second;
        std::unordered_set<channel_t> targets;

        // Add location to broadcast
        if(obj->get_location()) {
            targets.insert(obj->get_location());
        }

        // Add AI to broadcast
        if(obj->get_ai()) {
            targets.insert(obj->get_ai());
        }

        // Add owner to broadcast
        if(obj->get_owner()) {
            targets.insert(obj->get_owner());
        }

        // Build and send datagram
        DatagramPtr dg = Datagram::create(targets, sender, DBSS_OBJECT_DELETE_DISK);
        dg->add_doid(do_id);
        route_datagram(dg);
    }

    // Send delete to database
    DatagramPtr dg = Datagram::create(m_db_channel, do_id, DBSERVER_OBJECT_DELETE);
    dg->add_doid(do_id);
    route_datagram(dg);

}
Exemplo n.º 27
0
bool DBOperation::populate_get_fields(DatagramIterator &dgi, uint16_t field_count)
{
    for(uint16_t i = 0; i < field_count; ++i) {
        // Read the field from the datagram
        uint16_t field_id = dgi.read_uint16();
        const Field *field = g_dcf->get_field_by_id(field_id);
        if(!field) {
            m_dbserver->m_log->error() << "Get field request included invalid field #"
                                       << field_id << "\n";
            return false;
        }

        // Add the field to the fields we want to get from the database
        if(field->has_keyword("db"))
            m_get_fields.insert(field);
        else
            m_dbserver->m_log->error() << "Get field request included non-DB field "
                                       << field->get_name() << "\n";
    }

    // A GET_FIELDS request is only really valid if we're actually requesting fields.
    return !m_get_fields.empty();
}
Exemplo n.º 28
0
// handle_datagram is the handler for datagrams received from the Astron cluster
void Client::handle_datagram(DatagramHandle in_dg, DatagramIterator &dgi)
{
    lock_guard<recursive_mutex> lock(m_client_lock);
    if(is_terminated()) {
        return;
    }

    channel_t sender = dgi.read_channel();
    if(sender == m_channel) {
        return;    // ignore messages from ourselves
    }

    uint16_t msgtype = dgi.read_uint16();
    switch(msgtype) {
    case CLIENTAGENT_EJECT: {
        uint16_t reason = dgi.read_uint16();
        string error_string = dgi.read_string();
        send_disconnect(reason, error_string);
        return;
    }
    break;
    case CLIENTAGENT_DROP: {
        handle_drop();
        return;
    }
    break;
    case CLIENTAGENT_SET_STATE: {
        m_state = (ClientState)dgi.read_uint16();
    }
    break;
    case CLIENTAGENT_ADD_INTEREST: {
        uint32_t context = m_next_context++;

        Interest i;
        build_interest(dgi, false, i);
        handle_add_interest(i, context);
        add_interest(i, context, sender);
    }
    break;
    case CLIENTAGENT_ADD_INTEREST_MULTIPLE: {
        uint32_t context = m_next_context++;

        Interest i;
        build_interest(dgi, true, i);
        handle_add_interest(i, context);
        add_interest(i, context, sender);
    }
    break;
    case CLIENTAGENT_REMOVE_INTEREST: {
        uint32_t context = m_next_context++;

        uint16_t id = dgi.read_uint16();
        Interest &i = m_interests[id];
        handle_remove_interest(id, context);
        remove_interest(i, context, sender);
    }
    break;
    case CLIENTAGENT_SET_CLIENT_ID: {
        if(m_channel != m_allocated_channel) {
            unsubscribe_channel(m_channel);
        }

        m_channel = dgi.read_channel();
        subscribe_channel(m_channel);
    }
    break;
    case CLIENTAGENT_SEND_DATAGRAM: {
        DatagramPtr forward = Datagram::create();
        forward->add_data(dgi.read_string());
        forward_datagram(forward);
    }
    break;
    case CLIENTAGENT_OPEN_CHANNEL: {
        subscribe_channel(dgi.read_channel());
    }
    break;
    case CLIENTAGENT_CLOSE_CHANNEL: {
        unsubscribe_channel(dgi.read_channel());
    }
    break;
    case CLIENTAGENT_ADD_POST_REMOVE: {
        add_post_remove(m_allocated_channel, dgi.read_datagram());
    }
    break;
    case CLIENTAGENT_CLEAR_POST_REMOVES: {
        clear_post_removes(m_allocated_channel);
    }
    break;
    case CLIENTAGENT_DECLARE_OBJECT: {
        doid_t do_id = dgi.read_doid();
        uint16_t dc_id = dgi.read_uint16();

        if(m_declared_objects.find(do_id) != m_declared_objects.end()) {
            m_log->warning() << "Received object declaration for previously declared object "
                             << do_id << ".\n";
            return;
        }

        DeclaredObject obj;
        obj.id = do_id;
        obj.dcc = g_dcf->get_class_by_id(dc_id);
        m_declared_objects[do_id] = obj;
    }
    break;
    case CLIENTAGENT_UNDECLARE_OBJECT: {
        doid_t do_id = dgi.read_doid();

        if(m_declared_objects.find(do_id) == m_declared_objects.end()) {
            m_log->warning() << "Received undeclare object for unknown object "
                             << do_id << ".\n";
            return;
        }

        m_declared_objects.erase(do_id);
    }
    break;
    case CLIENTAGENT_SET_FIELDS_SENDABLE: {
        doid_t do_id = dgi.read_doid();
        uint16_t field_count = dgi.read_uint16();

        unordered_set<uint16_t> fields;
        for(uint16_t i{}; i < field_count; ++i) {
            fields.insert(dgi.read_uint16());
        }
        m_fields_sendable[do_id] = fields;
    }
    break;
    case CLIENTAGENT_ADD_SESSION_OBJECT: {
        doid_t do_id = dgi.read_doid();
        if(m_session_objects.find(do_id) != m_session_objects.end()) {
            m_log->warning() << "Received add session object for existing session object "
                             << do_id << ".\n";
            return;
        }

        m_log->debug() << "Added session object with id " << do_id << ".\n";

        m_session_objects.insert(do_id);
    }
    break;
    case CLIENTAGENT_REMOVE_SESSION_OBJECT: {
        doid_t do_id = dgi.read_doid();

        if(m_session_objects.find(do_id) == m_session_objects.end()) {
            m_log->warning() << "Received remove session object for non-session object "
                             << do_id << ".\n";
            return;
        }

        m_log->debug() << "Removed session object with id " << do_id << ".\n";

        m_session_objects.erase(do_id);
    }
    break;
    case CLIENTAGENT_GET_NETWORK_ADDRESS: {
        DatagramPtr resp = Datagram::create(sender, m_channel, CLIENTAGENT_GET_NETWORK_ADDRESS_RESP);
        resp->add_uint32(dgi.read_uint32()); // Context
        resp->add_string(get_remote_address());
        resp->add_uint16(get_remote_port());
        resp->add_string(get_local_address());
        resp->add_uint16(get_local_port());
        route_datagram(resp);
    }
    break;
    case STATESERVER_OBJECT_SET_FIELD: {
        doid_t do_id = dgi.read_doid();
        if(!lookup_object(do_id)) {
            if(try_queue_pending(do_id, in_dg)) {
                return;
            }
            m_log->warning() << "Received server-side field update for unknown object "
                             << do_id << ".\n";
            return;
        }
        if(sender != m_channel) {
            uint16_t field_id = dgi.read_uint16();
            handle_set_field(do_id, field_id, dgi);
        }
    }
    break;
    case STATESERVER_OBJECT_SET_FIELDS: {
        doid_t do_id = dgi.read_doid();
        if(!lookup_object(do_id)) {
            if(try_queue_pending(do_id, in_dg)) {
                return;
            }
            m_log->warning() << "Received server-side multi-field update for unknown object "
                             << do_id << ".\n";
            return;
        }
        if(sender != m_channel) {
            uint16_t num_fields = dgi.read_uint16();
            handle_set_fields(do_id, num_fields, dgi);
        }
    }
    break;
    case STATESERVER_OBJECT_DELETE_RAM: {
        doid_t do_id = dgi.read_doid();

        m_log->trace() << "Received DeleteRam for object with id " << do_id << "\n.";

        if(!lookup_object(do_id)) {
            if(try_queue_pending(do_id, in_dg)) {
                return;
            }
            m_log->warning() << "Received server-side object delete for unknown object "
                             << do_id << ".\n";
            return;
        }

        if(m_session_objects.find(do_id) != m_session_objects.end()) {
            // We have to erase the object from our session_objects here, because
            // the object has already been deleted and we don't want it to be deleted
            // again in the client's destructor.
            m_session_objects.erase(do_id);

            stringstream ss;
            ss << "The session object with id " << do_id << " has been unexpectedly deleted.";
            send_disconnect(CLIENT_DISCONNECT_SESSION_OBJECT_DELETED, ss.str());
            return;
        }

        if(m_seen_objects.find(do_id) != m_seen_objects.end()) {
            handle_remove_object(do_id);
            m_seen_objects.erase(do_id);
        }

        if(m_owned_objects.find(do_id) != m_owned_objects.end()) {
            handle_remove_ownership(do_id);
            m_owned_objects.erase(do_id);
        }

        m_historical_objects.insert(do_id);
        m_visible_objects.erase(do_id);
    }
    break;
    case STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER:
    case STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED: {
        doid_t do_id = dgi.read_doid();
        doid_t parent = dgi.read_doid();
        zone_t zone = dgi.read_zone();
        uint16_t dc_id = dgi.read_uint16();
        m_owned_objects.insert(do_id);

        if(m_visible_objects.find(do_id) == m_visible_objects.end()) {
            VisibleObject obj;
            obj.id = do_id;
            obj.parent = parent;
            obj.zone = zone;
            obj.dcc = g_dcf->get_class_by_id(dc_id);
            m_visible_objects[do_id] = obj;
        }

        bool with_other = (msgtype == STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER);
        handle_add_ownership(do_id, parent, zone, dc_id, dgi, with_other);
    }
    break;
    case STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED:
    case STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER: {
        doid_t do_id = dgi.read_doid();
        doid_t parent = dgi.read_doid();
        zone_t zone = dgi.read_zone();
        for(auto& it : m_pending_interests) {
            InterestOperation *interest_operation = it.second;
            if(interest_operation->m_parent == parent &&
               interest_operation->m_zones.find(zone) != interest_operation->m_zones.end()) {

                interest_operation->queue_datagram(in_dg);

                // Add the DoId to m_pending_objects, because while it's not an object
                // from opening the interest, we should begin queueing messages for it
                m_pending_objects.emplace(do_id, it.first);

                return;
            }
        }

        // Object entrance doesn't pertain to any pending iop,
        // so seek back to where we started and handle it normally
        dgi.seek_payload();
        dgi.skip(sizeof(channel_t) + sizeof(uint16_t)); // sender + msgtype

        bool with_other = (msgtype == STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER);
        handle_object_entrance(dgi, with_other);
    }
    break;
    case STATESERVER_OBJECT_ENTER_INTEREST_WITH_REQUIRED:
    case STATESERVER_OBJECT_ENTER_INTEREST_WITH_REQUIRED_OTHER: {
        uint32_t request_context = dgi.read_uint32();
        auto it = m_pending_interests.find(request_context);
        if(it == m_pending_interests.end()) {
            m_log->warning() << "Received object entrance into interest with unknown context "
                             << request_context << ".\n";
            return;
        }

        m_pending_objects.emplace(dgi.read_doid(), request_context);
        it->second->queue_expected(in_dg);
        if(it->second->is_ready()) {
            it->second->finish();
        }
        return;
    }
    break;
    case STATESERVER_OBJECT_GET_ZONES_COUNT_RESP: {
        uint32_t context = dgi.read_uint32();
        // using doid_t because <max_objects_in_zones> == <max_total_objects>
        doid_t count = dgi.read_doid();

        auto it = m_pending_interests.find(context);
        if(it == m_pending_interests.end()) {
            m_log->error() << "Received GET_ZONES_COUNT_RESP for unknown context "
                           << context << ".\n";
            return;
        }

        it->second->set_expected(count);
        if(it->second->is_ready()) {
            it->second->finish();
        }
    }
    break;
    case STATESERVER_OBJECT_CHANGING_LOCATION: {
        doid_t do_id = dgi.read_doid();
        if(try_queue_pending(do_id, in_dg)) {
            // We received a generate for this object, and the generate is sitting in a pending iop
            // we'll just store this dg under the m_pending_datagrams queue on the iop
            return;
        }

        doid_t n_parent = dgi.read_doid();
        zone_t n_zone = dgi.read_zone();

        bool disable = true;
        for(const auto& it : m_interests) {
            const Interest& i = it.second;
            for(const auto& it2 : i.zones) {
                if(it2 == n_zone) {
                    disable = false;
                    break;
                }
            }
        }

        if(m_visible_objects.find(do_id) != m_visible_objects.end()) {
            m_visible_objects[do_id].parent = n_parent;
            m_visible_objects[do_id].zone = n_zone;
        } else {
            // We don't actually *see* this object, we're receiving this
            // message as a fluke.
            return;
        }

        if(disable && m_owned_objects.find(do_id) == m_owned_objects.end()) {
            if(m_session_objects.find(do_id) != m_session_objects.end()) {
                stringstream ss;
                ss << "The session object with id " << do_id
                   << " has unexpectedly left interest.";
                send_disconnect(CLIENT_DISCONNECT_SESSION_OBJECT_DELETED, ss.str());
                return;
            }

            handle_remove_object(do_id);
            m_seen_objects.erase(do_id);
            m_historical_objects.insert(do_id);
            m_visible_objects.erase(do_id);
        } else {
            handle_change_location(do_id, n_parent, n_zone);
        }
    }
    break;
    case STATESERVER_OBJECT_CHANGING_OWNER: {
        doid_t do_id = dgi.read_doid();
        channel_t n_owner = dgi.read_channel();
        dgi.skip(sizeof(channel_t)); // don't care about the old owner

        if(n_owner == m_channel) {
            // We should already own this object, nothing changes and we
            // might get another enter_owner message.
            return;
        }

        if(m_owned_objects.find(do_id) == m_owned_objects.end()) {
            m_log->error() << "Received ChangingOwner for unowned object with id "
                           << do_id << ".\n";
            return;
        }

        if(m_seen_objects.find(do_id) == m_seen_objects.end()) {
            if(m_session_objects.find(do_id) != m_session_objects.end()) {
                stringstream ss;
                ss << "The session object with id " << do_id
                   << " has unexpectedly left ownership.";
                send_disconnect(CLIENT_DISCONNECT_SESSION_OBJECT_DELETED, ss.str());
                return;
            }

            handle_remove_ownership(do_id);
            m_owned_objects.erase(do_id);
            m_historical_objects.insert(do_id);
            m_visible_objects.erase(do_id);
        }
    }
    break;
    default:
        m_log->error() << "Recv'd unknown server msgtype " << msgtype << "\n.";
    }
}
Exemplo n.º 29
0
void DBStateServer::handle_get_all_resp(DatagramIterator& dgi)
{
    uint32_t db_context = dgi.read_uint32();
    if(!is_expected_context(db_context)) {
        return;
    }

    // Get the datagram from the db_context
    DatagramPtr dg = m_context_datagrams[db_context];
    m_context_datagrams.erase(db_context);

    // Check to make sure the datagram is appropriate
    DatagramIterator check_dgi = DatagramIterator(dg);
    uint16_t resp_type = check_dgi.get_msg_type();
    if(resp_type != STATESERVER_OBJECT_GET_ALL_RESP) {
        if(resp_type == STATESERVER_OBJECT_GET_FIELD_RESP) {
            m_log->warning() << "Received GetFieldResp, but expecting GetAllResp." << std::endl;
        } else if(resp_type == STATESERVER_OBJECT_GET_FIELDS_RESP) {
            m_log->warning() << "Received GetFieldsResp, but expecting GetAllResp." << std::endl;
        }
        return;
    }

    // Get do_id from datagram
    check_dgi.seek_payload();
    check_dgi.skip(sizeof(channel_t) + sizeof(doid_t)); // skip over sender and context to do_id;
    doid_t do_id = check_dgi.read_doid();

    // Remove cached loading operation
    if(m_inactive_loads[do_id].size() > 1) {
        m_inactive_loads[do_id].erase(db_context);
    } else {
        m_inactive_loads.erase(do_id);
    }

    m_log->trace() << "Received GetAllResp from database." << std::endl;

    // If object not found, just cleanup the context map
    if(dgi.read_bool() != true) {
        return; // Object not found
    }

    // Read object class
    uint16_t dc_id = dgi.read_uint16();
    if(!dc_id) {
        m_log->error() << "Received object from database with unknown dclass"
                       << " - id:" << dc_id << std::endl;
        return;
    }
    const Class* r_class = g_dcf->get_class_by_id(dc_id);

    // Get fields from database
    UnorderedFieldValues required_fields;
    FieldValues ram_fields;
    if(!unpack_db_fields(dgi, r_class, required_fields, ram_fields)) {
        m_log->error() << "Error while unpacking fields from database." << std::endl;
        return;
    }

    // Add class to response
    dg->add_uint16(r_class->get_id());

    // Add required fields to datagram
    int dcc_field_count = r_class->get_num_fields();
    for(int i = 0; i < dcc_field_count; ++i) {
        const Field *field = r_class->get_field(i);
        if(!field->as_molecular() && field->has_keyword("required")) {
            auto req_it = required_fields.find(field);
            if(req_it != required_fields.end()) {
                dg->add_data(req_it->second);
            } else {
                dg->add_data(field->get_default_value());
            }
        }
    }

    // Add ram fields to datagram
    dg->add_uint16(ram_fields.size());
    for(const auto& it : ram_fields) {
        dg->add_uint16(it.first->get_id());
        dg->add_data(it.second);
    }

    // Send response back to caller
    route_datagram(dg);
}
Exemplo n.º 30
0
void DBStateServer::handle_get_fields(channel_t sender, DatagramIterator &dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    uint16_t field_count = dgi.read_uint16();
    if(is_activated_object(r_do_id)) {
        return;
    }

    m_log->trace() << "Received GetFields for inactive object with id " << r_do_id << std::endl;

    // Read requested fields from datagram
    std::vector<const Field*> db_fields; // Ram|required db fields in request
    std::vector<const Field*> ram_fields; // Ram|required but not-db fields in request
    for(uint16_t i = 0; i < field_count; ++i) {
        uint16_t field_id = dgi.read_uint16();
        const Field* field = g_dcf->get_field_by_id(field_id);
        if(!field) {
            DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP);
            dg->add_uint32(r_context);
            dg->add_uint8(false);
            route_datagram(dg);
        } else if(field->has_keyword("ram") || field->has_keyword("required")) {
            if(field->has_keyword("db")) {
                db_fields.push_back(field);
            } else {
                ram_fields.push_back(field);
            }
        }
    }

    if(db_fields.size()) {
        // Get context for db query
        uint32_t db_context = m_next_context++;

        // Prepare reponse datagram
        if(m_context_datagrams.find(db_context) == m_context_datagrams.end()) {
            m_context_datagrams[db_context] = Datagram::create(sender, r_do_id,
                                              STATESERVER_OBJECT_GET_FIELDS_RESP);
        }
        m_context_datagrams[db_context]->add_uint32(r_context);
        m_context_datagrams[db_context]->add_bool(true);
        m_context_datagrams[db_context]->add_uint16(ram_fields.size() + db_fields.size());
        for(const auto& it : ram_fields) {
            m_context_datagrams[db_context]->add_uint16(it->get_id());
            m_context_datagrams[db_context]->add_data(it->get_default_value());
        }

        // Send query to database
        DatagramPtr dg = Datagram::create(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELDS);
        dg->add_uint32(db_context);
        dg->add_doid(r_do_id);
        dg->add_uint16(db_fields.size());
        for(const auto& it : db_fields) {
            dg->add_uint16(it->get_id());
        }
        route_datagram(dg);
    } else { // If no database fields exist
        DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP);
        dg->add_uint32(r_context);
        dg->add_bool(true);
        dg->add_uint16(ram_fields.size());
        for(const auto& it : ram_fields) {
            dg->add_uint16(it->get_id());
            dg->add_data(it->get_default_value());
        }
        route_datagram(dg);
    }
}