#include "precompile.h" #include #include #include #include "playermgr.h" #include "player.h" #include "cs_proto.pb.h" #include "room.h" #include "android.h" #include "metamgr.h" #include "bullet.h" #include "collider.h" #include "obstacle.h" #include "building.h" #include "loot.h" #include "collision.h" #include "roommgr.h" const int ROOM_MAX_PLAYER_NUM = 50; #if 0 const int ANDROID_NUM = 0; #else const int ANDROID_NUM = 10; #endif Room::~Room() { } void Room::Init() { ShuaAndroid(); CreateThings(); stats_timer_ = a8::Timer::Instance()->AddRepeatTimer( 1000 * 5, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); if (!room->game_over) { room->OutputDebugLog(); } } ); } void Room::UnInit() { if (!stats_timer_) { a8::Timer::Instance()->DeleteTimer(stats_timer_); stats_timer_ = nullptr; } } void Room::Update(int delta_time) { long long begin_tick = a8::XGetTickCount(); elapsed_time_ += delta_time; while (elapsed_time_ >= 50) { if (frame_no % 2 == 0) { ResetFrameData(); UpdateGas(); } for (auto& pair : moveable_hash_) { if (pair.second->entity_type == ET_Player && pair.second->updated_times <= 0) { if (frame_no % 2 == 0) { pair.second->Update(50); pair.second->updated_times++; } } else { pair.second->Update(50); pair.second->updated_times++; } } if (frame_no % 2 == 0) { for (auto& pair : accountid_hash_) { pair.second->SendUpdateMsg(); } ClearDeletedObjects(); ProcAddedObjects(); { { std::vector del_ids; for (auto& pair : frame_data.deleted_objects_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.deleted_objects_hash.erase(id); } } { std::vector del_ids; for (auto& pair : frame_data.softdeleted_objects_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.softdeleted_objects_hash.erase(id); } } { std::vector del_ids; for (auto& pair : frame_data.bullets_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.bullets_hash.erase(id); } } { std::vector del_ids; for (auto& pair : frame_data.shots_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.shots_hash.erase(id); } } { std::vector del_ids; for (auto& pair : frame_data.explosions_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.explosions_hash.erase(id); } } { std::vector del_ids; for (auto& pair : frame_data.smokes_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.smokes_hash.erase(id); } } { std::vector del_ids; for (auto& pair : frame_data.emotes_hash) { if (pair.first <= frame_no) { del_ids.push_back(pair.first); } } for (long long id : del_ids) { frame_data.emotes_hash.erase(id); } } } } ++frame_no; elapsed_time_ -= 50; } long long end_tick = a8::XGetTickCount(); if (end_tick - begin_tick > profile.max_rundelay) { profile.max_rundelay = end_tick - begin_tick; } } bool Room::IsFull() { return accountid_hash_.size() >= ROOM_MAX_PLAYER_NUM; } int Room::GetPlayerNum() { return accountid_hash_.size(); } Player* Room::GetPlayerByAccountId(const std::string& accountid) { auto itr = accountid_hash_.find(accountid); return itr != accountid_hash_.end() ? itr->second : nullptr; } Player* Room::GetPlayerByUniId(unsigned short uniid) { Entity* entity = GetEntityByUniId(uniid); return entity->entity_type == ET_Player && entity->entity_subtype == EST_Player ? (Player*)entity : nullptr; } Entity* Room::GetEntityByUniId(unsigned short uniid) { auto itr = uniid_hash_.find(uniid); return itr != uniid_hash_.end() ? itr->second : nullptr; } int Room::AliveCount() { return alive_count_; } void Room::AddPlayer(Player* hum) { { hum->pos.x = 3000 + rand() % 100; hum->pos.y = 3000 + rand() % 200; hum->attack_dir = hum->pos; hum->attack_dir.Normalize(); hum->attack_dir.Rotate(a8::RandAngle()); hum->move_dir = hum->attack_dir; } hum->entity_uniid = AllocUniid(); hum->room = this; hum->join_frameno = frame_no; hum->Initialize(); uniid_hash_[hum->entity_uniid] = hum; moveable_hash_[hum->entity_uniid] = hum; accountid_hash_[hum->account_id] = hum; human_hash_[hum->entity_uniid] = hum; ++alive_count_; hum->new_objects.insert(hum); hum->part_objects.insert(hum); for (auto& pair : human_hash_) { if (pair.second != hum) { pair.second->new_objects.insert(hum); pair.second->part_objects.insert(hum); hum->new_objects.insert(pair.second); hum->part_objects.insert(pair.second); } } for (auto& pair : uniid_hash_) { switch (pair.second->entity_type) { case ET_Building: case ET_Obstacle: { hum->new_objects.insert(pair.second); hum->part_objects.insert(pair.second); } break; default: { } break; } } } unsigned short Room::AllocUniid() { while (GetEntityByUniId(++current_uniid) || be_added_hash_.find(current_uniid) != be_added_hash_.end() || current_uniid == 0) {} return current_uniid; } void Room::ShuaAndroid() { MetaData::Player* hum_meta = MetaMgr::Instance()->GetPlayer(40002); assert(hum_meta); if (!hum_meta) { abort(); } for (int i = 0; i < ANDROID_NUM; ++i) { Android* hum = new Android(); hum->name = a8::Format("机器人%d", {i+1}); hum->meta = hum_meta; hum->entity_uniid = AllocUniid(); { hum->pos.x = 3000 + rand() % 400; hum->pos.y = 3000 + rand() % 500; hum->attack_dir = hum->pos; hum->attack_dir.Normalize(); hum->attack_dir.Rotate(a8::RandAngle()); hum->move_dir = hum->attack_dir; } hum->room = this; hum->Initialize(); uniid_hash_[hum->entity_uniid] = hum; moveable_hash_[hum->entity_uniid] = hum; human_hash_[hum->entity_uniid] = hum; ++alive_count_; for (auto& pair : human_hash_) { if (pair.second != hum) { pair.second->new_objects.insert(hum); pair.second->part_objects.insert(hum); hum->new_objects.insert(pair.second); hum->part_objects.insert(pair.second); } } } } Human* Room::FindEnemy(Human* hum) { std::vector enemys; enemys.reserve(50); for (auto& pair : accountid_hash_) { if (pair.second->team_id == 0 || pair.second->team_id != hum->team_id ) { enemys.push_back(pair.second); } } std::sort(enemys.begin(), enemys.end(), [hum] (Human* a, Human *b) -> bool { return (hum->pos - a->pos).Norm() < (hum->pos - b->pos).Norm(); }); return !enemys.empty() ? enemys[0] : nullptr; } void Room::CollisionDetection(Entity* sender, int detection_flags, std::vector& objects) { assert(uniid_hash_.size() < 1000); for (auto& pair : uniid_hash_) { if (a8::HasBitFlag(detection_flags, ET_Player) && pair.second->entity_type == ET_Player) { if (sender->entity_type == ET_Bullet) { Bullet* bullet = (Bullet*)sender; Human* hum = (Human*)pair.second; if (hum != bullet->player && (hum->team_id == 0 || bullet->player->team_id != hum->team_id)) { if (bullet->TestCollision(hum)) { objects.push_back(hum); } } } } if (a8::HasBitFlag(detection_flags, ET_Obstacle) && pair.second->entity_type == ET_Obstacle) { if (sender->entity_type == ET_Bullet || sender->entity_type == ET_Player) { if (pair.second != sender && (sender->last_collision_door == nullptr || sender->last_collision_door != pair.second) && sender->TestCollision(pair.second)) { objects.push_back(pair.second); } } } if (a8::HasBitFlag(detection_flags, ET_Building) && pair.second->entity_type == ET_Building) { if (sender->entity_type == ET_Bullet || sender->entity_type == ET_Player) { if (pair.second != sender && sender->TestCollision(pair.second)) { objects.push_back(pair.second); } } } } } void Room::AddDeletedObject(unsigned short obj_uniid, bool soft_delete) { std::set* deleted_objects = nullptr; if (soft_delete) { auto itr = frame_data.softdeleted_objects_hash.find(frame_no); if (itr == frame_data.softdeleted_objects_hash.end()) { frame_data.softdeleted_objects_hash[frame_no] = std::set(); itr = frame_data.softdeleted_objects_hash.find(frame_no); } deleted_objects = &itr->second; } else { auto itr = frame_data.deleted_objects_hash.find(frame_no); if (itr == frame_data.deleted_objects_hash.end()) { frame_data.deleted_objects_hash[frame_no] = std::set(); itr = frame_data.deleted_objects_hash.find(frame_no); } deleted_objects = &itr->second; } deleted_objects->insert(obj_uniid); } void Room::FillSMJoinedNotify(Player* self_hum, cs::SMJoinedNotify& msg) { msg.set_team_mode(msg.team_mode()); msg.set_player_id(self_hum->entity_uniid); msg.set_started(false); for (auto& pair : uniid_hash_) { if (pair.second->entity_type == ET_Player) { Human* hum = (Human*)pair.second; cs::MFPlayerInfo* info = msg.add_player_infos(); info->set_player_id(hum->entity_uniid); info->set_team_id(hum->team_id); info->set_name(hum->name); } } } void Room::ResetFrameData() { } void Room::ScatterDrop(Vector2D center, int drop_id) { MetaData::Drop* drop_meta = MetaMgr::Instance()->GetDrop(drop_id); if (drop_meta) { std::vector> drop_items; drop_meta->RandItems(drop_items); for (auto& item : drop_items) { Vector2D dir = Vector2D::UP; dir.Rotate(a8::RandAngle()); DropItem(center + dir * (5 + rand() % 50), std::get<0>(item), std::get<1>(item)); } } } void Room::CreateThings() { std::string tpl_name = map_meta->RandTemplate(); std::vector* things = MetaMgr::Instance()->GetMapTplThing(tpl_name); if (things) { for (auto& thing_tpl : *things) { if (thing_tpl.i->weight() >= rand() % 10000) { int thing_id = thing_tpl.RandThing(); MetaData::MapThing* thing_meta = MetaMgr::Instance()->GetMapThing(thing_id); if (thing_meta) { if (thing_meta->i->is_house()) { MetaData::Building* building_meta = MetaMgr::Instance()->GetBuilding(thing_meta->i->house_id()); if (building_meta) { Building* entity = new Building(); entity->room = this; entity->meta = building_meta; entity->building_id = thing_id; entity->entity_uniid = AllocUniid(); entity->pos = Vector2D(thing_tpl.i->x(), thing_tpl.i->y()); entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; } } else { Obstacle* entity = new Obstacle(); entity->room = this; entity->meta = thing_meta; entity->entity_uniid = AllocUniid(); entity->pos = Vector2D(thing_tpl.i->x(), thing_tpl.i->y()); entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; } } } } } } void Room::FillSMMapInfo(cs::SMMapInfo& map_info) { for (auto& pair :uniid_hash_) { switch (pair.second->entity_type) { case ET_Obstacle: { Obstacle* entity = (Obstacle*)pair.second; if (!entity->building) { cs::MFMapObject* p = map_info.add_objects(); p->set_object_id(entity->meta->i->thing_id()); entity->pos.ToPB(p->mutable_pos()); } } break; case ET_Building: { Building* entity = (Building*)pair.second; cs::MFMapObject* p = map_info.add_objects(); p->set_object_id(entity->building_id); entity->pos.ToPB(p->mutable_pos()); } break; default: { } break; } } } void Room::DropItem(Vector2D pos, int item_id, int item_count) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(item_id); if (equip_meta) { Loot* entity = new Loot(); entity->room = this; entity->meta = equip_meta; entity->entity_uniid = AllocUniid(); { Vector2D dir = Vector2D::UP; dir.Rotate(a8::RandAngle()); entity->pos = pos + dir * (25 + rand() % 50); } entity->item_id = equip_meta->i->id(); entity->count = item_count; entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; for (auto& pair : human_hash_) { pair.second->new_objects.insert(entity); pair.second->part_objects.insert(entity); } } } void Room::CreateDoor(Building* building, int door_idx) { if (door_idx >= 0 && door_idx < building->meta->doors.size()) { MetaData::Building::Door* door_meta = &building->meta->doors[door_idx]; MetaData::MapThing* thing = MetaMgr::Instance()->GetMapThing(61701); if (thing) { Obstacle* entity = new Obstacle(); entity->room = this; entity->meta = thing; entity->entity_uniid = AllocUniid(); entity->is_door = true; entity->door_id = door_meta->door_id; entity->door_state = DoorStateClose; entity->building = building; entity->door_house_uniid = building->entity_uniid; entity->door_state0 = door_meta->state0; entity->door_state1 = door_meta->state1; entity->pos = Vector2D(building->pos.x + entity->door_state0->x() - building->meta->i->tilewidth() / 2.0, building->pos.y + entity->door_state0->y() - building->meta->i->tileheight() / 2.0); entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; for (auto& pair : human_hash_) { pair.second->new_objects.insert(entity); pair.second->part_objects.insert(entity); } } } } void Room::CreateHouseObstacle(Building* building, int id, float x, float y) { MetaData::MapThing* thing = MetaMgr::Instance()->GetMapThing(id); if (thing) { Obstacle* entity = new Obstacle(); entity->room = this; entity->meta = thing; entity->building = building; entity->entity_uniid = AllocUniid(); entity->pos = Vector2D(building->pos.x + x - building->meta->i->tilewidth() / 2.0, building->pos.y + y - building->meta->i->tileheight() / 2.0); entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; for (auto& pair : human_hash_) { pair.second->new_objects.insert(entity); pair.second->part_objects.insert(entity); } } } void Room::CreateLoot(int equip_id, Vector2D pos, int count) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(equip_id); if (equip_meta) { Loot* entity = new Loot(); entity->room = this; entity->meta = equip_meta; entity->entity_uniid = AllocUniid(); entity->pos = pos; entity->item_id = equip_id; entity->count = count; entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; for (auto& pair : human_hash_) { pair.second->new_objects.insert(entity); pair.second->part_objects.insert(entity); } } } void Room::CreateBullet(Human* hum, MetaData::Equip* gun_meta, Vector2D pos, Vector2D dir, float fly_distance) { Bullet* bullet = new Bullet(); bullet->player = hum; bullet->room = this; bullet->gun_meta = gun_meta; bullet->meta = MetaMgr::Instance()->GetEquip(gun_meta->i->use_bullet()); bullet->pos = pos; bullet->dir = dir; bullet->born_pos = pos; bullet->born_dir = dir; bullet->fly_distance = fly_distance; bullet->entity_uniid = AllocUniid(); bullet->Initialize(); be_added_hash_[bullet->entity_uniid] = bullet; } void Room::FetchBuilding(Human* hum) { for (auto& pair : uniid_hash_) { if (pair.second->entity_type == ET_Building) { hum->new_objects.insert(pair.second); } } } void Room::OnHumanDie(Human* hum) { --alive_count_; } void Room::ClearDeletedObjects() { for (auto& pair : frame_data.deleted_objects_hash) { for (auto& obj_uniid : pair.second) { Entity* entity = GetEntityByUniId(obj_uniid); if (entity) { if (entity->entity_type != ET_Player) { uniid_hash_.erase(entity->entity_uniid); moveable_hash_.erase(entity->entity_uniid); } for (auto& pair : human_hash_) { pair.second->new_objects.erase(entity); pair.second->part_objects.erase(entity); } delete entity; } } } for (auto& pair : frame_data.softdeleted_objects_hash) { for (auto& obj_uniid : pair.second) { Entity* entity = GetEntityByUniId(obj_uniid); if (entity) { if (entity->entity_type != ET_Player) { uniid_hash_.erase(entity->entity_uniid); moveable_hash_.erase(entity->entity_uniid); } for (auto& pair : human_hash_) { pair.second->new_objects.erase(entity); pair.second->part_objects.erase(entity); } delete entity; } } } } void Room::TouchPlayerList(a8::XParams param, std::function func) { if (!func) { return; } for (auto& pair : accountid_hash_) { if (pair.second) { func(pair.second, param); } } } void Room::TouchHumanList(a8::XParams param, std::function func) { if (!func) { return; } for (auto& pair : human_hash_) { if (pair.second) { func(pair.second, param); } } } void Room::ProcAddedObjects() { if (!be_added_hash_.empty()) { for (auto& pair : be_added_hash_) { uniid_hash_[pair.second->entity_uniid] = pair.second; switch (pair.second->entity_type) { case ET_Player: case ET_Bullet: { moveable_hash_[pair.second->entity_uniid] = pair.second; } break; case ET_Smoke: { for (auto& pair : human_hash_) { pair.second->new_objects.insert(pair.second); } } break; default: abort(); break; } } be_added_hash_.clear(); } } void Room::BeAddedObject(Entity* entity) { be_added_hash_[entity->entity_uniid] = entity; } void Room::UpdateGas() { switch (gas_data.gas_mode) { case GasInactive: { if (frame_no - gas_data.gas_start_frameno >= GAS_INACTIVE_TIME * SERVER_FRAME_RATE) { gas_data.gas_mode = GasWaiting; gas_data.old_area_meta = MetaMgr::Instance()->GetSafeArea(30001); gas_data.new_area_meta = MetaMgr::Instance()->GetSafeArea(30002); gas_data.gas_progress = gas_data.old_area_meta->i->rad(); gas_data.gas_start_frameno = frame_no; gas_data.pos_old = Vector2D(MAP_WIDTH / 2.0f, MAP_HEIGHT / 2.0f); gas_data.pos_old_bk = gas_data.pos_old; { bool gen_ok = GenSmallCircle(gas_data.pos_old, gas_data.old_area_meta->i->rad(), gas_data.new_area_meta->i->rad(), gas_data.pos_new); assert(gen_ok); } gas_data.rad_old = gas_data.old_area_meta->i->rad(); gas_data.rad_new = gas_data.new_area_meta->i->rad(); RoomMgr::Instance()->RemoveFromInactiveRoomHash(room_uuid); } } break; case GasWaiting: { if (frame_no - gas_data.gas_start_frameno >= gas_data.old_area_meta->i->wait_time() * SERVER_FRAME_RATE) { gas_data.gas_mode = GasMoving; gas_data.gas_start_frameno = frame_no;; } } break; case GasMoving: { if (frame_no - gas_data.gas_start_frameno > 0 && !gas_data.is_last_gas) { float distance = gas_data.old_area_meta->i->shrink_speed() * ((frame_no - gas_data.gas_start_frameno) * (1.0 / SERVER_FRAME_RATE)); gas_data.gas_progress = std::max(gas_data.rad_old - distance, gas_data.rad_new); #if 0 { #else if (!(gas_data.pos_new == gas_data.pos_old)) { #endif Vector2D p1 = gas_data.pos_new - gas_data.pos_old_bk; gas_data.pre_pos_old = gas_data.pos_old; if (p1.Norm() - distance <= 0.01f) { gas_data.pos_old = gas_data.pos_new; } else { Vector2D dir = p1; dir.Normalize(); gas_data.pos_old = gas_data.pos_old_bk + dir * distance; } if (gas_data.rad_old - distance <= gas_data.rad_new) { #if 0 assert(gas_data.pos_new == gas_data.pos_old); #endif } } if (std::abs(gas_data.gas_progress - gas_data.rad_new) <= 0.001f) { int pre_area_id = gas_data.new_area_meta->i->id(); Vector2D pre_pos = gas_data.pos_new; if (!MetaMgr::Instance()->GetSafeArea(pre_area_id + 1)) { gas_data.is_last_gas = true; return; } gas_data.gas_mode = GasWaiting; gas_data.old_area_meta = MetaMgr::Instance()->GetSafeArea(pre_area_id); gas_data.new_area_meta = MetaMgr::Instance()->GetSafeArea(pre_area_id + 1); gas_data.gas_progress = gas_data.old_area_meta->i->rad(); gas_data.gas_start_frameno = frame_no; gas_data.pos_old = pre_pos; gas_data.pos_old_bk = gas_data.pos_old; { bool gen_ok = GenSmallCircle(gas_data.pos_old, gas_data.old_area_meta->i->rad(), gas_data.new_area_meta->i->rad(), gas_data.pos_new); assert(gen_ok); } gas_data.rad_old = gas_data.old_area_meta->i->rad(); gas_data.rad_new = gas_data.new_area_meta->i->rad(); } } } break; } if (gas_data.gas_mode != GasInactive) { if (!game_over && alive_count_ <= 1) { game_over = true; game_over_frameno = frame_no; } for (auto& pair : human_hash_) { if (pair.second->dead) { continue; } if (game_over) { pair.second->send_gameover = true; } bool b1 = CircleContainCircle(gas_data.pos_old, gas_data.gas_progress, pair.second->pos, pair.second->GetRadius() ); bool b2 = CircleContainCircle(gas_data.pos_new, gas_data.rad_new, pair.second->pos, pair.second->GetRadius() ); if (!b1 && !b2) { pair.second->poisoning = true; } else { pair.second->poisoning = false; pair.second->poisoning_time = false; } } } } bool Room::GenSmallCircle(Vector2D big_circle_pos, float big_circle_rad, float small_circle_rad, Vector2D& out_pos) { if (big_circle_rad <= small_circle_rad) { abort(); } Vector2D dir = Vector2D::UP; dir.Rotate(a8::RandAngle()); float rad = rand() % (int)(big_circle_rad - small_circle_rad); if (rad <= 0.001f){ rad = 0.001f; } out_pos = big_circle_pos + dir * rad; return true; } void Room::OutputDebugLog() { a8::UdpLog::Instance()->Debug("roomid:%d max_rundelay:%d frame_no:%d game_over:%d game_over_frameno:%d " "elapsed_time:%d alive_count:%d current_uniid:%d " "state:%d accountid_hash.size:%d moveable_hash_.size:%d " "uniid_hash.size:%d human_hash.size:%d be_added_hash:%d", { room_uuid, profile.max_rundelay, frame_no, game_over ? 1 : 0, game_over_frameno, elapsed_time_, alive_count_, current_uniid, state_, accountid_hash_.size(), moveable_hash_.size(), uniid_hash_.size(), human_hash_.size(), be_added_hash_.size() }); { int entity_num_arr[ET_MAX] = {0}; a8::MutableXObject* logobj = a8::MutableXObject::NewObject(); for (auto& pair : uniid_hash_) { if (pair.second->entity_type >= 0 && pair.second->entity_type < ET_MAX) { ++(entity_num_arr[pair.second->entity_type]); } } for (int i = 0; i < ET_MAX; ++i) { logobj->SetVal(a8::XValue(i).GetString(), entity_num_arr[i]); } a8::UdpLog::Instance()->Debug("roomid:%d %s", {room_uuid, logobj->ToJsonStr()}); delete logobj; } profile.max_rundelay = 0; }