#1 - ClientWorldController refactoring
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
#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 {
|
||||
|
||||
typedef HistoryBuffer<long, glm::vec3> EntityPositionHistory;
|
||||
|
||||
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_replicator_client.record_prediction(m_frame_idx);
|
||||
|
||||
|
||||
// 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_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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user