예제 #1
0
void DBStateServer::handle_get_activated(channel_t sender, DatagramIterator& dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    if(m_loading.find(r_do_id) != m_loading.end()) {
        return;
    }

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

    if(m_objs.find(r_do_id) != m_objs.end()) {
        // If object is active return true
        DatagramPtr dg = Datagram::create(sender, r_do_id, DBSS_OBJECT_GET_ACTIVATED_RESP);
        dg->add_uint32(r_context);
        dg->add_doid(r_do_id);
        dg->add_bool(true);
        route_datagram(dg);
    } else {
        // If object isn't active or loading, we can return false
        DatagramPtr dg = Datagram::create(sender, r_do_id, DBSS_OBJECT_GET_ACTIVATED_RESP);
        dg->add_uint32(r_context);
        dg->add_doid(r_do_id);
        dg->add_bool(false);
        route_datagram(dg);
    }
}
예제 #2
0
void DBStateServer::handle_get_field(channel_t sender, DatagramIterator &dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    uint16_t field_id = dgi.read_uint16();
    if(is_activated_object(r_do_id)) {
        return;
    }

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

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

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

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

        // Send query to database
        DatagramPtr dg = Datagram::create(m_db_channel, r_do_id, DBSERVER_OBJECT_GET_FIELD);
        dg->add_uint32(db_context);
        dg->add_doid(r_do_id);
        dg->add_uint16(field_id);
        route_datagram(dg);
    } else if(field->has_default_value()) { // Field is required and not-db
        DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP);
        dg->add_uint32(r_context);
        dg->add_bool(true);
        dg->add_uint16(field_id);
        dg->add_data(field->get_default_value());
        route_datagram(dg);
    } else {
        DatagramPtr dg = Datagram::create(sender, r_do_id, STATESERVER_OBJECT_GET_FIELD_RESP);
        dg->add_uint32(r_context);
        dg->add_bool(false);
        route_datagram(dg);
    }
}
예제 #3
0
void DBStateServer::handle_get_fields(channel_t sender, DatagramIterator &dgi)
{
    uint32_t r_context = dgi.read_uint32();
    doid_t r_do_id = dgi.read_doid();
    uint16_t field_count = dgi.read_uint16();
    if(is_activated_object(r_do_id)) {
        return;
    }

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

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

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

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

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