1
0
game/client/net/NetworkManager.cpp

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;
}}