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); }
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; }
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; }
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); }
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; }
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); }
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); }
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); } }
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; }
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(); } }
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); } }
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; }
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(); }
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"; } }
bool DBOperationDelete::initialize(channel_t sender, uint16_t, DatagramIterator &dgi) { m_sender = sender; m_type = DELETE_OBJECT; m_doid = dgi.read_doid(); return true; }
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); } }
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); } } }
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; } }
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); } }
// 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); } }
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; }
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); } }
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; }
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); } }
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(); }
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); }
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(); }
// 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."; } }
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); }
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); } }