Major networking refactoring to allow for snapshot filtering outside of netcode
This commit is contained in:
@@ -3,7 +3,7 @@ project(TacticalZ-Engine)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(GLEW REQUIRED)
|
||||
find_package(GLFW REQUIRED)
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono)
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono program_options)
|
||||
find_package(assimp REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
@@ -96,14 +96,18 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
auto attributeDecl = attributeUse->getAttrDeclaration();
|
||||
std::string name = XS::ToString(attributeDecl->getName());
|
||||
|
||||
// Read network replication flag
|
||||
if (name == "replicated") {
|
||||
// HACK: This should never happen since patched Xerces. Run deploy to get the updated DLL.
|
||||
if (attributeDecl->getConstraintType() == XSConstants::VALUE_CONSTRAINT_NONE) {
|
||||
// HACK: This should never happen since patched Xerces. Run deploy to get the updated DLL.
|
||||
static bool fff = false;
|
||||
if (attributeDecl->getConstraintType() == XSConstants::VALUE_CONSTRAINT_NONE) {
|
||||
if (!fff) {
|
||||
system("explorer https://imon.nu/deploy.html");
|
||||
continue;
|
||||
fff = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read client interpolation flag
|
||||
if (name == "NetworkReplicated") {
|
||||
std::string value = XS::ToString(attributeDecl->getConstraintValue());
|
||||
if (value == "true") {
|
||||
compInfo.Meta->NetworkReplicated = true;
|
||||
|
||||
@@ -100,9 +100,9 @@ void EditorSystem::Enable()
|
||||
}
|
||||
|
||||
// Pause the world we're editing
|
||||
Events::Pause ePause;
|
||||
ePause.World = m_World;
|
||||
m_EventBroker->Publish(ePause);
|
||||
//Events::Pause ePause;
|
||||
//ePause.World = m_World;
|
||||
//m_EventBroker->Publish(ePause);
|
||||
|
||||
m_Enabled = true;
|
||||
}
|
||||
|
||||
@@ -2,40 +2,50 @@
|
||||
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
|
||||
Client::Client(ConfigFile* config) : m_Socket(m_IOService)
|
||||
Client::Client(World* world, EventBroker* eventBroker)
|
||||
: Network(world, eventBroker)
|
||||
, m_Socket(m_IOService)
|
||||
{
|
||||
Network::initialize();
|
||||
|
||||
// Asumes root node is EntityID_Invalid
|
||||
insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid);
|
||||
// Init timer
|
||||
m_TimeSinceSentInputs = std::clock();
|
||||
// Default is local host
|
||||
std::string address = config->Get<std::string>("Networking.Address", "127.0.0.1");
|
||||
int port = config->Get<int>("Networking.Port", 27666);
|
||||
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
|
||||
// Set up network stream
|
||||
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_PlayerName = config->Get<std::string>("Networking.Name", "Raptorcopter");
|
||||
m_SendInputIntervalMs = config->Get<int>("Networking.SendInputIntervalMs", 33);
|
||||
|
||||
LOG_INFO("Client initialized");
|
||||
}
|
||||
|
||||
Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr<SnapshotFilter> snapshotFilter)
|
||||
: Client(world, eventBroker)
|
||||
{
|
||||
m_SnapshotFilter = std::move(snapshotFilter);
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{ }
|
||||
|
||||
void Client::Start(World* world, EventBroker* eventBroker)
|
||||
void Client::Connect(std::string address, int port)
|
||||
{
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
|
||||
// Subscribe to events
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Client::OnPlayerDamage);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Client::OnPlayerSpawned);
|
||||
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
if (address.empty()) {
|
||||
address = config->Get<std::string>("Networking.Address", "127.0.0.1");
|
||||
}
|
||||
if (port == 0) {
|
||||
port = config->Get<int>("Networking.Port", 27666);
|
||||
}
|
||||
|
||||
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
|
||||
LOG_INFO("Client connecting...");
|
||||
m_Socket.connect(m_ReceiverEndpoint);
|
||||
LOG_INFO("I am client. BIP BOP");
|
||||
connect();
|
||||
}
|
||||
|
||||
void Client::Update()
|
||||
@@ -49,6 +59,7 @@ void Client::Update()
|
||||
sendInputCommands();
|
||||
m_TimeSinceSentInputs = std::clock();
|
||||
}
|
||||
// HACK: Send absolute player positions for now to avoid desync until we have reliable messages
|
||||
sendLocalPlayerTransform();
|
||||
}
|
||||
Network::Update();
|
||||
@@ -173,34 +184,46 @@ void Client::parseComponentDeletion(Packet & packet)
|
||||
}
|
||||
}
|
||||
|
||||
// Fields with strings will not work right now
|
||||
void Client::InterpolateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
|
||||
{
|
||||
int sizeOfFields = 0;
|
||||
for (auto field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
sizeOfFields += fieldInfo.Stride;
|
||||
}
|
||||
// Is the size correct?
|
||||
boost::shared_array<char> eventData(new char[componentInfo.Stride]);
|
||||
memcpy(eventData.get(), packet.ReadData(componentInfo.Stride), componentInfo.Stride);
|
||||
//Send event to interpolat system
|
||||
Events::Interpolate e;
|
||||
e.Entity = entityID;
|
||||
e.DataArray = eventData;
|
||||
m_EventBroker->Publish(e);
|
||||
|
||||
}
|
||||
|
||||
void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
|
||||
void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID)
|
||||
{
|
||||
for (auto field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
if (fieldInfo.Type == "string") {
|
||||
std::string& value = packet.ReadString();
|
||||
m_World->GetComponent(entityID, componentType)[fieldInfo.Name] = value;
|
||||
m_World->GetComponent(entityID, componentInfo.Name)[fieldInfo.Name] = value;
|
||||
} else {
|
||||
memcpy(m_World->GetComponent(entityID, componentType).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
|
||||
memcpy(m_World->GetComponent(entityID, componentInfo.Name).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedComponentWrapper Client::createSharedComponent(Packet& packet, EntityID entityID, const ComponentInfo& componentInfo)
|
||||
{
|
||||
// Create shared allocation
|
||||
char* data = new char[sizeof(EntityID) + componentInfo.Stride];
|
||||
// Copy entity ID to start of data buffer
|
||||
memcpy(data, &entityID, sizeof(EntityID));
|
||||
// Read and copy fields
|
||||
for (auto& field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
if (fieldInfo.Type == "string") {
|
||||
new (data + sizeof(EntityID) + fieldInfo.Offset) std::string(packet.ReadString());
|
||||
} else {
|
||||
memcpy(data + sizeof(EntityID) + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
|
||||
return SharedComponentWrapper(componentInfo, boost::shared_array<char>(data));
|
||||
}
|
||||
|
||||
void Client::ignoreFields(Packet& packet, const ComponentInfo& componentInfo)
|
||||
{
|
||||
for (auto field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
if (fieldInfo.Type == "string") {
|
||||
packet.ReadString();
|
||||
} else {
|
||||
packet.ReadData(fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,26 +237,32 @@ void Client::parseSnapshot(Packet& packet)
|
||||
int ammountOfComponents = packet.ReadPrimitive<int>();
|
||||
for (int i = 0; i < ammountOfComponents; i++) {
|
||||
std::string componentType = packet.ReadString();
|
||||
ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
|
||||
const ComponentInfo& componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
|
||||
if (serverClientMapsHasEntity(serverEntityID)) {
|
||||
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
|
||||
EntityWrapper localEntity(m_World, localEntityID);
|
||||
|
||||
// Update entity
|
||||
if (m_World->HasComponent(localEntityID, componentType)) {
|
||||
// Update component
|
||||
if (componentType == "Transform") {
|
||||
// Interpolate only transform components
|
||||
InterpolateFields(packet, componentInfo, localEntityID, componentType);
|
||||
} else if (componentType == "Physics" && m_World->HasComponent(localEntityID, "Player")) {
|
||||
// HACK: Ignore velocity of physics
|
||||
packet.ReadData(componentInfo.Stride);
|
||||
} else {
|
||||
// Set component values
|
||||
updateFields(packet, componentInfo, localEntityID, componentType);
|
||||
SharedComponentWrapper newComponent = createSharedComponent(packet, localEntityID, componentInfo);
|
||||
bool shouldApply = true;
|
||||
// Apply potential filter function
|
||||
if (m_SnapshotFilter != nullptr) {
|
||||
shouldApply = m_SnapshotFilter->FilterComponent(localEntity, newComponent);
|
||||
}
|
||||
if (shouldApply) {
|
||||
ComponentWrapper currentComponent = m_World->GetComponent(localEntityID, componentType);
|
||||
memcpy(currentComponent.Data, newComponent.Data, componentInfo.Stride);
|
||||
}
|
||||
//if (localEntity != m_LocalPlayer && !localEntity.IsChildOf(m_LocalPlayer)) {
|
||||
// updateFields(packet, componentInfo, localEntityID);
|
||||
//} else {
|
||||
// ignoreFields(packet, componentInfo);
|
||||
//}
|
||||
} else {
|
||||
// Has entity but no component
|
||||
m_World->AttachComponent(localEntityID, componentType);
|
||||
updateFields(packet, componentInfo, localEntityID, componentType);
|
||||
updateFields(packet, componentInfo, localEntityID);
|
||||
}
|
||||
} else {
|
||||
// Create Entity and component
|
||||
@@ -246,7 +275,7 @@ void Client::parseSnapshot(Packet& packet)
|
||||
m_World->SetName(newLocalEntityID, serverEntityName);
|
||||
insertIntoServerClientMaps(serverEntityID, newLocalEntityID);
|
||||
m_World->AttachComponent(newLocalEntityID, componentType);
|
||||
updateFields(packet, componentInfo, newLocalEntityID, componentType);
|
||||
updateFields(packet, componentInfo, newLocalEntityID);
|
||||
}
|
||||
}
|
||||
// Parent logic
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
#include "Network/Network.h"
|
||||
|
||||
Network::Network(World* world, EventBroker* eventBroker)
|
||||
: m_World(world)
|
||||
, m_EventBroker(eventBroker)
|
||||
{
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_MaxConnections = config->Get<int>("Networking.MaxConnections", 8);
|
||||
m_TimeoutMs = config->Get<int>("Networking.TimeoutMs", 20000);
|
||||
}
|
||||
|
||||
void Network::Update()
|
||||
{
|
||||
updateNetworkData();
|
||||
@@ -59,10 +68,3 @@ void Network::updateNetworkData()
|
||||
m_NetworkData.DataReceivedThisInterval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Network::initialize()
|
||||
{
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_MaxConnections = config->Get<int>("Networking.MaxConnections", 8);
|
||||
m_TimeoutMs = config->Get<int>("Networking.TimeoutMs", 20000);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
#include "Network/Server.h"
|
||||
|
||||
Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666))
|
||||
Server::Server(World* world, EventBroker* eventBroker, int port)
|
||||
: Network(world, eventBroker)
|
||||
{
|
||||
Network::initialize();
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
snapshotInterval = 1000 * config->Get<float>("Networking.SnapshotInterval", 0.05f);
|
||||
pingIntervalMs = config->Get<float>("Networking.PingIntervalMs", 1000);
|
||||
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Server::Start(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
m_World = world;
|
||||
m_EventBroker = eventBroker;
|
||||
// Subscribe to events
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted);
|
||||
LOG_INFO("I am Server. BIP BOP\n");
|
||||
|
||||
// Bind
|
||||
if (port == 0) {
|
||||
port = config->Get<float>("Networking.Port", 27666);
|
||||
}
|
||||
m_Port = port;
|
||||
m_Socket = std::make_unique<boost::asio::ip::udp::socket>(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port));
|
||||
LOG_INFO("Server initialized and bound to port %i", port);
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Server::Update()
|
||||
@@ -38,7 +39,7 @@ void Server::Update()
|
||||
|
||||
void Server::readFromClients()
|
||||
{
|
||||
while (m_Socket.available()) {
|
||||
while (m_Socket->available()) {
|
||||
try {
|
||||
bytesRead = receive(readBuffer);
|
||||
Packet packet(readBuffer, bytesRead);
|
||||
@@ -105,7 +106,7 @@ void Server::parseMessageType(Packet& packet)
|
||||
|
||||
size_t Server::receive(char * data)
|
||||
{
|
||||
size_t length = m_Socket.receive_from(
|
||||
size_t length = m_Socket->receive_from(
|
||||
boost::asio::buffer((void*)data
|
||||
, INPUTSIZE)
|
||||
, m_ReceiverEndpoint, 0);
|
||||
@@ -121,7 +122,7 @@ size_t Server::receive(char * data)
|
||||
void Server::send(PlayerID player, Packet& packet)
|
||||
{
|
||||
try {
|
||||
size_t bytesSent = m_Socket.send_to(
|
||||
size_t bytesSent = m_Socket->send_to(
|
||||
boost::asio::buffer(packet.Data(), packet.Size()),
|
||||
m_ConnectedPlayers[player].Endpoint,
|
||||
0);
|
||||
@@ -139,7 +140,7 @@ void Server::send(PlayerID player, Packet& packet)
|
||||
|
||||
void Server::send(Packet & packet)
|
||||
{
|
||||
m_Socket.send_to(
|
||||
m_Socket->send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
@@ -240,7 +241,7 @@ void Server::checkForTimeOuts()
|
||||
static_cast<double>(CLOCKS_PER_SEC);
|
||||
if (startPing > stopPing + m_TimeoutMs) {
|
||||
LOG_INFO("User %i timed out!", i);
|
||||
disconnect(i);
|
||||
//disconnect(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ void Renderer::InitializeWindow()
|
||||
ss << " DEBUG";
|
||||
#endif
|
||||
LOG_INFO(ss.str().c_str());
|
||||
glfwSetWindowTitle(m_Window, ss.str().c_str());
|
||||
SetWindowTitle(ss.str());
|
||||
|
||||
// Initialize GLEW
|
||||
if (glewInit() != GLEW_OK) {
|
||||
|
||||
Reference in New Issue
Block a user