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); }
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); } }
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 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 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); } }
// handle_datagram is the handler for datagrams received from the Astron cluster void Client::handle_datagram(DatagramHandle, DatagramIterator &dgi) { std::lock_guard<std::recursive_mutex> lock(m_client_lock); channel_t sender = dgi.read_channel(); uint16_t msgtype = dgi.read_uint16(); switch(msgtype) { case CLIENTAGENT_EJECT: { uint16_t reason = dgi.read_uint16(); std::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(dgi.read_datagram()); } break; case CLIENTAGENT_CLEAR_POST_REMOVES: { clear_post_removes(); } 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(); std::unordered_set<uint16_t> fields; for(unsigned int i = 0; 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 STATESERVER_OBJECT_SET_FIELD: { doid_t do_id = dgi.read_doid(); if(!lookup_object(do_id)) { 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)) { 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)) { 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); std::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; } handle_add_ownership(do_id, parent, zone, dc_id, dgi, msgtype == STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_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(); uint16_t dc_id = dgi.read_uint16(); 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, msgtype == STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER); // TODO: This is a tad inefficient as it checks every pending interest. // In practice, there shouldn't be many add-interest operations active // at once, however. std::list<uint32_t> deferred_deletes; for(auto it = m_pending_interests.begin(); it != m_pending_interests.end(); ++it) { if(it->second.is_ready(m_visible_objects)) { notify_interest_done(&it->second); handle_interest_done(it->second.m_interest_id, it->second.m_client_context); deferred_deletes.push_back(it->first); } } for(auto it = deferred_deletes.begin(); it != deferred_deletes.end(); ++it) { m_pending_interests.erase(*it); } } 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.store_total(count); if(it->second.is_ready(m_visible_objects)) { notify_interest_done(&it->second); handle_interest_done(it->second.m_interest_id, it->second.m_client_context); m_pending_interests.erase(context); } } break; case STATESERVER_OBJECT_CHANGING_LOCATION: { doid_t do_id = dgi.read_doid(); doid_t n_parent = dgi.read_doid(); zone_t n_zone = dgi.read_zone(); dgi.skip(sizeof(doid_t) + sizeof(zone_t)); // don't care about the old location bool disable = true; for(auto it = m_interests.begin(); it != m_interests.end(); ++it) { Interest &i = it->second; for(auto it2 = i.zones.begin(); it2 != i.zones.end(); ++it2) { 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; } if(disable && m_owned_objects.find(do_id) == m_owned_objects.end()) { if(m_session_objects.find(do_id) != m_session_objects.end()) { std::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()) { std::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_datagram(Datagram &in_dg, DatagramIterator &dgi) { channel_t sender = dgi.read_uint64(); 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: { uint32_t do_id = dgi.read_uint32(); // 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::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 Datagram dg(targets, sender, DBSS_OBJECT_DELETE_DISK); dg.add_uint32(do_id); send(dg); } // Send delete to database Datagram dg(m_db_channel, do_id, DBSERVER_OBJECT_DELETE); dg.add_uint32(do_id); send(dg); break; } case STATESERVER_OBJECT_SET_FIELD: { uint32_t do_id = dgi.read_uint32(); uint32_t field_id = dgi.read_uint16(); DCField* field = g_dcf->get_field_by_index(field_id); if(field && field->is_db()) { m_log->trace() << "Forwarding SetField for field with id " << field_id << ", on object " << do_id << " to database." << std::endl; Datagram dg(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELD); dg.add_uint32(do_id); dg.add_uint16(field_id); dg.add_data(dgi.read_remainder()); send(dg); } break; } case STATESERVER_OBJECT_SET_FIELDS: { uint32_t do_id = dgi.read_uint32(); uint16_t field_count = dgi.read_uint16(); std::unordered_map<DCField*, std::vector<uint8_t> > db_fields; for(uint16_t i = 0; i < field_count; ++i) { uint16_t field_id = dgi.read_uint16(); DCField* field = g_dcf->get_field_by_index(field_id); if(!field) { m_log->warning() << "Received invalid field in SetFields" " with id " << field_id << std::endl; break; } if(field->is_db()) { dgi.unpack_field(field, db_fields[field]); } } if(db_fields.size() > 0) { m_log->trace() << "Forwarding SetFields on object " << do_id << " to database." << std::endl; Datagram dg(m_db_channel, do_id, DBSERVER_OBJECT_SET_FIELDS); dg.add_uint32(do_id); dg.add_uint16(field_count); for(auto it = db_fields.begin(); it != db_fields.end(); ++it) { dg.add_uint16(it->first->get_number()); dg.add_data(it->second); } send(dg); } break; } case STATESERVER_OBJECT_GET_FIELD: { uint32_t r_context = dgi.read_uint32(); uint32_t r_do_id = dgi.read_uint32(); uint16_t field_id = dgi.read_uint16(); // If object is active or loading, the Object or Loader will handle it if(m_objs.find(r_do_id) != m_objs.end() || m_loading.find(r_do_id) != m_loading.end()) { break; } m_log->trace() << "Received GetField for field with id " << field_id << " on inactive object with id " << r_do_id << std::endl; // Check field is "ram db" or "required" DCField* field = g_dcf->get_field_by_index(field_id); if(!field || !(field->is_required() || (field->is_ram() && field->is_db()))) { Datagram dg(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); dg.add_uint32(r_context); dg.add_uint8(false); send(dg); } if(field->is_db()) { // 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].add_server_header(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); } m_context_datagrams[db_context].add_uint32(r_context); // Send query to database Datagram dg(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELD); dg.add_uint32(db_context); dg.add_uint32(r_do_id); dg.add_uint16(field_id); send(dg); } else // Field is required and not-db { Datagram dg = Datagram(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); dg.add_uint32(r_context); dg.add_uint8(true); dg.add_uint16(field_id); dg.add_data(field->get_default_value()); send(dg); } break; } case DBSERVER_OBJECT_GET_FIELD_RESP: { uint32_t db_context = dgi.read_uint32(); // Check context auto dg_keyval = m_context_datagrams.find(db_context); if(dg_keyval == m_context_datagrams.end()) { break; // Not meant for me, handled by LoadingObject } m_log->trace() << "Received GetFieldResp from database." << std::endl; // Get the datagram from the db_context Datagram dg(dg_keyval->second); // Cleanup the 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_FIELD_RESP) { if(resp_type == STATESERVER_OBJECT_GET_FIELDS_RESP) { m_log->warning() << "Received GetFieldsResp, but expecting GetFieldResp." << std::endl; } else if(resp_type == STATESERVER_OBJECT_GET_ALL_RESP) { m_log->warning() << "Received GetAllResp, but expecting GetFieldResp." << std::endl; } break; } // Add database field payload to response (don't know dclass, so must copy payload) and send dg.add_data(dgi.read_remainder()); send(dg); break; } case STATESERVER_OBJECT_GET_FIELDS: { uint32_t r_context = dgi.read_uint32(); uint32_t r_do_id = dgi.read_uint32(); uint16_t field_count = dgi.read_uint16(); // If object is active or loading, the Object or Loader will handle it if(m_objs.find(r_do_id) != m_objs.end() || m_loading.find(r_do_id) != m_loading.end()) { break; } m_log->trace() << "Received GetFields for inactive object with id " << r_do_id << std::endl; // Read requested fields from datagram std::list<DCField*> db_fields; // Ram|required db fields in request std::list<DCField*> 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(); DCField* field = g_dcf->get_field_by_index(field_id); if(!field) { Datagram dg(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); dg.add_uint32(r_context); dg.add_uint8(false); send(dg); } else if(field->is_ram() || field->is_required()) { if(field->is_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].add_server_header(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); } m_context_datagrams[db_context].add_uint32(r_context); m_context_datagrams[db_context].add_uint8(true); m_context_datagrams[db_context].add_uint16(ram_fields.size() + db_fields.size()); for(auto it = ram_fields.begin(); it != ram_fields.end(); ++it) { m_context_datagrams[db_context].add_uint16((*it)->get_number()); m_context_datagrams[db_context].add_data((*it)->get_default_value()); } // Send query to database Datagram dg(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELDS); dg.add_uint32(db_context); dg.add_uint32(r_do_id); dg.add_uint16(db_fields.size()); for(auto it = db_fields.begin(); it != db_fields.end(); ++it) { dg.add_uint16((*it)->get_number()); } send(dg); } else // If no database fields exist { Datagram dg = Datagram(sender, r_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); dg.add_uint32(r_context); dg.add_uint8(true); dg.add_uint16(ram_fields.size()); for(auto it = ram_fields.begin(); it != ram_fields.end(); ++it) { dg.add_uint16((*it)->get_number()); dg.add_data((*it)->get_default_value()); } send(dg); } break; } case DBSERVER_OBJECT_GET_FIELDS_RESP: { uint32_t db_context = dgi.read_uint32(); // Check context auto dg_keyval = m_context_datagrams.find(db_context); if(dg_keyval == m_context_datagrams.end()) { break; // Not meant for me, handled by LoadingObject } // Get the datagram from the db_context Datagram dg(dg_keyval->second); // Cleanup the 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; } break; } 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_uint8() == true) { dgi.read_uint16(); // Discard field count dg.add_data(dgi.read_remainder()); } send(dg); break; } case STATESERVER_OBJECT_GET_ALL: { uint32_t r_context = dgi.read_uint32(); uint32_t r_do_id = dgi.read_uint32(); // If object is active or loading, the Object or Loader will handle it if(m_objs.find(r_do_id) != m_objs.end() || m_loading.find(r_do_id) != m_loading.end()) { break; } 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++; if(m_context_datagrams.find(db_context) == m_context_datagrams.end()) { m_context_datagrams[db_context].add_server_header(sender, r_do_id, STATESERVER_OBJECT_GET_ALL_RESP); } m_context_datagrams[db_context].add_uint32(r_context); m_context_datagrams[db_context].add_uint32(r_do_id); m_context_datagrams[db_context].add_uint64(INVALID_CHANNEL); // Location // Cache the do_id --> context in case we get a dbss_activate m_inactive_loads[r_do_id].insert(r_context); // Send query to database Datagram dg(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_ALL); dg.add_uint32(db_context); dg.add_uint32(r_do_id); send(dg); break; } case DBSERVER_OBJECT_GET_ALL_RESP: { uint32_t db_context = dgi.read_uint32(); // Check context auto dg_keyval = m_context_datagrams.find(db_context); if(dg_keyval == m_context_datagrams.end()) { break; // Not meant for me, handled by LoadingObject } // Get the datagram from the db_context Datagram dg(dg_keyval->second); // Cleanup the 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; } break; } // Get do_id from datagram check_dgi.seek_payload(); check_dgi.skip(8 + 4); // skip over sender and context to do_id; uint32_t do_id = check_dgi.read_uint32(); // 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_uint8() != true) { break; // 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; break; } DCClass* r_dclass = g_dcf->get_class(dc_id); // Get fields from database std::unordered_map<DCField*, std::vector<uint8_t> > required_fields; std::map<DCField*, std::vector<uint8_t> > ram_fields; if(!unpack_db_fields(dgi, r_dclass, required_fields, ram_fields)) { m_log->error() << "Error while unpacking fields from database." << std::endl; break; } // Add class to response dg.add_uint16(r_dclass->get_number()); // Add required fields to datagram int dcc_field_count = r_dclass->get_num_inherited_fields(); for(int i = 0; i < dcc_field_count; ++i) { DCField *field = r_dclass->get_inherited_field(i); if(!field->as_molecular_field() && field->is_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(auto it = ram_fields.begin(); it != ram_fields.end(); ++it) { dg.add_uint16(it->first->get_number()); dg.add_data(it->second); } // Send response back to caller send(dg); break; } default: { if(msgtype < STATESERVER_MSGTYPE_MIN || msgtype > DBSERVER_MSGTYPE_MAX) { m_log->warning() << "Received unknown message of type " << msgtype << std::endl; } else { m_log->trace() << "Ignoring stateserver or database message" << " of type " << msgtype << std::endl; } } } }
virtual void handle_datagram(Datagram &in_dg, DatagramIterator &dgi) { channel_t sender = dgi.read_uint64(); unsigned short msg_type = dgi.read_uint16(); switch(msg_type) { case DBSERVER_CREATE_STORED_OBJECT: { unsigned int context = dgi.read_uint32(); // Start response with generic header Datagram resp; resp.add_server_header(sender, m_control_channel, DBSERVER_CREATE_STORED_OBJECT_RESP); resp.add_uint32(context); // Get DistributedClass unsigned short dc_id = dgi.read_uint16(); DCClass *dcc = gDCF->get_class(dc_id); if(!dcc) { m_log->error() << "Invalid DCClass when creating object: #" << dc_id << std::endl; resp.add_uint32(0); send(resp); return; } // Unpack fields to be passed to database DatabaseObject dbo(dc_id); unsigned short field_count = dgi.read_uint16(); m_log->spam() << "Unpacking fields..." << std::endl; try { for(unsigned int i = 0; i < field_count; ++i) { unsigned short field_id = dgi.read_uint16(); DCField *field = dcc->get_field_by_index(field_id); if(field) { if(field->is_db()) { dgi.unpack_field(field, dbo.fields[field]); } else { std::string tmp; dgi.unpack_field(field, tmp); } } } } catch(std::exception &e) { m_log->error() << "Error while unpacking fields, msg may be truncated. e.what(): " << e.what() << std::endl; resp.add_uint32(0); send(resp); return; } // Check for required fields, and populate with defaults m_log->spam() << "Checking all required fields exist..." << std::endl; for(int i = 0; i < dcc->get_num_inherited_fields(); ++i) { DCField *field = dcc->get_inherited_field(i); if(field->is_required() && field->is_db() && !field->as_molecular_field()) { if(dbo.fields.find(field) == dbo.fields.end()) { if(!field->has_default_value()) { m_log->error() << "Field " << field->get_name() << " missing when trying to create " "object of type " << dcc->get_name(); resp.add_uint32(0); send(resp); return; } else { dbo.fields[field] = field->get_default_value(); } } } } // Create object in database m_log->spam() << "Creating stored object..." << std::endl; unsigned int do_id = m_db_engine->create_object(dbo); if(do_id == 0 || do_id < m_min_id || do_id > m_max_id) { m_log->error() << "Ran out of DistributedObject ids while creating new object." << std::endl; resp.add_uint32(0); send(resp); return; } m_log->spam() << "... created with ID: " << do_id << std::endl; resp.add_uint32(do_id); send(resp); } break; case DBSERVER_SELECT_STORED_OBJECT_ALL: { unsigned int context = dgi.read_uint32(); Datagram resp; resp.add_server_header(sender, m_control_channel, DBSERVER_SELECT_STORED_OBJECT_ALL_RESP); resp.add_uint32(context); unsigned int do_id = dgi.read_uint32(); DatabaseObject dbo; if(m_db_engine->get_object(do_id, dbo)) { resp.add_uint8(1); resp.add_uint16(dbo.dc_id); resp.add_uint16(dbo.fields.size()); for(auto it = dbo.fields.begin(); it != dbo.fields.end(); ++it) { resp.add_uint16(it->first->get_number()); resp.add_data(it->second); } } else { resp.add_uint8(0); } send(resp); } break; case DBSERVER_DELETE_STORED_OBJECT: { if(dgi.read_uint32() == DBSERVER_DELETE_STORED_OBJECT_VERIFY_CODE) { unsigned int do_id = dgi.read_uint32(); m_db_engine->delete_object(do_id); m_log->debug() << "Deleted object with ID: " << do_id << std::endl; } else { m_log->warning() << "Wrong delete verify code." << std::endl; } } break; default: m_log->error() << "Recieved unknown MsgType: " << msg_type << std::endl; }; }
static BSONObj bamboo2bson(const dclass::DistributedType *type, DatagramIterator &dgi) { // The BSON library's weird data model doesn't allow elements to exist on // their own; they must be part of an object. Therefore, we always return // results in a single BSONObj with key "_" BSONObjBuilder b; switch(type->get_type()) { case dclass::Type::T_INT8: { b << "_" << dgi.read_int8(); } break; case dclass::Type::T_INT16: { b << "_" << dgi.read_int16(); } break; case dclass::Type::T_INT32: { b << "_" << dgi.read_int32(); } break; case dclass::Type::T_INT64: { b.appendIntOrLL("_", dgi.read_int64()); } break; case dclass::Type::T_UINT8: { b << "_" << dgi.read_uint8(); } break; case dclass::Type::T_UINT16: { b << "_" << dgi.read_uint16(); } break; case dclass::Type::T_UINT32: { b << "_" << dgi.read_uint32(); } break; case dclass::Type::T_UINT64: { b.appendIntOrLL("_", dgi.read_uint64()); } break; case dclass::Type::T_CHAR: { unsigned char c = dgi.read_uint8(); string str(c, 1); b << "_" << str; } break; case dclass::Type::T_FLOAT32: { b << "_" << dgi.read_float32(); } break; case dclass::Type::T_FLOAT64: { b << "_" << dgi.read_float64(); } break; case dclass::Type::T_STRING: { vector<uint8_t> vec = dgi.read_data(type->get_size()); string str((const char *)vec.data(), vec.size()); b << "_" << str; } case dclass::Type::T_VARSTRING: { b << "_" << dgi.read_string(); } break; case dclass::Type::T_BLOB: { vector<uint8_t> blob = dgi.read_data(type->get_size()); b.appendBinData("_", blob.size(), BinDataGeneral, blob.data()); } break; case dclass::Type::T_VARBLOB: { vector<uint8_t> blob = dgi.read_blob(); b.appendBinData("_", blob.size(), BinDataGeneral, blob.data()); } break; case dclass::Type::T_ARRAY: { const dclass::ArrayType *array = type->as_array(); BSONArrayBuilder ab; for(size_t i = 0; i < array->get_array_size(); i++) { ab << bamboo2bson(array->get_element_type(), dgi)["_"]; } b << "_" << ab.arr(); } break; case dclass::Type::T_VARARRAY: { const dclass::ArrayType *array = type->as_array(); dgsize_t array_length = dgi.read_size(); dgsize_t starting_size = dgi.tell(); BSONArrayBuilder ab; while(dgi.tell() != starting_size + array_length) { ab << bamboo2bson(array->get_element_type(), dgi)["_"]; if(dgi.tell() > starting_size + array_length) { throw mongo::DBException("Discovered corrupt array-length tag!", 0); } } b << "_" << ab.arr(); } break; case dclass::Type::T_STRUCT: { const dclass::Struct *s = type->as_struct(); size_t fields = s->get_num_fields(); BSONObjBuilder ob; for(unsigned int i = 0; i < fields; ++i) { const dclass::Field *field = s->get_field(i); ob << field->get_name() << bamboo2bson(field->get_type(), dgi)["_"]; } b << "_" << ob.obj(); } break; case dclass::Type::T_METHOD: { const dclass::Method *m = type->as_method(); size_t parameters = m->get_num_parameters(); BSONObjBuilder ob; for(unsigned int i = 0; i < parameters; ++i) { const dclass::Parameter *parameter = m->get_parameter(i); string name = parameter->get_name(); if(name.empty()) { stringstream n; n << "_" << i; name = n.str(); } ob << name << bamboo2bson(parameter->get_type(), dgi)["_"]; } b << "_" << ob.obj(); } break; case dclass::Type::T_INVALID: default: assert(false); break; } return b.obj(); }
// handle_datagram is the handler for datagrams received from the Astron cluster void Client::handle_datagram(Datagram &dg, DatagramIterator &dgi) { channel_t sender = dgi.read_uint64(); uint16_t msgtype = dgi.read_uint16(); switch(msgtype) { case CLIENTAGENT_EJECT: { uint16_t reason = dgi.read_uint16(); std::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 STATESERVER_OBJECT_SET_FIELD: { uint32_t do_id = dgi.read_uint32(); if(!lookup_object(do_id)) { m_log->warning() << "Received server-side field update for unknown object " << do_id << std::endl; return; } if(sender != m_channel) { uint16_t field_id = dgi.read_uint16(); handle_set_field(do_id, field_id, dgi); } } break; case STATESERVER_OBJECT_DELETE_RAM: { uint32_t do_id = dgi.read_uint32(); if(!lookup_object(do_id)) { m_log->warning() << "Received server-side object delete for unknown object " << do_id << std::endl; return; } if(m_seen_objects.find(do_id) != m_seen_objects.end()) { m_seen_objects.erase(do_id); m_id_history.insert(do_id); handle_remove_object(do_id); } if(m_owned_objects.find(do_id) != m_owned_objects.end()) { m_owned_objects.erase(do_id); handle_remove_ownership(do_id); } m_dist_objs.erase(do_id); } break; case STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER: case STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED: { uint32_t do_id = dgi.read_uint32(); uint32_t parent = dgi.read_uint32(); uint32_t zone = dgi.read_uint32(); uint16_t dc_id = dgi.read_uint16(); m_owned_objects.insert(do_id); if(m_dist_objs.find(do_id) == m_dist_objs.end()) { VisibleObject obj; obj.id = do_id; obj.parent = parent; obj.zone = zone; obj.dcc = g_dcf->get_class(dc_id); m_dist_objs[do_id] = obj; } handle_add_ownership(do_id, parent, zone, dc_id, dgi, true); } break; case CLIENTAGENT_SET_CLIENT_ID: { if(m_channel != m_allocated_channel) { unsubscribe_channel(m_channel); } m_channel = dgi.read_uint64(); subscribe_channel(m_channel); } break; case CLIENTAGENT_SEND_DATAGRAM: { Datagram forward; forward.add_data(dgi.read_string()); send_datagram(forward); } break; case CLIENTAGENT_OPEN_CHANNEL: { subscribe_channel(dgi.read_uint64()); } break; case CLIENTAGENT_CLOSE_CHANNEL: { unsubscribe_channel(dgi.read_uint64()); } break; case CLIENTAGENT_ADD_POST_REMOVE: { add_post_remove(dgi.read_string()); } break; case CLIENTAGENT_CLEAR_POST_REMOVES: { clear_post_removes(); } break; case STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED: case STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER: { uint32_t do_id = dgi.read_uint32(); uint32_t parent = dgi.read_uint32(); uint32_t zone = dgi.read_uint32(); uint16_t dc_id = dgi.read_uint16(); if(m_owned_objects.find(do_id) != m_owned_objects.end() || m_seen_objects.find(do_id) != m_seen_objects.end()) { return; } if(m_dist_objs.find(do_id) == m_dist_objs.end()) { VisibleObject obj; obj.id = do_id; obj.dcc = g_dcf->get_class(dc_id); obj.parent = parent; obj.zone = zone; m_dist_objs[do_id] = obj; } m_seen_objects.insert(do_id); handle_add_object(do_id, parent, zone, dc_id, dgi, msgtype == STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER); // TODO: This is a tad inefficient as it checks every pending interest. // In practice, there shouldn't be many add-interest operations active // at once, however. std::list<uint32_t> deferred_deletes; for(auto it = m_pending_interests.begin(); it != m_pending_interests.end(); ++it) { if(it->second->is_ready(m_dist_objs)) { handle_interest_done(it->second->m_interest_id, it->second->m_client_context); deferred_deletes.push_back(it->first); } } for(auto it = deferred_deletes.begin(); it != deferred_deletes.end(); ++it) { m_pending_interests.erase(*it); } } break; case STATESERVER_OBJECT_GET_ZONES_COUNT_RESP: { uint32_t context = dgi.read_uint32(); uint32_t count = dgi.read_uint32(); if(m_pending_interests.find(context) == m_pending_interests.end()) { m_log->error() << "Received GET_ZONES_COUNT_RESP for unknown context " << context << std::endl; return; } m_pending_interests[context]->store_total(count); if(m_pending_interests[context]->is_ready(m_dist_objs)) { handle_interest_done(m_pending_interests[context]->m_interest_id, m_pending_interests[context]->m_client_context); m_pending_interests.erase(context); } } break; case STATESERVER_OBJECT_CHANGING_LOCATION: { uint32_t do_id = dgi.read_uint32(); uint32_t n_parent = dgi.read_uint32(); uint32_t n_zone = dgi.read_uint32(); dgi.read_uint32(); // Old parent; we don't care about this. dgi.read_uint32(); // Old zone; we don't care about this. bool disable = true; for(auto it = m_interests.begin(); it != m_interests.end(); ++it) { Interest &i = it->second; for(auto it2 = i.zones.begin(); it2 != i.zones.end(); ++it2) { if(*it2 == n_zone) { disable = false; break; } } } if(m_dist_objs.find(do_id) != m_dist_objs.end()) { m_dist_objs[do_id].parent = n_parent; m_dist_objs[do_id].zone = n_zone; } if(disable && m_owned_objects.find(do_id) == m_owned_objects.end()) { handle_remove_object(do_id); m_seen_objects.erase(do_id); m_dist_objs.erase(do_id); } else { handle_change_location(do_id, n_parent, n_zone); } } break; default: m_log->error() << "Recv'd unk server msgtype " << msgtype << std::endl; } }
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 DistributedObject::handle_datagram(DatagramHandle, DatagramIterator &dgi) { channel_t sender = dgi.read_channel(); uint16_t msgtype = dgi.read_uint16(); switch(msgtype) { case STATESERVER_DELETE_AI_OBJECTS: { if(m_ai_channel != dgi.read_channel()) { m_log->warning() << " received reset for wrong AI channel.\n"; break; // Not my AI! } annihilate(sender); break; } case STATESERVER_OBJECT_DELETE_RAM: { if(m_do_id != dgi.read_doid()) { break; // Not meant for me! } // Delete object annihilate(sender); break; } case STATESERVER_OBJECT_DELETE_CHILDREN: { doid_t r_do_id = dgi.read_doid(); if(r_do_id == m_do_id) { delete_children(sender); } else if(r_do_id == m_parent_id) { annihilate(sender, false); } break; } case STATESERVER_OBJECT_SET_FIELD: { if(m_do_id != dgi.read_doid()) { break; // Not meant for me! } handle_one_update(dgi, sender); break; } case STATESERVER_OBJECT_SET_FIELDS: { if(m_do_id != dgi.read_doid()) { break; // Not meant for me! } uint16_t field_count = dgi.read_uint16(); for(int16_t i = 0; i < field_count; ++i) { if(!handle_one_update(dgi, sender)) { break; } } break; } case STATESERVER_OBJECT_CHANGING_AI: { doid_t r_parent_id = dgi.read_doid(); channel_t new_channel = dgi.read_channel(); m_log->trace() << "Received ChangingAI notification from " << r_parent_id << ".\n"; if(r_parent_id != m_parent_id) { m_log->warning() << "Received AI channel from " << r_parent_id << " but my parent_id is " << m_parent_id << ".\n"; break; } if(m_ai_explicitly_set) { break; } handle_ai_change(new_channel, sender, false); break; } case STATESERVER_OBJECT_SET_AI: { channel_t new_channel = dgi.read_channel(); m_log->trace() << "Updating AI to " << new_channel << ".\n"; handle_ai_change(new_channel, sender, true); break; } case STATESERVER_OBJECT_GET_AI: { m_log->trace() << "Received AI query from " << sender << ".\n"; DatagramPtr dg = Datagram::create(sender, m_do_id, STATESERVER_OBJECT_GET_AI_RESP); dg->add_uint32(dgi.read_uint32()); // Get context dg->add_doid(m_do_id); dg->add_channel(m_ai_channel); route_datagram(dg); break; } case STATESERVER_OBJECT_GET_AI_RESP: { dgi.read_uint32(); // Discard context doid_t r_parent_id = dgi.read_doid(); m_log->trace() << "Received AI query response from " << r_parent_id << ".\n"; if(r_parent_id != m_parent_id) { m_log->warning() << "Received AI channel from " << r_parent_id << " but my parent_id is " << m_parent_id << ".\n"; break; } channel_t new_ai = dgi.read_channel(); if(m_ai_explicitly_set) { break; } handle_ai_change(new_ai, sender, false); break; } case STATESERVER_OBJECT_CHANGING_LOCATION: { doid_t child_id = dgi.read_doid(); doid_t new_parent = dgi.read_doid(); zone_t new_zone = dgi.read_zone(); doid_t r_do_id = dgi.read_doid(); zone_t r_zone = dgi.read_zone(); if(new_parent == m_do_id) { if(m_do_id == r_do_id) { if(new_zone == r_zone) { break; // No change, so do nothing. } auto &children = m_zone_objects[r_zone]; children.erase(child_id); if(children.empty()) { m_zone_objects.erase(r_zone); } } m_zone_objects[new_zone].insert(child_id); DatagramPtr dg = Datagram::create(child_id, m_do_id, STATESERVER_OBJECT_LOCATION_ACK); dg->add_doid(m_do_id); dg->add_zone(new_zone); route_datagram(dg); } else if(r_do_id == m_do_id) { auto &children = m_zone_objects[r_zone]; children.erase(child_id); if(children.empty()) { m_zone_objects.erase(r_zone); } } else { m_log->warning() << "Received changing location from " << child_id << " for " << r_do_id << ", but my id is " << m_do_id << ".\n"; } break; } case STATESERVER_OBJECT_LOCATION_ACK: { doid_t r_parent_id = dgi.read_doid(); zone_t r_zone_id = dgi.read_zone(); if(r_parent_id != m_parent_id) { m_log->trace() << "Received location acknowledgement from " << r_parent_id << " but my parent_id is " << m_parent_id << ".\n"; } else if(r_zone_id != m_zone_id) { m_log->trace() << "Received location acknowledgement for zone " << r_zone_id << " but my zone_id is " << m_zone_id << ".\n"; } else { m_log->trace() << "Parent acknowledged my location change.\n"; m_parent_synchronized = true; } break; } case STATESERVER_OBJECT_SET_LOCATION: { doid_t new_parent = dgi.read_doid(); zone_t new_zone = dgi.read_zone(); m_log->trace() << "Updating location to Parent: " << new_parent << ", Zone: " << new_zone << ".\n"; handle_location_change(new_parent, new_zone, sender); break; } case STATESERVER_OBJECT_GET_LOCATION: { uint32_t context = dgi.read_uint32(); DatagramPtr dg = Datagram::create(sender, m_do_id, STATESERVER_OBJECT_GET_LOCATION_RESP); dg->add_uint32(context); dg->add_doid(m_do_id); dg->add_location(m_parent_id, m_zone_id); route_datagram(dg); break; } case STATESERVER_OBJECT_GET_LOCATION_RESP: { // This case occurs immediately after object creation. // A parent expects to receive a location_resp from each // of its pre-existing children. if(dgi.read_uint32() != STATESERVER_CONTEXT_WAKE_CHILDREN) { m_log->warning() << "Received unexpected GetLocationResp from " << dgi.read_uint32() << ".\n"; break; } // Get DOID of our child doid_t doid = dgi.read_doid(); // Get location doid_t r_parent = dgi.read_doid(); zone_t r_zone = dgi.read_zone(); // Update the child count if(r_parent == m_do_id) { m_zone_objects[r_zone].insert(doid); } break; } case STATESERVER_OBJECT_GET_ALL: { uint32_t context = dgi.read_uint32(); if(dgi.read_doid() != m_do_id) { return; // Not meant for this object! } DatagramPtr dg = Datagram::create(sender, m_do_id, STATESERVER_OBJECT_GET_ALL_RESP); dg->add_uint32(context); append_required_data(dg); append_other_data(dg); route_datagram(dg); break; } case STATESERVER_OBJECT_GET_FIELD: { uint32_t context = dgi.read_uint32(); if(dgi.read_doid() != m_do_id) { return; // Not meant for this object! } uint16_t field_id = dgi.read_uint16(); DatagramPtr raw_field = Datagram::create(); bool success = handle_one_get(raw_field, field_id); DatagramPtr dg = Datagram::create(sender, m_do_id, STATESERVER_OBJECT_GET_FIELD_RESP); dg->add_uint32(context); dg->add_bool(success); if(success) { dg->add_data(raw_field); } route_datagram(dg); break; } case STATESERVER_OBJECT_GET_FIELDS: { uint32_t context = dgi.read_uint32(); if(dgi.read_doid() != m_do_id) { return; // Not meant for this object! } uint16_t field_count = dgi.read_uint16(); // Read our requested fields into a sorted set set<uint16_t> requested_fields; for(int i = 0; i < field_count; ++i) { uint16_t field_id = dgi.read_uint16(); if(!requested_fields.insert(field_id).second) { const dclass::Field* field = m_dclass->get_field_by_id(field_id); if(field != nullptr) { // If it is null, handle_one_get will produce a warning for us later m_log->warning() << "Received duplicate field '" << field->get_name() << "' in get_fields.\n"; } } } // Try to get the values for all the fields bool success = true; uint16_t fields_found = 0; DatagramPtr raw_fields = Datagram::create(); for(auto it = requested_fields.begin(); it != requested_fields.end(); ++it) { uint16_t field_id = *it; uint16_t length = raw_fields->size(); if(!handle_one_get(raw_fields, field_id, true)) { success = false; break; } if(raw_fields->size() > length) { fields_found++; } } // Send get fields response DatagramPtr dg = Datagram::create(sender, m_do_id, STATESERVER_OBJECT_GET_FIELDS_RESP); dg->add_uint32(context); dg->add_bool(success); if(success) { dg->add_uint16(fields_found); dg->add_data(raw_fields); } route_datagram(dg); break; } case STATESERVER_OBJECT_SET_OWNER: { channel_t new_owner = dgi.read_channel(); m_log->trace() << "Updating owner to " << new_owner << "...\n"; if(new_owner == m_owner_channel) { m_log->trace() << "... owner is the same, do nothing.\n"; return; } if(m_owner_channel) { m_log->trace() << "... broadcasting changing owner...\n"; DatagramPtr dg = Datagram::create(m_owner_channel, sender, STATESERVER_OBJECT_CHANGING_OWNER); dg->add_doid(m_do_id); dg->add_channel(new_owner); dg->add_channel(m_owner_channel); route_datagram(dg); } m_owner_channel = new_owner; if(new_owner) { m_log->trace() << "... sending owner entry...\n"; send_owner_entry(new_owner); } m_log->trace() << "... updated owner.\n"; break; } case STATESERVER_OBJECT_GET_ZONE_OBJECTS: case STATESERVER_OBJECT_GET_ZONES_OBJECTS: { uint32_t context = dgi.read_uint32(); doid_t queried_parent = dgi.read_doid(); m_log->trace() << "Handling get_zones_objects with parent '" << queried_parent << "'" << ". My id is " << m_do_id << " and my parent is " << m_parent_id << ".\n"; uint16_t zone_count = 1; if(msgtype == STATESERVER_OBJECT_GET_ZONES_OBJECTS) { zone_count = dgi.read_uint16(); } if(queried_parent == m_parent_id) { // Query was relayed from parent! See if we match any of the zones // and if so, reply: for(uint16_t i = 0; i < zone_count; ++i) { if(dgi.read_zone() == m_zone_id) { // The parent forwarding this request down to us may or may // not yet know about our presence (and therefore have us // included in the count that it sent to the interested // peer). If we are included in this count, we reply with a // normal interest entry. If not, we reply with a standard // location entry and allow the interested peer to resolve // the difference itself. if(m_parent_synchronized) { send_interest_entry(sender, context); } else { send_location_entry(sender); } break; } } } else if(queried_parent == m_do_id) { doid_t child_count = 0; // Start datagram to relay to children DatagramPtr child_dg = Datagram::create(parent_to_children(m_do_id), sender, STATESERVER_OBJECT_GET_ZONES_OBJECTS); child_dg->add_uint32(context); child_dg->add_doid(queried_parent); child_dg->add_uint16(zone_count); // Get all zones requested for(int i = 0; i < zone_count; ++i) { zone_t zone = dgi.read_zone(); child_count += m_zone_objects[zone].size(); child_dg->add_zone(zone); } // Reply to requestor with count of objects expected DatagramPtr count_dg = Datagram::create(sender, m_do_id, STATESERVER_OBJECT_GET_ZONES_COUNT_RESP); count_dg->add_uint32(context); count_dg->add_doid(child_count); route_datagram(count_dg); // Bounce the message down to all children and have them decide // whether or not to reply. // TODO: Is this really that efficient? if(child_count > 0) { route_datagram(child_dg); } } break; } // zones in Astron don't have meaning to the cluster itself // as such, there is no table of zones to query in the network // instead, a zone is said to be active if it has at least one object in it // to get the active zones, get the keys from m_zone_objects and dump them into a std::set<zone_t> // using an std::set ensures that no duplicate zones are sent // TODO: evaluate efficiency on large games with many DistributedObjects case STATESERVER_GET_ACTIVE_ZONES: { uint32_t context = dgi.read_uint32(); std::unordered_set<zone_t> keys; for(auto kv : m_zone_objects) { keys.insert(kv.first); } DatagramPtr dg = Datagram::create(sender, m_do_id, STATESERVER_GET_ACTIVE_ZONES_RESP); dg->add_uint32(context); dg->add_uint16(keys.size()); std::unordered_set<zone_t>::iterator it; for(it = keys.begin(); it != keys.end(); ++it) { dg->add_zone(*it); } route_datagram(dg); break; } default: if(msgtype < STATESERVER_MSGTYPE_MIN || msgtype > STATESERVER_MSGTYPE_MAX) { m_log->warning() << "Received unknown message of type " << msgtype << ".\n"; } else { m_log->trace() << "Ignoring stateserver message of type " << msgtype << ".\n"; } } }
// 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 LoadingObject::handle_datagram(DatagramHandle in_dg, DatagramIterator &dgi) { /*channel_t sender =*/ dgi.read_channel(); // sender not used uint16_t msgtype = dgi.read_uint16(); switch(msgtype) { case DBSERVER_OBJECT_GET_ALL_RESP: { if(m_is_loaded) { break; // Don't care about these message any more if loaded } uint32_t db_context = dgi.read_uint32(); if(db_context != m_context && m_valid_contexts.find(db_context) == m_valid_contexts.end()) { m_log->warning() << "Received get_all_resp with incorrect context '" << db_context << "'.\n"; break; } m_log->trace() << "Received GetAllResp from database.\n"; m_is_loaded = true; if(dgi.read_bool() != true) { m_log->debug() << "Object not found in database.\n"; m_dbss->discard_loader(m_do_id); forward_datagrams(); break; } uint16_t dc_id = dgi.read_uint16(); const Class *r_dclass = g_dcf->get_class_by_id(dc_id); if(!r_dclass) { m_log->error() << "Received object from database with unknown dclass" << " - id:" << dc_id << std::endl; m_dbss->discard_loader(m_do_id); forward_datagrams(); break; } if(m_dclass && r_dclass != m_dclass) { m_log->error() << "Requested object of class '" << m_dclass->get_id() << "', but received class " << dc_id << std::endl; m_dbss->discard_loader(m_do_id); forward_datagrams(); break; } // Get fields from database if(!unpack_db_fields(dgi, r_dclass, m_required_fields, m_ram_fields)) { m_log->error() << "Error while unpacking fields from database.\n"; m_dbss->discard_loader(m_do_id); forward_datagrams(); break; } // Add default values and updated values int dcc_field_count = r_dclass->get_num_fields(); for(int i = 0; i < dcc_field_count; ++i) { const Field *field = r_dclass->get_field(i); if(!field->as_molecular()) { if(field->has_keyword("required")) { if(m_field_updates.find(field) != m_field_updates.end()) { m_required_fields[field] = m_field_updates[field]; } else if(m_required_fields.find(field) == m_required_fields.end()) { std::string val = field->get_default_value(); m_required_fields[field] = std::vector<uint8_t>(val.begin(), val.end()); } } else if(field->has_keyword("ram")) { if(m_field_updates.find(field) != m_field_updates.end()) { m_ram_fields[field] = m_field_updates[field]; } } } } // Create object on stateserver DistributedObject* obj = new DistributedObject(m_dbss, m_dbss->m_db_channel, m_do_id, m_parent_id, m_zone_id, r_dclass, m_required_fields, m_ram_fields); // Tell DBSS about object and handle datagram queue m_dbss->receive_object(obj); replay_datagrams(obj); // Cleanup this loader m_dbss->discard_loader(m_do_id); forward_datagrams(); break; } case DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS: case DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER: { // Don't cache these messages in the queue, they are received and // handled by the DBSS. Since the object is already loading they // are simply ignored (the DBSS may generate a warning/error). break; } default: { m_datagram_queue.push_back(in_dg); } } }