241 lines
8.1 KiB
C++
241 lines
8.1 KiB
C++
#include "ClientWorldController.hpp"
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <chrono>
|
|
#include <spdlog/spdlog.h>
|
|
#include <span>
|
|
|
|
#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<Transform>(entity,
|
|
Transform(position));
|
|
|
|
m_world->registry()
|
|
.emplace<tw::WorldEntity>(entity,
|
|
tw::WorldEntity(name, (uint32_t)entity));
|
|
|
|
m_world->registry()
|
|
.emplace<tw::drw::Mesh>(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<entt::entity> 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<EntityPositionHistory>(entity.value());
|
|
//
|
|
// if(history != nullptr) {
|
|
// auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(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<mmo::LoginResponse>(
|
|
[this](msg::PeerId, const mmo::LoginResponse& mesg) {
|
|
spdlog::info("Logged in!");
|
|
});
|
|
|
|
m_messages.set_handler<mmo::SetControlledEntity>(
|
|
[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<mmo::WorldStateMessage>::value,
|
|
[this](msg::PeerId, std::span<const std::byte> data) {
|
|
|
|
serial::WorldStateReader reader(data);
|
|
|
|
m_replicator_client.handle_snapshot(m_network_frame_idx, reader);
|
|
});
|
|
|
|
m_messages.set_handler<mmo::EntitySpawnMessage>(
|
|
[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<Transform>(entity);
|
|
glm::vec3 position = transform ? transform->position() : glm::vec3(0.0f);
|
|
|
|
m_world->registry().emplace<CharacterController>(entity, 20.0f);
|
|
m_world->registry().emplace<CharacterBody>(entity, m_physics_world->create_character(
|
|
new JPH::BoxShape(JPH::Vec3Arg(0.5f, 0.5f, 0.5f)),
|
|
position
|
|
));
|
|
|
|
if(m_world->registry().all_of<net::EntityPositionInterpolation>(entity)) {
|
|
m_world->registry().remove<net::EntityPositionInterpolation>(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<Transform>()
|
|
// .each([&](const auto e, Transform& t) {
|
|
// m_position_history_exporter.write((uint32_t)e,
|
|
// std::chrono::duration_cast<std::chrono::milliseconds>(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<CharacterController>(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<Transform>(m_player_entity.value());
|
|
if(player_transform) {
|
|
// m_player_controller.set_target(player_transform->position());
|
|
}
|
|
}
|
|
|
|
m_frame_idx++;
|
|
|
|
// m_replicator_client.update();
|
|
}
|
|
|
|
}
|