コード例 #1
0
ファイル: DBStateServer.cpp プロジェクト: Astron/Astron
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);
}
コード例 #2
0
ファイル: DBStateServer.cpp プロジェクト: Astron/Astron
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);
}
コード例 #3
0
ファイル: DBOperation.cpp プロジェクト: Christofosho/Astron
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);
}
コード例 #4
0
ファイル: DBStateServer.cpp プロジェクト: Astron/Astron
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);
    }
}
コード例 #5
0
ファイル: DBStateServer.cpp プロジェクト: Astron/Astron
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);
    }
}
コード例 #6
0
ファイル: DBOperation.cpp プロジェクト: Christofosho/Astron
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;
}
コード例 #7
0
ファイル: DBOperation.cpp プロジェクト: Christofosho/Astron
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;
}
コード例 #8
0
ファイル: DBStateServer.cpp プロジェクト: Astron/Astron
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);
    }
}
コード例 #9
0
ファイル: Client.cpp プロジェクト: Unkn0wn0ne/Astron
// 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.";
    }
}
コード例 #10
0
ファイル: DBStateServer.cpp プロジェクト: shadowcoder/Astron
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;
			}
		}
	}
}
コード例 #11
0
ファイル: DatabaseServer.cpp プロジェクト: Idylia/OpenOTP
		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;
			};
		}
コード例 #12
0
ファイル: MongoDatabase.cpp プロジェクト: Fantasticer/Astron
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();
}
コード例 #13
0
ファイル: Client.cpp プロジェクト: shadowcoder/Astron
// 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;
	}
}
コード例 #14
0
ファイル: DBStateServer.cpp プロジェクト: Astron/Astron
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);
}
コード例 #15
0
ファイル: DistributedObject.cpp プロジェクト: Astron/Astron
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";
        }
    }
}
コード例 #16
0
ファイル: Client.cpp プロジェクト: staticfox/Astron
// 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.";
    }
}
コード例 #17
0
ファイル: LoadingObject.cpp プロジェクト: Echocage/Astron
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);
		}
	}
}