408 lines
13 KiB
C++
408 lines
13 KiB
C++
#include "NetworkManager.hpp"
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <chrono>
|
|
|
|
NetworkManager::NetworkManager() {loginSocketworldSocket.open(udp::v4());
|
|
startLoginReceive();
|
|
startWorldReceive
|
|
ioThread = std::thread([this] { ioContext.run(); });
|
|
}
|
|
|
|
NetworkManager::~NetworkManager() {
|
|
disconnect();
|
|
ioContext.stop();
|
|
if (ioThread.joinable()) ioThread.join();
|
|
}
|
|
|
|
bool NetworkManager::connectToLogin(const std::string& server, uint16_t port) {
|
|
try {
|
|
tcp::resolver resolver(ioContext);
|
|
auto endpoints = resolver.resolve(server, std::to_string(port));
|
|
boost::asio::connect(loginSocket, endpoints);
|
|
|
|
loginConnected = true;
|
|
startLoginReceive();
|
|
return true;
|
|
} catch (std::exception& e) {
|
|
std::cerr << "Failed to connect to login server: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool NetworkManager::connectToWorld(const std::string& server, uint16_t port, const std::string& token) {
|
|
try {
|
|
tcp::resolver resolver(ioContext);
|
|
auto endpoints = resolver.resolve(server, std::to_string(port));
|
|
boost::asio::connect(worldSocket, endpoints);
|
|
|
|
// Send authentication
|
|
json authMsg;
|
|
authMsg["type"] = "auth";
|
|
authMsg["token"] = token;
|
|
|
|
std::string msg = authMsg.dump() + "\n";
|
|
boost::asio::write(worldSocket, boost::asio::buffer(msg));
|
|
|
|
// Wait for auth response
|
|
boost::asio::streambuf response;
|
|
boost::asio::read_until(worldSocket, response, '\n');
|
|
|
|
std::istream response_stream(&response);
|
|
std::string response_str;
|
|
std::getline(response_stream, response_str);
|
|
|
|
json authResp = json::parse(response_str);
|
|
if (authResp["success"] == true) {
|
|
worldConnected = true;
|
|
connected = true;
|
|
startWorldReceive();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} catch (std::exception& e) {
|
|
std::cerr << "Failed to connect to world server: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void NetworkManager::startLoginReceive() {
|
|
auto self(shared_from_this());
|
|
boost::asio::async_read_until(loginSocket, loginBuffer, '\n',
|
|
[this, self](boost::system::error_code ec, std::size_t length) {
|
|
if (!ec) {
|
|
std::istream is(&loginBuffer);
|
|
std::string line;
|
|
std::getline(is, line);
|
|
|
|
try {
|
|
json msg = json::parse(line);
|
|
processLoginMessage(msg);
|
|
} catch (json::exception& e) {
|
|
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
|
}
|
|
|
|
if (loginConnected) {
|
|
startLoginReceive();
|
|
}
|
|
void NetworkManager::connectToLoginServer(const std::string& host, uint16_t port) {
|
|
loginEndpoint = udp::endpoint(ip::make_address(host), port);
|
|
std::cout << "Login server endpoint set to " << host << ":" << port << "\n";
|
|
}
|
|
|
|
void NetworkManager::connectToWorldServer(const std::string& host, uint16_t port) {
|
|
worldEndpoint = udp::endpoint(ip::make_address(host), port);
|
|
std::cout << "World server endpoint set to " << host << ":" << port << "\n";
|
|
}
|
|
|
|
void NetworkManager::sendLoginRequest(const std::string& username, const std::string& password) {
|
|
std::vector<uint8_t> msg(2 + username.size() + 1 + password.size());
|
|
msg[0] = static_cast<uint8_t>(MessageType::LoginRequest);
|
|
msg[1] = static_cast<uint8_t>(username.size());
|
|
std::memcpy(&msg[2], username.data(), username.size());
|
|
msg[2 + username.size()] = static_cast<uint8_t>(password.size());
|
|
std::memcpy(&msg[3 + username.size()], password.data(), password.size());
|
|
|
|
loginSocket.send_to(buffer(msg), loginEndpoint);
|
|
loginResponseReceived = false;
|
|
}
|
|
|
|
bool NetworkManager::waitForLoginResponse(LoginResult& result, float timeout) {
|
|
auto start = std::chrono::steady_clock::now();
|
|
auto timeoutDuration = std::chrono::milliseconds(static_cast<int>(timeout * 1000));
|
|
|
|
while (!loginResponseReceived) {
|
|
if (std::chrono::steady_clock::now() - start > timeoutDuration) {
|
|
return false;
|
|
}
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(loginMutex);
|
|
result = lastLoginResult;
|
|
return true;
|
|
}
|
|
|
|
void NetworkManager::sendAuth(const std::array<uint8_t, 32>& token) {
|
|
std::array<uint8_t, 33> msg;
|
|
msg[0] = static_cast<uint8_t>(MessageType::Auth);
|
|
std::memcpy(&msg[1], token.data(), 32);
|
|
|
|
worldSocket.send_to(buffer(msg), worldEndpoint);
|
|
}
|
|
|
|
void NetworkManager::sendMove(float dx, float dy, float dz) {
|
|
if (!connected) return;
|
|
|
|
std::array<uint8_t, 17> msg{};
|
|
msg[0] = static_cast<uint8_t>(MessageType::Move);
|
|
|
|
uint32_t id = playerID;
|
|
std::memcpy(&msg[1], &id, sizeof(id));
|
|
std::memcpy(&msg[5], &dx, sizeof(dx));
|
|
std::memcpy(&msg[9], &dy, sizeof(dy));
|
|
std::memcpy(&msg[13], &dz, sizeof(dz));
|
|
|
|
worldSocket.send_to(buffer(msg), worldEndpoint);
|
|
}
|
|
|
|
void NetworkManager::sendHeartbeat() {
|
|
if (!connected) return;
|
|
|
|
std::array<uint8_t, 5> msg{};
|
|
msg[0] = static_cast<uint8_t>(MessageType::Heartbeat);
|
|
uint32_t id = playerID;
|
|
std::memcpy(&msg[1], &id, sizeof(id));
|
|
worldSocket.send_to(buffer(msg), worldEndpoint);
|
|
}
|
|
|
|
void NetworkManager::sendLogout() {
|
|
if (!connected) return;
|
|
|
|
std::array<uint8_t, 5> msg{};
|
|
msg[0] = static_cast<uint8_t>(MessageType::Logout);
|
|
uint32_t id = playerID;
|
|
std::memcpy(&msg[1], &id, sizeof(id));
|
|
worldSocket.send_to(buffer(msg), worldEndpoint);
|
|
|
|
connected = false;
|
|
authenticated = false;
|
|
playerID = 0;
|
|
|
|
std::lock_guard lock(remotePlayersMutex);
|
|
remotePlayers.clear();
|
|
}
|
|
|
|
void NetworkManager::startLoginReceive() {
|
|
loginSocket.async_receive_from(
|
|
buffer(loginRecvBuffer), loginEndpoint,
|
|
processLoginMessageloginRecvBuffer }
|
|
});
|
|
}
|
|
|
|
void NetworkManager::startWorldReceive() {
|
|
auto self(shared_from_this());
|
|
boost::asio::async_read_until(worldSocket, worldBuffer, '\n',
|
|
[this, self](boost::system::error_code ec, std::size_t length) {
|
|
if (!ec) {
|
|
std::istream is(&worldBuffer);
|
|
std::string line;
|
|
std::getline(is, line);
|
|
|
|
try {
|
|
json msg = json::parse(line);
|
|
processWorldMessage(msg);
|
|
} catch (json::exception& e) {
|
|
std::cerr << "JSON parse error: " << e.what() << std::endl;
|
|
}
|
|
|
|
if (worldConnected) {
|
|
startWorldReceive();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void NetworkManager::processLoginMessage(const json& msg) {
|
|
std::string type = msg["type"];
|
|
|
|
if (type == "loginResponse") {
|
|
if (msg["success"] == true) {
|
|
authToken = msg["token"];
|
|
worldServerUrl = msg["worldUrl"];
|
|
playerID = msg["playerId"];
|
|
loginSuccess = true;
|
|
loginErrorMsg = "";
|
|
} else {
|
|
loginErrorMsg = msg.value("message", "Login failed");
|
|
loginSuccess = false;
|
|
startLoginReceive }
|
|
} else if (type == "registerResponse") {
|
|
if (msg["success"] == false) {
|
|
loginErrorMsg = msg.value("message", "Registration failed");
|
|
void NetworkManager::startWorldReceive() {
|
|
worldSocket.async_receive_from(
|
|
buffer(worldRecvBuffer), worldEndpoint,
|
|
[this](std::error_code ec, std::size_t bytes) {
|
|
if (!ec && bytes > 0) {
|
|
processWorldMessage(worldRecvBuffer.data(), bytes);
|
|
}
|
|
startWorldReceive();
|
|
}
|
|
);
|
|
}
|
|
|
|
void NetworkManager::processLoginMessage(const uint8_t* data, std::size_t size) {
|
|
if (size == 0) return;
|
|
|
|
auto msgType = static_cast<MessageType>(data[0]);
|
|
|
|
switch (msgType) {
|
|
case MessageType::LoginResponse:
|
|
handleLoginResponse(data, size);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NetworkManager::processWorldMessage(const uint8_t* data, std::size_t size) {
|
|
case MessageType::AuthResponse:
|
|
handleAuthResponse(data, size);
|
|
break;
|
|
handleLoginResponse3lock_guard<std::mutex>lockloginMutex) lastLoginResult.success = (data[1] == 1);
|
|
|
|
uint8_t msgLen = data[2];
|
|
if (size >= 3u + msgLen) {
|
|
lastLoginResult.message = std::string(reinterpret_cast<const char*>(&data[3]), msgLen);
|
|
}
|
|
|
|
if (lastLoginResult.success && size >= 3u + msgLen + 32 + 1) {
|
|
// Extract token
|
|
std::memcpy(lastLoginResult.token.data(), &data[3 + msgLen], 32);
|
|
|
|
// Extract world host
|
|
uint8_t hostLen = data[3 + msgLen + 32];
|
|
if (size >= 3u + msgLen + 32 + 1 + hostLen + 2 + 4) {
|
|
lastLoginResult.worldHost = std::string(reinterpret_cast<const char*>(&data[3 + msgLen + 32 + 1]), hostLen);
|
|
|
|
// Extract world port
|
|
std::memcpy(&lastLoginResult.worldPort, &data[3 + msgLen + 32 + 1 + hostLen], 2);
|
|
|
|
// Extract player ID
|
|
std::memcpy(&lastLoginResult.playerID, &data[3 + msgLen + 32 + 1 + hostLen + 2], 4);
|
|
}
|
|
}
|
|
|
|
loginResponseReceived = true;
|
|
|
|
if (lastLoginResult.success) {
|
|
std::cout << "Login successful! World: " << lastLoginResult.worldHost
|
|
<< ":" << lastLoginResult.worldPort << "\n";
|
|
} else {
|
|
std::cout << "Login failed: " << lastLoginResult.message << "\n";
|
|
}
|
|
}
|
|
|
|
void NetworkManager::handleAuthResponse(const uint8_t* data, std::size_t size) {
|
|
if (size < 3) return;
|
|
|
|
bool success = (data[1] == 1);
|
|
uint8_t msgLen = data[2];
|
|
|
|
if (size >= 3u + msgLen) {
|
|
std::string message(reinterpret_cast<const char*>(&data[3]), msgLen);
|
|
|
|
if (success) {
|
|
authenticated = true;
|
|
std::cout << "World authentication successful\n";
|
|
} else {
|
|
authenticated = false;
|
|
std::cout << "World authentication failed: " << message << "\n";
|
|
}
|
|
}
|
|
17Spawnedat(" << x << ",y, " << z << ") }
|
|
}
|
|
}
|
|
|
|
void NetworkManager::processWorldMessage(const json& msg) {
|
|
std::string type = msg["type"];
|
|
|
|
if (type == "init") {
|
|
playerID = msg["playerId"];
|
|
serverPosition = {msg["x"], msg["y"], msg["z"]};
|
|
serverTimeOfDay = msg["timeOfDay"];
|
|
connected = true;
|
|
std::cout << "Connected to world as player " << playerID << std::endl;
|
|
|
|
} else if (type == "playerMovement") {
|
|
uint32_t id = msg["playerId"];
|
|
if (id != playerID) {
|
|
std::lock_guard lock(remotePlayersMutex);
|
|
if (remotePlayers.find(id) != remotePlayers.end()) {
|
|
remotePlayers[id].position = {msg["x"], msg["y"], msg["z"]};
|
|
remotePlayers[id].lastUpdate = GetTime();
|
|
}
|
|
}
|
|
|
|
} else if (type == "playerJoined") {
|
|
uint32_t id = msg["playerId"];
|
|
if (id != playerID) {
|
|
std::lock_guard lock(remotePlayersMutex);
|
|
remotePlayers[id] = {
|
|
id,
|
|
{msg["x"], msg["y"], msg["z"]},
|
|
msg.value("username", "Player"),
|
|
static_cast<float>(GetTime())
|
|
};
|
|
std::cout << "Player " << msg["username"] << " joined" << std::endl;
|
|
}
|
|
|
|
} else if (type == "playerLeft") {
|
|
uint32_t id = msg["playerId"];
|
|
usernameLenusernamePlayerusernameLenusernameusernameLen std::lock_guard lock(remotePlayersMutex);
|
|
remotePlayers.erase(id);
|
|
|
|
} else if (type == "positionCorrection") {
|
|
serverPosition = {msg["x"], msg["y"], msg["z"]};
|
|
|
|
} else if (type == "timeUpdate") {
|
|
serverTimeOfDay = msg["timeOfDay"];
|
|
|
|
} else if (type == "chat") {
|
|
std::cout << "[" << msg["username"] << "]: " << msg["message"] << std::endl;
|
|
usernameusername << " (ID: " << ) }
|
|
}
|
|
|
|
void NetworkManager::sendLogin(const std::string& username, const std::string& password) {
|
|
if (!loginConnected) return;
|
|
|
|
json msg;
|
|
msg["type"] = "login";
|
|
msg["username"] = username;
|
|
msg["password"] = password;
|
|
|
|
std::string data = msg.dump() + "\n";
|
|
boost::asio::async_write(loginSocket, boost::asio::buffer(data),
|
|
[](boost::system::error_code ec, std::size_t) {
|
|
if (ec) std::cerr << "Send login error: " << ec.message() << std::endl;
|
|
});
|
|
}
|
|
|
|
void NetworkManager::sendMovement(const Vector3& position, float yaw, float pitch, const Vector3& velocity) {
|
|
if (!worldConnected) return;
|
|
|
|
json msg;
|
|
msg["type"] = "movement";
|
|
msg["x"] = position.x;
|
|
msg["y"] = position.y;
|
|
msg["z"] = position.z;
|
|
msg["yaw"] = yaw;
|
|
msg["pitch"] = pitch;
|
|
msg["velX"] = velocity.x;
|
|
msg["velY"] = velocity.y;
|
|
msg["velZ"] = velocity.z;
|
|
|
|
std::string data = msg.dump() + "\n";
|
|
boost::asio::async_write(worldSocket, boost::asio::buffer(data),
|
|
[](boost::system::error_code ec, std::size_t) {
|
|
if (ec) std::cerr << "Send movement error: " << ec.message() << std::endl;
|
|
});
|
|
}
|
|
void NetworkManager::handleTimeSync(const uint8_t* data, std::size_t size) {
|
|
if (size < 5) return;
|
|
float timeOfDay;
|
|
memcpy&timeOfDay,&data[1],sizeof(timeOfDay)) serverTimeOfDay.store(timeOfDay);
|
|
}
|
|
|
|
Vector3 NetworkManager::getPosition() {
|
|
std::lock_guard lock(positionMutex);
|
|
return serverPosition;
|
|
}
|
|
|
|
std::unordered_map<uint32_t, RemotePlayer> NetworkManager::getRemotePlayers() {
|
|
std::lock_guard lock(remotePlayersMutex);
|
|
return remotePlayers;
|
|
}}
|