#include "ClientWorldController.hpp" #include #include #include #include #include "network/ServerConnection.hpp" #include "Entity.pb.h" #include "Login.pb.h" #include "WorldState.pb.h" #include "PlayerMove.pb.h" #include "entt/entity/entity.hpp" #include "entt/entity/fwd.hpp" #include "messages/PlayerMoveMessage.hpp" #include "metrics/HistoryBuffer.hpp" #include "world/CharacterBody.hpp" #include "world/CharacterController.hpp" #include "world/JoltPhysicsWorld.hpp" #include "world/WorldEntity.hpp" #include "tw/serial/WorldStateWriter.hpp" #include "network/EntityInterpolation.hpp" namespace tw { entt::entity ClientWorldController::create_entity(const std::string& name, glm::vec3 position) { const auto entity = m_world->registry().create(); if(!m_mesh.has_value()) { m_mesh = m_world_renderer->add_mesh(drw::MeshData::cube(glm::vec3(1.0f))); } spdlog::info("Creating entity {}", name); m_world->registry() .emplace(entity, Transform(position)); m_world->registry() .emplace(entity, tw::WorldEntity(name, (uint32_t)entity)); m_world->registry() .emplace(entity, m_mesh.value()); return entity; } // void ClientWorldController::apply_entity_positions(const mmo::EntityPosition* const* positions, size_t count) { // for(int i = 0; i < count; i++) { // const mmo::EntityPosition* const position = positions[i]; // std::optional entity = m_entity_id_map.get_local(position->id()); // // if(!entity.has_value()) { // spdlog::warn("Received position update for unknown entity {}", position->id()); // continue; // } // // glm::vec3 p = {position->x(), position->y(), position->z()}; // m_entity_interpolator.add_position_for_entity(entity.value(), p); // // EntityPositionHistory* history = m_world->registry().try_get(entity.value()); // // if(history != nullptr) { // auto millis = std::chrono::duration_cast(Clock::now().time_since_epoch()).count(); // history->set(millis, p); // } // } // } void ClientWorldController::spawn_entity(const std::string& name, uint32_t server_id) { auto entity = create_entity(name, glm::vec3()); spdlog::info("Spawning entity {}", server_id); m_entity_id_map.register_server_to_local(server_id, entity); if(m_controlled_server_id.has_value() && m_controlled_server_id.value() == server_id) { try_bind_player_entity(); } else { m_interpolator.register_entity(entity); } } ClientWorldController::ClientWorldController( const io::InputManager* inputs, World* world, JoltPhysicsWorld* physics_world, drw::WorldRenderer* world_renderer, net::ServerConnection* connection, dbg::NetworkMetrics* network_metrics ) : m_input_manager(inputs), m_world(world), m_physics_world(physics_world), m_world_renderer(world_renderer), m_player_controller(&world_renderer->camera(), glm::vec3()), m_connection(connection), m_messages(connection->endpoint()), m_network_metrics(network_metrics), m_input_update_tick_step(20), m_interpolator(&m_world->registry(), 300), m_replicator_client(&m_messages, &m_entity_id_map, m_network_metrics, m_physics_world, m_world, m_connection, &m_interpolator) { m_messages.set_handler( [this](msg::PeerId, const mmo::LoginResponse& mesg) { spdlog::info("Logged in!"); }); m_messages.set_handler( [this](msg::PeerId, const mmo::SetControlledEntity& mesg) { spdlog::info("Setting controlled entity from server id {}", mesg.entity_id()); m_controlled_server_id = mesg.entity_id(); try_bind_player_entity(); }); m_connection->endpoint()->set_handler(Message::value, [this](msg::PeerId, std::span data) { serial::WorldStateReader reader(data); m_replicator_client.handle_snapshot(m_network_frame_idx, reader); }); m_messages.set_handler( [this](msg::PeerId, const mmo::EntitySpawnMessage& mesg) { for(auto& spawn : mesg.spawns()) { spawn_entity(spawn.name(), spawn.entity_id()); } }); } ClientWorldController::~ClientWorldController() { } void ClientWorldController::try_bind_player_entity() { if(!m_controlled_server_id.has_value()) { return; } auto local_entity = m_entity_id_map.get_local(m_controlled_server_id.value()); if(!local_entity.has_value()) { return; } if(m_player_entity.has_value() && m_player_entity.value() == local_entity.value()) { return; } entt::entity entity = local_entity.value(); spdlog::info("Binding player entity"); m_player_entity = entity; m_replicator_client.set_player_entity(entity); Transform* transform = m_world->registry().try_get(entity); glm::vec3 position = transform ? transform->position() : glm::vec3(0.0f); m_world->registry().emplace(entity, 20.0f); m_world->registry().emplace(entity, m_physics_world->create_character( new JPH::BoxShape(JPH::Vec3Arg(0.5f, 0.5f, 0.5f)), position )); if(m_world->registry().all_of(entity)) { m_world->registry().remove(entity); } } void ClientWorldController::update_network() { auto network_start = Clock::now(); // collects incoming messages m_connection->update(); m_network_metrics->record_update_time(Clock::now() - network_start); m_network_metrics->sample({ .bytes_sent = m_connection->endpoint()->bytes_sent(), .bytes_received = m_connection->endpoint()->bytes_received(), .messages_sent = m_connection->endpoint()->messages_sent(), .messages_received = m_connection->endpoint()->messages_received() }); // m_world->registry().view() // .each([&](const auto e, Transform& t) { // m_position_history_exporter.write((uint32_t)e, // std::chrono::duration_cast(Clock::now().time_since_epoch()).count(), t.position()); // }); } void ClientWorldController::update(double delta_time) { // collects messages from network for processing update_network(); m_player_controller.update(m_input_manager, delta_time); // collect input every tick of the input_update_tick_step if(m_input_update_tick_step.has_ticked()) { // TODO: might be more like `calc_velocity_vector`. // 1. it is not exactly *input* but input mapped to normalized velocity vector // 2. I hate calling methods `update` glm::vec3 input = m_player_controller.input(); if(m_player_entity.has_value()) { CharacterController* controller = m_world->registry().try_get(m_player_entity.value()); if(controller) { // controller->set_input(m_network_frame_idx, input); m_replicator_client.set_input(m_network_frame_idx, input); m_replicator_client.record_prediction(m_network_frame_idx); } } m_physics_world->step(m_network_frame_idx, JoltPhysicsWorld::FIXED_DELTA_TIME, true); m_network_frame_idx++; } m_interpolator.interpolate_smoothed_entities(std::chrono::high_resolution_clock::now() - std::chrono::milliseconds(500)); if(m_player_entity.has_value()) { // after interpolation happen, follow the target // TODO: The third person controller could pull Transform* player_transform = m_world->registry().try_get(m_player_entity.value()); if(player_transform) { // m_player_controller.set_target(player_transform->position()); } } m_frame_idx++; // m_replicator_client.update(); } }