#include "SimUser.h" #include "RakPeerInterface.h" #include "RakNetworkFactory.h" #include "BitStream.h" #include "MessageIdentifiers.h" #include "Logger.h" #include "dNetCommon.h" #include "Game.h" #include #include #ifdef _WIN32 #include #else #include #endif SimUser::SimUser(uint32_t id, const std::string& username, const std::string& password) : m_ID(id) , m_Username(username) , m_Password(password) , m_State(SimUserState::DISCONNECTED) , m_AuthPeer(nullptr) , m_WorldPeer(nullptr) , m_CharacterID(LWOOBJID_EMPTY) , m_WorldInstanceID(0) , m_ZoneID(1000) // Default to Venture Explorer , m_Position(NiPoint3::ZERO) , m_Rotation(NiQuaternion::IDENTITY) , m_Velocity(NiPoint3::ZERO) , m_TargetPosition(NiPoint3::ZERO) , m_IsMoving(false) , m_PacketsSent(0) , m_PacketsReceived(0) , m_BytesSent(0) , m_BytesReceived(0) , m_HasError(false) , m_RandomEngine(std::random_device{}()) , m_RandomFloat(0.0f, 1.0f) { m_LastUpdate = std::chrono::steady_clock::now(); m_LastHeartbeat = std::chrono::steady_clock::now(); m_LastRandomAction = std::chrono::steady_clock::now(); } SimUser::~SimUser() { Disconnect(); } bool SimUser::ConnectToAuth(const std::string& authIP, uint16_t authPort) { if (m_State != SimUserState::DISCONNECTED) { SetError("Cannot connect to auth server: already connected or in error state"); return false; } m_AuthPeer = RakNetworkFactory::GetRakPeerInterface(); if (!m_AuthPeer) { SetError("Failed to create RakPeer interface for auth connection"); return false; } SocketDescriptor socketDescriptor(0, nullptr); if (m_AuthPeer->Startup(1, 30, &socketDescriptor, 1) != RAKNET_STARTED) { SetError("Failed to startup auth peer"); RakNetworkFactory::DestroyRakPeerInterface(m_AuthPeer); m_AuthPeer = nullptr; return false; } if (m_AuthPeer->Connect(authIP.c_str(), authPort, nullptr, 0) != CONNECTION_ATTEMPT_STARTED) { SetError("Failed to start connection attempt to auth server"); m_AuthPeer->Shutdown(0); RakNetworkFactory::DestroyRakPeerInterface(m_AuthPeer); m_AuthPeer = nullptr; return false; } m_AuthServerAddr = SystemAddress(authIP.c_str(), authPort); SetState(SimUserState::CONNECTING_TO_AUTH); LogMessage("Connecting to auth server at " + authIP + ":" + std::to_string(authPort)); return true; } bool SimUser::ConnectToWorld(const std::string& worldIP, uint16_t worldPort) { if (m_State != SimUserState::AUTHENTICATING && m_State != SimUserState::CONNECTING_TO_WORLD) { SetError("Cannot connect to world server: not in correct state"); return false; } m_WorldPeer = RakNetworkFactory::GetRakPeerInterface(); if (!m_WorldPeer) { SetError("Failed to create RakPeer interface for world connection"); return false; } SocketDescriptor socketDescriptor(0, nullptr); if (m_WorldPeer->Startup(1, 30, &socketDescriptor, 1) != RAKNET_STARTED) { SetError("Failed to startup world peer"); RakNetworkFactory::DestroyRakPeerInterface(m_WorldPeer); m_WorldPeer = nullptr; return false; } if (m_WorldPeer->Connect(worldIP.c_str(), worldPort, nullptr, 0) != CONNECTION_ATTEMPT_STARTED) { SetError("Failed to start connection attempt to world server"); m_WorldPeer->Shutdown(0); RakNetworkFactory::DestroyRakPeerInterface(m_WorldPeer); m_WorldPeer = nullptr; return false; } m_WorldServerAddr = SystemAddress(worldIP.c_str(), worldPort); SetState(SimUserState::CONNECTING_TO_WORLD); LogMessage("Connecting to world server at " + worldIP + ":" + std::to_string(worldPort)); return true; } void SimUser::Disconnect() { if (m_AuthPeer) { m_AuthPeer->Shutdown(100); RakNetworkFactory::DestroyRakPeerInterface(m_AuthPeer); m_AuthPeer = nullptr; } if (m_WorldPeer) { m_WorldPeer->Shutdown(100); RakNetworkFactory::DestroyRakPeerInterface(m_WorldPeer); m_WorldPeer = nullptr; } SetState(SimUserState::DISCONNECTED); } void SimUser::ProcessIncomingPackets() { // Process auth server packets if (m_AuthPeer) { Packet* packet = nullptr; while ((packet = m_AuthPeer->Receive()) != nullptr) { m_PacketsReceived++; m_BytesReceived += packet->length; HandleAuthPacket(packet); m_AuthPeer->DeallocatePacket(packet); } } // Process world server packets if (m_WorldPeer) { Packet* packet = nullptr; while ((packet = m_WorldPeer->Receive()) != nullptr) { m_PacketsReceived++; m_BytesReceived += packet->length; HandleWorldPacket(packet); m_WorldPeer->DeallocatePacket(packet); } } } void SimUser::SendHeartbeat() { auto now = std::chrono::steady_clock::now(); auto timeSinceLastHeartbeat = std::chrono::duration_cast(now - m_LastHeartbeat); if (timeSinceLastHeartbeat.count() >= 5000) { // Send heartbeat every 5 seconds // Send to appropriate server based on current state RakNet::BitStream heartbeat; heartbeat.Write(static_cast(ID_USER_PACKET_ENUM + 1)); // Simple heartbeat if (m_State == SimUserState::IN_WORLD && m_WorldPeer) { m_WorldPeer->Send(&heartbeat, HIGH_PRIORITY, RELIABLE, 0, m_WorldServerAddr, false); m_PacketsSent++; m_BytesSent += heartbeat.GetNumberOfBytesUsed(); } m_LastHeartbeat = now; } } void SimUser::Update(float deltaTime) { ProcessIncomingPackets(); SendHeartbeat(); auto now = std::chrono::steady_clock::now(); auto timeSinceLastAction = std::chrono::duration_cast(now - m_LastRandomAction); // Perform random actions based on current state if (m_State == SimUserState::IN_WORLD && timeSinceLastAction.count() >= 5000) { SimulateRandomAction(); m_LastRandomAction = now; } // Update movement simulation if (m_IsMoving) { SimulateMovement(); } } void SimUser::SimulateMovement() { // Simple movement simulation - move towards target position const float moveSpeed = 5.0f; // units per second const float deltaTime = 0.016f; // Assuming 60 FPS NiPoint3 direction = m_TargetPosition - m_Position; float distance = direction.Length(); if (distance > 0.1f) { direction = direction / distance; // Normalize NiPoint3 movement = direction * moveSpeed * deltaTime; if (movement.Length() >= distance) { m_Position = m_TargetPosition; m_IsMoving = false; } else { m_Position = m_Position + movement; } SendPositionUpdate(); } else { m_IsMoving = false; } } void SimUser::SimulateRandomAction() { if (m_State != SimUserState::IN_WORLD) return; float action = m_RandomFloat(m_RandomEngine); if (action < 0.6f) { // Generate random movement GenerateRandomMovement(); } else if (action < 0.8f) { // Send a chat message (if enabled) SendChatMessage("Hello from simulated client " + std::to_string(m_ID)); } else { // Just idle LogMessage("Idling..."); } } void SimUser::SendChatMessage(const std::string& message) { if (m_State != SimUserState::IN_WORLD || !m_WorldPeer) return; // Create a simple chat message RakNet::BitStream chatBitStream; chatBitStream.Write(static_cast(ID_USER_PACKET_ENUM + 2)); chatBitStream.Write(static_cast(0)); // General chat channel chatBitStream.Write(static_cast(0)); // Unknown // Convert message to UTF-16 std::u16string u16message; for (char c : message) { u16message.push_back(static_cast(c)); } chatBitStream.Write(static_cast(u16message.length())); for (char16_t c : u16message) { chatBitStream.Write(c); } m_WorldPeer->Send(&chatBitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_WorldServerAddr, false); m_PacketsSent++; m_BytesSent += chatBitStream.GetNumberOfBytesUsed(); LogMessage("Sent chat message: " + message); } void SimUser::GenerateRandomMovement() { // Generate a random target position within a reasonable area float randomX = (m_RandomFloat(m_RandomEngine) - 0.5f) * 20.0f; // -10 to 10 float randomZ = (m_RandomFloat(m_RandomEngine) - 0.5f) * 20.0f; // -10 to 10 m_TargetPosition = m_Position + NiPoint3(randomX, 0, randomZ); m_IsMoving = true; LogMessage("Started movement to (" + std::to_string(m_TargetPosition.x) + ", " + std::to_string(m_TargetPosition.y) + ", " + std::to_string(m_TargetPosition.z) + ")"); } void SimUser::SendPositionUpdate() { if (m_State != SimUserState::IN_WORLD || !m_WorldPeer) return; // Create position update packet RakNet::BitStream positionBitStream; positionBitStream.Write(static_cast(ID_USER_PACKET_ENUM + 3)); // Position update positionBitStream.Write(m_Position.x); positionBitStream.Write(m_Position.y); positionBitStream.Write(m_Position.z); positionBitStream.Write(m_Rotation.x); positionBitStream.Write(m_Rotation.y); positionBitStream.Write(m_Rotation.z); positionBitStream.Write(m_Rotation.w); m_WorldPeer->Send(&positionBitStream, HIGH_PRIORITY, UNRELIABLE_SEQUENCED, 0, m_WorldServerAddr, false); m_PacketsSent++; m_BytesSent += positionBitStream.GetNumberOfBytesUsed(); } void SimUser::HandleAuthPacket(Packet* packet) { switch (packet->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: LogMessage("Connected to auth server"); SendLoginRequest(); break; case ID_DISCONNECTION_NOTIFICATION: case ID_CONNECTION_LOST: LogMessage("Disconnected from auth server"); SetError("Lost connection to auth server"); break; default: // Handle auth-specific packets if (packet->data[0] >= ID_USER_PACKET_ENUM) { HandleLoginResponse(packet); } break; } } void SimUser::HandleWorldPacket(Packet* packet) { switch (packet->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: LogMessage("Connected to world server"); SendWorldLoginRequest(); break; case ID_DISCONNECTION_NOTIFICATION: case ID_CONNECTION_LOST: LogMessage("Disconnected from world server"); SetError("Lost connection to world server"); break; default: // Handle world-specific packets if (packet->data[0] >= ID_USER_PACKET_ENUM) { // For simplicity, just log that we received a world packet LogMessage("Received world packet of type " + std::to_string(packet->data[0])); } break; } } void SimUser::SendLoginRequest() { if (!m_AuthPeer) return; SetState(SimUserState::AUTHENTICATING); // Create login request packet RakNet::BitStream loginBitStream; loginBitStream.Write(static_cast(ID_USER_PACKET_ENUM)); loginBitStream.Write(static_cast(m_Username.length())); loginBitStream.Write(m_Username.c_str(), m_Username.length()); loginBitStream.Write(static_cast(m_Password.length())); loginBitStream.Write(m_Password.c_str(), m_Password.length()); m_AuthPeer->Send(&loginBitStream, HIGH_PRIORITY, RELIABLE, 0, m_AuthServerAddr, false); m_PacketsSent++; m_BytesSent += loginBitStream.GetNumberOfBytesUsed(); LogMessage("Sent login request for user: " + m_Username); } void SimUser::HandleLoginResponse(Packet* packet) { // Simple login response handling LogMessage("Received login response"); // For simulation purposes, assume successful login and move to world m_SessionKey = "sim_session_" + std::to_string(m_ID); // Simulate connecting to world server (hardcoded for now) ConnectToWorld("127.0.0.1", 2000); } void SimUser::SendWorldLoginRequest() { if (!m_WorldPeer) return; SetState(SimUserState::LOADING_WORLD); // Create world login request RakNet::BitStream worldLoginBitStream; worldLoginBitStream.Write(static_cast(ID_USER_PACKET_ENUM + 10)); worldLoginBitStream.Write(static_cast(m_SessionKey.length())); worldLoginBitStream.Write(m_SessionKey.c_str(), m_SessionKey.length()); m_WorldPeer->Send(&worldLoginBitStream, HIGH_PRIORITY, RELIABLE, 0, m_WorldServerAddr, false); m_PacketsSent++; m_BytesSent += worldLoginBitStream.GetNumberOfBytesUsed(); LogMessage("Sent world login request"); // For simulation, immediately consider ourselves in world SetState(SimUserState::IN_WORLD); m_Position = NiPoint3(0, 0, 0); // Start at origin LogMessage("Entered world simulation mode"); } void SimUser::SetError(const std::string& error) { m_HasError = true; m_LastError = error; m_State = SimUserState::ERROR_STATE; LogMessage("ERROR: " + error); } void SimUser::LogMessage(const std::string& message) { if (Game::logger) { LOG("[SimUser %u (%s)] %s", m_ID, m_Username.c_str(), message.c_str()); } else { std::cout << "[SimUser " << m_ID << " (" << m_Username << ")] " << message << std::endl; } }