/* core/entity.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2. */ #include #include #include #include "auxiliary/functions.h" #include "sys/sys.h" #include "core/entity.h" #include "core/cvar.h" #include "core/application.h" #include "core/gameinterface.h" #include "core/gameserver.h" #include "BulletCollision/CollisionShapes/btBoxShape.h" namespace core { // maximal number of entities const size_t MAX_ENTITY = 1048574; using math::Color; using math::Vector3f; /* ---- Static functions for the Entity registry ------------------- */ Entity::Registry Entity::entity_registry; size_t Entity::entity_nextid = 0; void Entity::add(Entity *ent) { Registry::iterator it; entity_nextid = (entity_nextid % MAX_ENTITY) + 1; // lowest entity-id is 1 unsigned int id = entity_nextid; for (it = entity_registry.begin(); it != entity_registry.end() && id == (*it).second->id(); it++) { id++; } ent->entity_id = id; entity_registry[id] = ent; } void Entity::add(Entity *ent, unsigned int id) { if (find(id)) { con_warn << "Duplicate entity " << id << "!\n"; return; } ent->entity_id = id; entity_registry[id] = ent; } Entity *Entity::find(unsigned int id) { Registry::iterator it = entity_registry.find(id); if (it == entity_registry.end()) return 0; else return (*it).second; } void Entity::erase(unsigned int id) { Registry::iterator it = entity_registry.find(id); if (it != entity_registry.end()) { delete((*it).second); (*it).second = 0; entity_registry.erase(it); } else { con_warn << "Could not erase entity " << id << "!\n"; } } void Entity::clear() { for (Entity::Registry::iterator it = Entity::registry().begin(); it != Entity::registry().end(); it++) { delete(*it).second; } Entity::registry().clear(); entity_nextid = 0; } void Entity::list_inventory() const { con_print << " entity id ^B" << id() << " ^Nlabel ^B" << label() << " ^Nname ^B" << name() << std::endl; if (!entity_inventory) { con_print << "no inventory availble" << std::endl; return; } con_print << " ^Nid type label amount" << std::endl; for (Inventory::Items::const_iterator it = entity_inventory->items().begin(); it != entity_inventory->items().end(); it++) { Item *item = (*it); con_print << " " << " ^B" << std::setw(4) << item->info()->id() << " ^N" << aux::pad_right((item->info()->type() ? item->info()->type()->label() : "NULL"), 8) << " ^N" << aux::pad_right(item->info()->label(), 24) << std::setw(5) << item->amount() << std::endl; } con_print << "^B " << entity_inventory->items().size() << " inventory items" << std::endl; } void Entity::list_header() { con_print << " " << " id " << "type " << "label " << "name" << std::endl; } void Entity::list(const Entity *entity) { con_print << " " << "^B" << std::setw(4) << entity->id() << " " << "^N" << std::setfill('0') << std::setw(4) << entity->type() << ":" << std::setw(4) << entity->moduletype() << std::setfill(' ') << " " << "^N" << aux::pad_right(entity->label(), 24) << " " << "^N" << entity->name() << std::endl; } void Entity::list() { list_header(); for (Registry::iterator it = entity_registry.begin(); it != entity_registry.end(); it++) { std::string typeindicator; Entity *entity = (*it).second; list(entity); } con_print << "^B " << entity_registry.size() << " entities" << std::endl; } /* ---- class Entity ----------------------------------------------- */ Entity::Entity() : entity_location(0.0f, 0.0f, 0.0f), entity_color(1.0f, 1.0f, 1.0f, 1.0f), entity_color_second(1.0f, 1.0f, 1.0f, 1.0f) { entity_id = 0; entity_flags = 0; entity_moduletypeid = 0; entity_model = 0; entity_body = 0; entity_body_info = 0; entity_motionstate = 0; entity_collision_shape = 0; entity_speed = 0.0f; entity_mass = 0.0f; entity_radius = 0.5f; entity_shape = Diamond; entity_created = true; entity_destroyed = false; entity_dirty = false; entity_keepalive = 0; entity_zone = 0; entity_oldzone = 0; entity_visible = true; entity_serverside = false; entity_inventory = 0; entity_info = 0; memset(entity_extension, 0, sizeof(entity_extension)); add(this); } Entity::Entity(std::istream & is) { entity_serverside = false; entity_id = 0; entity_zone = 0; entity_oldzone = 0; entity_visible = true; entity_keepalive = 0; entity_model = 0; entity_body = 0; entity_body_info = 0; entity_motionstate = 0; entity_collision_shape = 0; entity_speed = 0.0f; entity_mass = 0.0f; entity_created = true; entity_destroyed = false; entity_inventory = 0; entity_info = 0; entity_inventory = 0; memset(entity_extension, 0, sizeof(entity_extension)); } Entity::~Entity() { // delete extensions for (size_t i = 0; i < 4; i++) { if (entity_extension[i]) delete entity_extension[i]; entity_extension[i] = 0; } // delete entity menus for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { delete(*it); } menus().clear(); // delete inventory if (entity_inventory) delete entity_inventory; if (entity_zone) { entity_zone->remove(this); if (body() && entity_zone->physics()) { entity_zone->physics()->removeRigidBody(body()); } } if (entity_motionstate) delete entity_motionstate; if (entity_collision_shape) delete entity_collision_shape; if (entity_body) delete entity_body; if (entity_body_info) delete entity_body_info; } void Entity::die() { entity_destroyed = true; } void Entity::clear_updates() { entity_created = false; entity_dirty = false; entity_oldzone = 0; if (entity_inventory && entity_inventory->dirty()) { // inventory timestamp must be set for singleplayer entity_inventory->set_timestamp(game()->timestamp()); entity_inventory->set_dirty(false); } } void Entity::set_info(Info *info) { entity_info = info; if (entity_info) { entity_info->set_model(model()); entity_info->set_name(name()); } } void Entity::set_inventory(Inventory *inventory) { if (entity_inventory && (entity_inventory != inventory)) { delete entity_inventory; } entity_inventory = inventory; } void Entity::set_zone(Zone *zone) { if (entity_zone == zone) return; if (entity_zone) { entity_zone->remove(this); if (body() && entity_zone->physics()) { entity_zone->physics()->removeRigidBody(body()); } } // oldzone is cleared after every game frame if (!entity_oldzone) entity_oldzone = entity_zone; entity_zone = zone; entity_dirty = true; if (entity_zone) { entity_zone->add(this); if (body() && entity_zone->physics()) { entity_zone->physics()->addRigidBody(body()); reset(); } } } void Entity::set_model(model::Model *model) { // server-side property should not clear modelname entity_model = model; if (entity_model) { entity_radius = entity_model->radius(); entity_modelname.assign(entity_model->name()); } if (entity_info) { entity_info->set_model(entity_model); } } void Entity::set_modelname(const std::string &modelname) { if (!modelname.size()) { set_model(0); } else { set_model(model::Model::load(modelname)); } if (!entity_model) entity_modelname.clear(); } void Entity::serialize_server_create(std::ostream & os) const { os << moduletype() << " " << flags() << " " << (visible() ? 1 : 0) << " " << (zone() ? zone()->id() : 0) << " " << std::setprecision(8) << entity_location << " " << color() << " " << color_second() << " " << shape() << " " << radius() << " " << std::setprecision(8) << entity_axis.forward() << " " << std::setprecision(8) << entity_axis.left() << " " << "\"" <name() : "") << "\" " << (info() ? info()->id() : 0) << " " << (inventory() ? 1 : 0) << " "; if (inventory()) inventory()->serialize_server_update(os); } void Entity::receive_server_create(std::istream &is) { unsigned int s; unsigned int zo; unsigned int o = 0; std::string n; char c; is >> entity_moduletypeid; is >> entity_flags; is >> o; if (o) entity_visible = true; else entity_visible = false; is >> zo; set_zone(Zone::find(zo)); if (entity_zone && !zo) { con_warn << "Received entity " << entity_id << " for unknown zone " << zo << "!" << std::endl; } is >> entity_location; is >> entity_color; is >> entity_color_second; is >> s; // shape entity_shape = (Shape) s; is >> entity_radius; if (entity_model) entity_radius = model()->radius(); is >> entity_axis[0]; is >> entity_axis[1]; entity_axis[2] = math::crossproduct(entity_axis.forward(), entity_axis.left()); // read label n.clear(); while ((is.get(c)) && (c != '"')); while ((is.get(c)) && (c != '"')) n += c; set_label(n); // read name n.clear(); while ((is.get(c)) && (c != '"')); while ((is.get(c)) && (c != '"')) n += c; set_name(n); // read model name n.clear(); while ((is.get(c)) && (c != '"')); while ((is.get(c)) && (c != '"')) n += c; set_modelname(n); // read info id if(is >> o) { entity_info = Info::find(o); if (o && !entity_info) entity_info = new Info(o); } else { entity_info = 0; } // has inventory if (is >> o) { if (!o) { if (inventory()) { con_warn << "Receive no inventory for entity " << id() << " " << label() << " with inventory!" << std::endl; entity_inventory->clear(); } } else { if (!inventory()) { entity_inventory = new Inventory(); } } } else { if (inventory()) { con_warn << "Receive no inventory for entity " << id() << " " << label() << " with inventory!" << std::endl; entity_inventory->clear(); } } if (inventory()) { inventory()->receive_server_update(is); } entity_dirty = false; } void Entity::serialize_client_update(std::ostream & os) const { } void Entity::receive_client_update(std::istream &is) { } void Entity::serialize_server_update(std::ostream & os) const { } void Entity::receive_server_update(std::istream &is) { } void Entity::frame(float seconds) { } void Entity::upkeep(const unsigned long timestamp) { } void Entity::add_menu(MenuDescription *menu) { entity_menus.push_back(menu); } MenuDescription *Entity::find_menu(std::string const &label) { for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { if (label.compare((*it)->label()) == 0) return (*it); } return 0; } void Entity::remove_menu(std::string const &label) { for (Menus::iterator it = menus().begin(); it != menus().end(); it++) { if (label.compare((*it)->label()) == 0) menus().erase(it); return; } } void Entity::reset() { // location and orientation btTransform t; t.setIdentity(); t.setOrigin(to_btVector3(location())); t.setBasis(to_btMatrix3x3(axis())); // construct physics body if necessary if (!entity_body) { // create collision shape if (model()) { entity_collision_shape = new btBoxShape(to_btVector3(model()->box().max())); } else { entity_collision_shape = new btSphereShape(radius()); } btVector3 inertia(0, 0, 0); if (entity_mass) entity_collision_shape->calculateLocalInertia(entity_mass, inertia); // create motion state entity_motionstate = new btDefaultMotionState(t); // create physics body entity_body_info = new btRigidBody::btRigidBodyConstructionInfo(entity_mass, entity_motionstate, entity_collision_shape, inertia); entity_body = new btRigidBody(*entity_body_info); entity_body->setActivationState(ISLAND_SLEEPING); if (zone()) zone()->physics()->addRigidBody(entity_body); } // transfer entity location to motion state body()->setWorldTransform(t); body()->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); body()->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f)); if (zone()) zone()->physics()->synchronizeSingleMotionState(entity_body); set_dirty(); } /* ---- class EntityDynamic ---------------------------------------- */ EntityDynamic::EntityDynamic() : Entity() { entity_state = Normal; entity_timer = 0; } EntityDynamic::EntityDynamic(std::istream & is) : Entity(is) { entity_state = Normal; entity_timer = 0; } EntityDynamic::~EntityDynamic() { } void EntityDynamic::set_state(int state) { if (this->state() != state) { entity_state = state; set_dirty(); } } void EntityDynamic::reset() { Entity::reset(); if (entity_state == Docked) { body()->setLinearFactor(btVector3(0.0f, 0.0f, 0.0f)); body()->setAngularFactor(btVector3(0.0f, 0.0f, 0.0f)); } else { body()->setLinearFactor(btVector3(1.0f, 1.0f, 1.0f)); body()->setAngularFactor(btVector3(1.0f, 1.0f, 1.0f)); } } void EntityDynamic::frame(float seconds) { if (entity_state == Docked) { return; } // transfer bullet state to entity state if (entity_body) { // this makes sure an update is sent if speed goes to 0 in the next step if (entity_speed > 0) { set_dirty(); } btTransform t; entity_motionstate->getWorldTransform(t); get_location().assign(t.getOrigin()); get_axis().assign(t.getBasis()); entity_speed = (float) entity_body->getLinearVelocity().length(); if (entity_speed > 0) { set_dirty(); } } } void EntityDynamic::serialize_server_create(std::ostream & os) const { Entity::serialize_server_create(os); os << roundf(entity_speed * 100.0f) << " " << entity_state << " "; if (entity_state != Normal) { os << entity_timer << " "; } } void EntityDynamic::receive_server_create(std::istream &is) { Entity::receive_server_create(is); is >> entity_speed; entity_speed /= 100.0f; is >> entity_state; if (entity_state != Normal) { is >> entity_timer; } else { entity_timer = 0; } } void EntityDynamic::serialize_client_update(std::ostream & os) const { } void EntityDynamic::receive_client_update(std::istream &is) { } void EntityDynamic::serialize_server_update(std::ostream & os) const { os << (visible() ? 1 : 0) << " "; if (visible()) { os << std::setprecision(8) << location() << " " << axis().forward() << " " << axis().left() << " " << roundf(entity_speed * 100.0f) << " " << entity_state << " "; if (entity_state != Normal) { os << entity_timer << " "; } } } void EntityDynamic::receive_server_update(std::istream &is) { unsigned int o = 0; is >> o; // visibility if (o) { set_visible(); is >> get_location(); // axis up vector is the crossproduct of forward and left is >> get_axis()[0]; is >> get_axis()[1]; get_axis()[2] = math::crossproduct(axis().forward(), axis().left()); is >> entity_speed; entity_speed /= 100.0f; is >> entity_state; if (entity_state != Normal) { is >> entity_timer; } else { entity_timer = 0; } } else { set_visible(false); } } /*----- EntityControlable::ActionInterface ------------------------- */ EntityControlable::ActionInterface::ActionInterface(EntityControlable *entity) { actioninterface_entity = entity; } EntityControlable::ActionInterface::~ActionInterface() { actioninterface_entity = 0; } void EntityControlable::ActionInterface::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { actioninterface_entity->action(deltaTimeStep); } void EntityControlable::ActionInterface::debugDraw(btIDebugDraw* debugDrawer) { } /*----- EntityControlable ------------------------------------------ */ EntityControlable::EntityControlable() : EntityDynamic() { entity_thrust = 0; entity_movement = 0; target_direction = 0.0f; target_thrust = 0.0f; target_pitch = 0.0f; target_roll = 0.0f; target_strafe = 0.0f; target_afterburner = 0.0f; entity_owner = 0; entity_actioninterface = 0; } EntityControlable::EntityControlable(std::istream & is) : EntityDynamic(is) { entity_thrust = 0; entity_movement = 0; target_direction = 0.0f; target_thrust = 0.0f; target_pitch = 0.0f; target_roll = 0.0f; target_strafe = 0.0f; target_afterburner = 0.0f; entity_owner = 0; entity_actioninterface = 0; } EntityControlable::~EntityControlable() { if (entity_owner) entity_owner->remove_asset(this); if (entity_actioninterface) { entity_zone->physics()->removeAction(entity_actioninterface); delete entity_actioninterface; } } void EntityControlable::set_owner(Player *owner) { if (entity_owner) entity_owner->remove_asset(this); entity_owner = owner; if (entity_owner) entity_owner->add_asset(this); } void EntityControlable::serialize_server_create(std::ostream & os) const { EntityDynamic::serialize_server_create(os); os << roundf(entity_thrust*100.0f) << " " << (entity_owner ? entity_owner->id() : 0) << " "; } void EntityControlable::receive_server_create(std::istream &is) { EntityDynamic::receive_server_create(is); is >> entity_thrust; entity_thrust /= 100.0f; entity_owner = 0; int owner_id = 0; is >> owner_id; if (owner_id) { for (GameInterface::Players::iterator pit = game()->players().begin(); pit != game()->players().end(); pit++) { Player *player = (*pit); if (player->id() == owner_id) { player->add_asset(this); entity_owner = player; } } if (!entity_owner) { con_warn << "could not find owner " << owner_id << " for entity " << id() << "\n"; } } } void EntityControlable::serialize_client_update(std::ostream & os) const { EntityDynamic::serialize_client_update(os); os << target_direction << " "; os << target_pitch << " "; os << target_thrust << " "; os << target_roll << " "; os << target_strafe << " "; os << target_vstrafe << " "; os << target_afterburner << " "; } void EntityControlable::receive_client_update(std::istream &is) { EntityDynamic::receive_client_update(is); is >> target_direction; is >> target_pitch; is >> target_thrust; is >> target_roll; is >> target_strafe; is >> target_vstrafe; is >> target_afterburner; } void EntityControlable::serialize_server_update(std::ostream & os) const { EntityDynamic::serialize_server_update(os); os << roundf(entity_thrust*100.0f) << " "; os << roundf(entity_movement * 100.0f) << " "; } void EntityControlable::receive_server_update(std::istream &is) { EntityDynamic::receive_server_update(is); is >> entity_thrust; entity_thrust /= 100.0f; is >> entity_movement; entity_movement /= 100.0f; } void EntityControlable::set_thrust(float thrust) { if (thrust != target_thrust) { target_thrust = thrust; set_dirty(); } } void EntityControlable::set_direction(float direction) { if (target_direction != direction) { target_direction = direction; set_dirty(); } } void EntityControlable::set_pitch(float pitch) { if (target_pitch != pitch) { target_pitch = pitch; set_dirty(); } } void EntityControlable::set_roll(float roll) { if (target_roll != roll) { target_roll = roll; set_dirty(); } } void EntityControlable::set_strafe(float strafe) { if (target_strafe != strafe) { target_strafe = strafe; set_dirty(); } } void EntityControlable::set_vstrafe(float vstrafe) { if (target_vstrafe != vstrafe) { target_vstrafe = vstrafe; set_dirty(); } } void EntityControlable::set_afterburner(float afterburner) { if (target_afterburner != afterburner) { target_afterburner = afterburner; set_dirty(); } } void EntityControlable::set_zone(Zone *zone) { if (entity_zone == zone) return; if (entity_zone) { entity_zone->remove(this); if (body() && entity_zone->physics()) { entity_zone->physics()->removeAction(entity_actioninterface); entity_zone->physics()->removeRigidBody(body()); } } // oldzone is cleared after every game frame if (!entity_oldzone) entity_oldzone = entity_zone; entity_zone = zone; set_dirty(); if (entity_zone) { entity_zone->add(this); if (body() && entity_zone->physics()) { entity_zone->physics()->addRigidBody(body()); entity_zone->physics()->addAction(entity_actioninterface); reset(); } } } void EntityControlable::reset() { // location and orientation btTransform t; t.setIdentity(); t.setOrigin(to_btVector3(location())); t.setBasis(to_btMatrix3x3(axis())); // construct physics body if necessary if (!entity_body) { // create collision shape if (model()) { entity_collision_shape = new btBoxShape(to_btVector3(model()->box().max())); } else { entity_collision_shape = new btSphereShape(radius()); } btVector3 inertia(0, 0, 0); if (entity_mass) entity_collision_shape->calculateLocalInertia(entity_mass, inertia); // create motion state entity_motionstate = new btDefaultMotionState(t); // create physics body entity_body_info = new btRigidBody::btRigidBodyConstructionInfo(entity_mass, entity_motionstate, entity_collision_shape, inertia); entity_body = new btRigidBody(*entity_body_info); entity_body->setActivationState(DISABLE_DEACTIVATION); entity_actioninterface = new ActionInterface(this); if (zone()) { entity_zone->physics()->addRigidBody(body()); entity_zone->physics()->addAction(entity_actioninterface); } } body()->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); body()->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); body()->setWorldTransform(t); if (entity_state == Docked) { body()->setLinearFactor(btVector3(0.0f, 0.0f, 0.0f)); body()->setAngularFactor(btVector3(0.0f, 0.0f, 0.0f)); } else { body()->setLinearFactor(btVector3(1.0f, 1.0f, 1.0f)); body()->setAngularFactor(btVector3(1.0f, 1.0f, 1.0f)); } if (zone()) { // transfer entity location to motion state zone()->physics()->synchronizeSingleMotionState(entity_body); } set_dirty(); } // bullet physics frame (runs at bullet framerate) void EntityControlable::action(btScalar seconds) { } // osirion game frame (runs at osirion server framerate) void EntityControlable::frame(float seconds) { EntityDynamic::frame(seconds); } /*----- EntityGlobe ------------------------------------------------ */ EntityGlobe::EntityGlobe() : Entity() { render_texture = 0; entity_rotationspeed = 0; entity_shape = Sphere; } EntityGlobe::EntityGlobe(std::istream & is) : Entity(is) { render_texture = 0; entity_rotationspeed = 0; entity_shape = Sphere; } EntityGlobe::~EntityGlobe() { } void EntityGlobe::serialize_server_create(std::ostream & os) const { Entity::serialize_server_create(os); os << entity_rotationspeed << " \"" << entity_texture << "\" "; } void EntityGlobe::receive_server_create(std::istream &is) { Entity::receive_server_create(is); is >> entity_rotationspeed; std::string n; char c; while ((is.get(c)) && (c != '"')); while ((is.get(c)) && (c != '"')) n += c; entity_texture = n; n.clear(); } }