From 789f70b6f1a9067843dfc1ff73d86b645efe1da9 Mon Sep 17 00:00:00 2001 From: LaG1924 <12997935+LaG1924@users.noreply.github.com> Date: Sat, 17 Jun 2017 19:23:53 +0500 Subject: 2017-06-17 --- src/network/Network.cpp | 284 ++++++++++++++++-------- src/network/Network.hpp | 42 ++-- src/network/NetworkClient.cpp | 175 +++++++-------- src/network/NetworkClient.hpp | 52 ++--- src/network/Packet.hpp | 492 ++++++++++++++++++++++++++++++++++++++++++ src/network/Socket.cpp | 29 +++ src/network/Socket.hpp | 45 ++++ src/network/Stream.cpp | 334 ++++++++++++++++++++++++++++ src/network/Stream.hpp | 115 ++++++++++ 9 files changed, 1331 insertions(+), 237 deletions(-) create mode 100644 src/network/Packet.hpp create mode 100644 src/network/Socket.cpp create mode 100644 src/network/Socket.hpp create mode 100644 src/network/Stream.cpp create mode 100644 src/network/Stream.hpp (limited to 'src/network') diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 4ce424c..59c4e00 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -1,101 +1,215 @@ #include "Network.hpp" +#include -Network::Network(std::string address, unsigned short port) : m_address(address), m_port(port) { - LOG(INFO) << "Connecting to server " << m_address << ":" << m_port; - sf::Socket::Status status = m_socket.connect(sf::IpAddress(m_address), m_port); - m_socket.setBlocking(true); - if (status != sf::Socket::Done) { - if (status == sf::Socket::Error) { - LOG(ERROR) << "Can't connect to remote server"; - } else { - LOG(ERROR) << "Connection failed with unknown reason"; - throw std::runtime_error("Connection is failed"); - throw 13; - } - } - LOG(INFO) << "Connected to server"; +Network::Network(std::string address, unsigned short port) { + socket = new Socket(address, port); + stream = new StreamSocket(socket); } Network::~Network() { - m_socket.disconnect(); - LOG(INFO) << "Disconnected"; -} - -void Network::SendHandshake(std::string username) { - //Handshake packet - Packet handshakePacket = PacketBuilder::CHandshaking0x00(316, m_address, m_port, 2); - SendPacket(handshakePacket); - - //LoginStart packet - Field fName; - fName.SetString(username); - Packet loginPacket(0); - loginPacket.AddField(fName); - SendPacket(loginPacket); + delete stream; + delete socket; } -void DumpPacket(Packet &packet, std::string DumpName) { - return; - byte *buff = new byte[packet.GetLength()]; - packet.CopyToBuff(buff); - std::ofstream fs(DumpName, std::ios::out | std::ios::binary); - fs.write(reinterpret_cast(buff), packet.GetLength()); - fs.close(); - delete buff; +std::shared_ptr Network::ReceivePacket(ConnectionState state) { + int packetSize = stream->ReadVarInt(); + auto packetData = stream->ReadByteArray(packetSize); + StreamBuffer streamBuffer(packetData.data(), packetData.size()); + int packetId = streamBuffer.ReadVarInt(); + auto packet = ReceivePacketByPacketId(packetId, state, streamBuffer); + return packet; } -static int pn = 0; - void Network::SendPacket(Packet &packet) { - m_socket.setBlocking(true); - byte *packetData = new byte[packet.GetLength()]; - packet.CopyToBuff(packetData); - m_socket.send(packetData, packet.GetLength()); - std::ostringstream out; - out << "s" << pn++ << "-"; - out << "0x" << (packet.GetId() < 15 ? "0" : "") << std::hex << packet.GetId() << std::dec; - DumpPacket(packet, out.str()); - - delete[] packetData; + StreamCounter packetSize; + packetSize.WriteVarInt(packet.GetPacketId()); + packet.ToStream(&packetSize); + stream->WriteVarInt(packetSize.GetCountedSize()); + stream->WriteVarInt(packet.GetPacketId()); + packet.ToStream(stream); } -Packet Network::ReceivePacket() { - byte bufLen[5] = {0}; - size_t rec = 0; - for (int i = 0; i < 5; i++) { - byte buff = 0; - size_t r = 0; - m_socket.receive(&buff, 1, r); - rec += r; - bufLen[i] = buff; - if ((buff & 0b10000000) == 0) { - break; - } - } - Field fLen = FieldParser::Parse(VarIntType, bufLen); - size_t packetLen = fLen.GetVarInt() + fLen.GetLength(); - if (packetLen > 1024 * 1024 * 15) - LOG(WARNING) << "OMG SIZEOF PACKAGE IS " << packetLen; - if (packetLen < rec) { - return Packet(bufLen); - } - byte *bufPack = new byte[packetLen]; - std::copy(bufLen, bufLen + rec, bufPack); - size_t dataLen = rec; - while (m_socket.receive(bufPack + dataLen, packetLen - dataLen, rec) == sf::Socket::Done && dataLen < packetLen) { - dataLen += rec; +std::shared_ptr Network::ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream) { + std::shared_ptr packet(nullptr); + switch (state) { + case Handshaking: + switch (packetId) { + case PacketNameHandshakingCB::Handshake: + packet = std::make_shared(); + break; + } + case Login: + switch (packetId) { + case PacketNameLoginCB::LoginSuccess: + packet = std::make_shared(); + break; + } + break; + case Play: + packet = ParsePacketPlay((PacketNamePlayCB) packetId); + break; + case Status: + break; } - if (dataLen < packetLen) { - LOG(ERROR) << "Received data is " << dataLen << " but " << packetLen << " is promoted"; - throw std::runtime_error("Data is losted"); - } else { - Packet p(bufPack); - delete[] bufPack; + if (packet.get() != nullptr) + packet->FromStream(&stream); + return packet; +} - std::ostringstream out; - out << "r" << pn++ << "-"; - out << "0x" << (p.GetId() < 15 ? "0" : "") << std::hex << p.GetId() << std::dec; - DumpPacket(p, out.str()); - return p; +std::shared_ptr Network::ParsePacketPlay(PacketNamePlayCB id) { + switch (id) { + case SpawnObject: + break; + case SpawnExperienceOrb: + break; + case SpawnGlobalEntity: + break; + case SpawnMob: + break; + case SpawnPainting: + break; + case SpawnPlayer: + break; + case AnimationCB: + break; + case Statistics: + break; + case BlockBreakAnimation: + break; + case UpdateBlockEntity: + break; + case BlockAction: + break; + case BlockChange: + break; + case BossBar: + break; + case ServerDifficulty: + break; + case TabCompleteCB: + break; + case ChatMessageCB: + break; + case MultiBlockChange: + break; + case ConfirmTransactionCB: + break; + case CloseWindowCB: + break; + case OpenWindow: + break; + case WindowItems: + break; + case WindowProperty: + break; + case SetSlot: + break; + case SetCooldown: + break; + case PluginMessageCB: + break; + case NamedSoundEffect: + break; + case DisconnectPlay: + return std::make_shared(); + case EntityStatus: + break; + case Explosion: + break; + case UnloadChunk: + break; + case ChangeGameState: + break; + case KeepAliveCB: + return std::make_shared(); + case ChunkData: + return std::make_shared(); + case Effect: + break; + case Particle: + break; + case JoinGame: + return std::make_shared(); + case Map: + break; + case EntityRelativeMove: + break; + case EntityLookAndRelativeMove: + break; + case EntityLook: + break; + case Entity: + break; + case VehicleMove: + break; + case OpenSignEditor: + break; + case PlayerAbilitiesCB: + break; + case CombatEvent: + break; + case PlayerListItem: + break; + case PlayerPositionAndLookCB: + return std::make_shared(); + case UseBed: + break; + case DestroyEntities: + break; + case RemoveEntityEffect: + break; + case ResourcePackSend: + break; + case Respawn: + break; + case EntityHeadLook: + break; + case WorldBorder: + break; + case Camera: + break; + case HeldItemChangeCB: + break; + case DisplayScoreboard: + break; + case EntityMetadata: + break; + case AttachEntity: + break; + case EntityVelocity: + break; + case EntityEquipment: + break; + case SetExperience: + break; + case UpdateHealth: + return std::make_shared(); + case ScoreboardObjective: + break; + case SetPassengers: + break; + case Teams: + break; + case UpdateScore: + break; + case SpawnPosition: + return std::make_shared(); + case TimeUpdate: + break; + case Title: + break; + case SoundEffect: + break; + case PlayerListHeaderAndFooter: + break; + case CollectItem: + break; + case EntityTeleport: + break; + case EntityProperties: + break; + case EntityEffect: + break; } + return nullptr; } diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 84f2e7f..1281289 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -1,28 +1,26 @@ #pragma once -#include -#include -#include -#include "../packet/Packet.hpp" -#include "../packet/PacketBuilder.hpp" - +#include +#include "Socket.hpp" +#include "Packet.hpp" + +enum ConnectionState { + Handshaking, + Login, + Play, + Status, +}; class Network { -public: - Network(std::string address, unsigned short port); - - ~Network(); - - void SendHandshake(std::string username); + Socket *socket; + StreamSocket *stream; - void SendPacket(Packet &packet); - - Packet ReceivePacket(); - -private: - std::string m_address; - unsigned short m_port; - sf::TcpSocket m_socket; - bool m_isCommpress=false; -}; + std::shared_ptr ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream); +public: + Network(std::string address, unsigned short port); + ~Network(); + std::shared_ptr ReceivePacket(ConnectionState state = Play); + void SendPacket(Packet &packet); + std::shared_ptr ParsePacketPlay(PacketNamePlayCB id); +}; \ No newline at end of file diff --git a/src/network/NetworkClient.cpp b/src/network/NetworkClient.cpp index fd957a5..b8d880d 100644 --- a/src/network/NetworkClient.cpp +++ b/src/network/NetworkClient.cpp @@ -1,107 +1,94 @@ #include "NetworkClient.hpp" +#include "Packet.hpp" -ServerInfo NetworkClient::ServerPing(std::string address, unsigned short port) { - ServerInfo info; - Network network(address, port); - Packet packet_handshake = PacketBuilder::CHandshaking0x00(316, address, port, 1); - network.SendPacket(packet_handshake); - Packet packet_request(0); - network.SendPacket(packet_request); - Packet packet_response = network.ReceivePacket(); - PacketParser::Parse(packet_response, Login); - //std::string json = static_cast(packet_response_parsed.GetFieldById(0))->GetValue(); - std::string json = packet_response.GetField(0).GetString(); - try { - nlohmann::json j = nlohmann::json::parse(json); - info.protocol = j["version"]["protocol"].get(); - info.version = j["version"]["name"].get(); - info.players_max = j["players"]["max"].get(); - info.players_online = j["players"]["online"].get(); - info.description = j["description"]["text"].get(); - for (auto t:j["description"]["extra"]) { - info.description += t["text"].get(); - } - if (!j["favicon"].is_null()) - info.favicon = j["favicon"].get(); - info.json = json; - for (auto t:j["players"]["sample"]) { - std::pair player; - player.first = t["id"].get(); - player.second = t["name"].get(); - info.players.push_back(player); - } - } catch (const nlohmann::detail::exception e) { - std::cerr << "Parsed json is not valid (" << e.id << "): " << e.what() << std::endl; - } - //Ping - Packet packet_ping(0x01); - Field payload; - payload.SetLong(771235); - packet_ping.AddField(payload); - std::chrono::high_resolution_clock clock; - auto t1 = clock.now(); - network.SendPacket(packet_ping); - Packet pong = network.ReceivePacket(); - auto t2 = clock.now(); - pong.ParseField(Long); - if (pong.GetField(0).GetLong() == 771235) { - std::chrono::duration pingTime = t2 - t1; - info.ping = pingTime.count(); - } - return info; -} +NetworkClient::NetworkClient(std::string address, unsigned short port, std::string username, bool &quit) + : network(address, port), isRunning(quit) { + state = Handshaking; -NetworkClient::NetworkClient(std::string address, unsigned short port, std::string username) : m_network(address, - port) { - m_network.SendHandshake(username); - Update(); - m_networkThread = std::thread(&NetworkClient::MainLoop, this); -} + PacketHandshake handshake; + handshake.protocolVersion = 335; + handshake.serverAddress = "127.0.0.1"; + handshake.serverPort = 25565; + handshake.nextState = 2; + network.SendPacket(handshake); + state = Login; -NetworkClient::~NetworkClient() { - LOG(INFO)<<"NC stopping..."; - isContinue=false; - m_networkThread.join(); - LOG(INFO)<<"NC is stopped"; -} + PacketLoginStart loginStart; + loginStart.Username = "HelloOne"; + network.SendPacket(loginStart); -Packet * NetworkClient::GetPacket() { - if (m_received.size() < 1) - return nullptr; - Packet packet = m_received.front(); - m_received.pop(); - return new Packet(packet); + auto response = std::static_pointer_cast(network.ReceivePacket(Login)); + if (response->Username != username) { + throw std::logic_error("Received username is not sended username"); + } + + state = Play; + + isActive = true; + std::thread thread(&NetworkClient::NetworkLoop, this); + std::swap(networkThread, thread); } -void NetworkClient::AddPacketToQueue(Packet packet) { - m_toSend.push(packet); +NetworkClient::~NetworkClient() { + isActive = false; + networkThread.join(); } -void NetworkClient::Update() { - if (m_toSend.size() > 0) { - m_network.SendPacket(m_toSend.front()); - m_toSend.pop(); - } - Packet received = m_network.ReceivePacket(); - if (received.GetId() == 0x1F) { - PacketParser::Parse(received); - Packet response = PacketBuilder::CPlay0x0B(received.GetField(0).GetVarInt()); - m_network.SendPacket(response); - return; - } - m_updateMutex.lock(); - m_received.push(received); - m_updateMutex.unlock(); +std::shared_ptr NetworkClient::ReceivePacket() { + if (toReceive.empty()) + return std::shared_ptr(nullptr); + toReceiveMutex.lock(); + auto ret = toReceive.front(); + toReceive.pop(); + toReceiveMutex.unlock(); + return ret; } -void NetworkClient::MainLoop() { - el::Helpers::setThreadName("Network"); - try { - while (isContinue) { - Update(); - } - } catch (int e){ - LOG(ERROR)<<"Catched exception in NC: "< packet) { + toSendMutex.lock(); + toSend.push(packet); + toSendMutex.unlock(); +} +void NetworkClient::NetworkLoop() { + auto timeOfLastKeepAlivePacket = std::chrono::steady_clock::now(); + el::Helpers::setThreadName("Network"); + LOG(INFO) << "Network thread is started"; + try { + while (isActive) { + toSendMutex.lock(); + while (!toSend.empty()) { + if (toSend.front()!=nullptr) + network.SendPacket(*toSend.front()); + toSend.pop(); + } + toSendMutex.unlock(); + auto packet = network.ReceivePacket(state); + if (packet.get() != nullptr) { + if (packet->GetPacketId() != PacketNamePlayCB::KeepAliveCB) { + toReceiveMutex.lock(); + toReceive.push(packet); + toReceiveMutex.unlock(); + } else { + timeOfLastKeepAlivePacket = std::chrono::steady_clock::now(); + auto packetKeepAlive = std::static_pointer_cast(packet); + auto packetKeepAliveSB = std::make_shared(packetKeepAlive->KeepAliveId); + network.SendPacket(*packetKeepAliveSB); + } + } + using namespace std::chrono_literals; + if (std::chrono::steady_clock::now() - timeOfLastKeepAlivePacket > 20s) { + auto disconnectPacket = std::make_shared(); + disconnectPacket->Reason = "Timeout"; + toReceiveMutex.lock(); + toReceive.push(disconnectPacket); + toReceiveMutex.unlock(); + break; + } + } + } catch (std::exception &e) { + LOG(ERROR) << "Exception catched in NetworkLoop: " << e.what(); + isRunning = false; + } + LOG(INFO) << "Network thread is stopped"; } diff --git a/src/network/NetworkClient.hpp b/src/network/NetworkClient.hpp index 14745a5..bf7aa4e 100644 --- a/src/network/NetworkClient.hpp +++ b/src/network/NetworkClient.hpp @@ -1,45 +1,25 @@ #pragma once -#include #include +#include #include -#include #include "Network.hpp" -#include "../packet/PacketParser.hpp" -#include "../packet/PacketBuilder.hpp" -struct ServerInfo{ - std::string version; - int protocol = 0; - int players_max = 0; - int players_online = 0; - std::vector> players; - std::string description; - double ping = 0; - std::string favicon; - std::string json; -}; class NetworkClient { + Network network; + std::thread networkThread; + std::mutex toSendMutex; + std::mutex toReceiveMutex; + std::queue > toSend; + std::queue > toReceive; + bool isActive=true; + bool &isRunning; + ConnectionState state; + void NetworkLoop(); public: - NetworkClient(std::string address, unsigned short port, std::string username); - ~NetworkClient(); - - void Update(); - - void MainLoop(); - - Packet * GetPacket(); - void AddPacketToQueue(Packet packet); - - static ServerInfo ServerPing(std::string address,unsigned short port); -private: - std::mutex m_updateMutex; - std::thread m_networkThread; - bool isContinue=true; - NetworkClient (const NetworkClient&); - NetworkClient&operator=(const NetworkClient&); - Network m_network; - std::queue m_received; - std::queue m_toSend; -}; + NetworkClient(std::string address, unsigned short port, std::string username, bool &quit); + ~NetworkClient(); + std::shared_ptr ReceivePacket(); + void SendPacket(std::shared_ptr packet); +}; \ No newline at end of file diff --git a/src/network/Packet.hpp b/src/network/Packet.hpp new file mode 100644 index 0000000..9249a34 --- /dev/null +++ b/src/network/Packet.hpp @@ -0,0 +1,492 @@ +#pragma once + +#include +#include "Stream.hpp" + +enum PacketNameLoginSB { + LoginStart = 0x00, + EncryptionResponse = 0x01, +}; +enum PacketNamePlaySB { + TeleportConfirm, + PrepareCraftingGrid, + TabCompleteSB, + ChatMessageSB, + ClientStatus, + ClientSettings, + ConfirmTransactionSB, + EnchantItem, + ClickWindow, + CloseWindowSB, + PluginMessageSB, + UseEntity, + KeepAliveSB, + Player, + PlayerPosition, + PlayerPositionAndLookSB, + PlayerLook, + VehicleMoveSB, + SteerBoat, + PlayerAbilitiesSB, + PlayerDigging, + EntityAction, + SteerVehicle, + CraftingBookData, + ResourcePackStatus, + AdvancementTab, + HeldItemChangeSB, + CreativeInventoryAction, + UpdateSign, + AnimationSB, + Spectate, + PlayerBlockPlacement, + UseItem, +}; + +enum PacketNameHandshakingCB { + Handshake = 0x00, +}; +enum PacketNameLoginCB { + Disconnect = 0x00, + EncryptionRequest = 0x01, + LoginSuccess = 0x02, + SetCompression = 0x03, +}; +enum PacketNamePlayCB { + SpawnObject = 0x00, + SpawnExperienceOrb, + SpawnGlobalEntity, + SpawnMob, + SpawnPainting, + SpawnPlayer, + AnimationCB, + Statistics, + BlockBreakAnimation, + UpdateBlockEntity, + BlockAction, + BlockChange, + BossBar, + ServerDifficulty, + TabCompleteCB, + ChatMessageCB, + MultiBlockChange, + ConfirmTransactionCB, + CloseWindowCB, + OpenWindow, + WindowItems, + WindowProperty, + SetSlot, + SetCooldown, + PluginMessageCB, + NamedSoundEffect, + DisconnectPlay, + EntityStatus, + Explosion, + UnloadChunk, + ChangeGameState, + KeepAliveCB, + ChunkData, + Effect, + Particle, + JoinGame, + Map, + EntityRelativeMove, + EntityLookAndRelativeMove, + EntityLook, + Entity, + VehicleMove, + OpenSignEditor, + PlayerAbilitiesCB, + CombatEvent, + PlayerListItem, + PlayerPositionAndLookCB, + UseBed, + UnlockRecipes, + DestroyEntities, + RemoveEntityEffect, + ResourcePackSend, + Respawn, + EntityHeadLook, + SelectAdvancementTab, + WorldBorder, + Camera, + HeldItemChangeCB, + DisplayScoreboard, + EntityMetadata, + AttachEntity, + EntityVelocity, + EntityEquipment, + SetExperience, + UpdateHealth, + ScoreboardObjective, + SetPassengers, + Teams, + UpdateScore, + SpawnPosition, + TimeUpdate, + Title, + SoundEffect, + PlayerListHeaderAndFooter, + CollectItem, + EntityTeleport, + Advancements, + EntityProperties, + EntityEffect, +}; + +struct Packet { + virtual ~Packet() = default; + virtual void ToStream(StreamOutput *stream) = 0; + virtual void FromStream(StreamInput *stream) = 0; + virtual int GetPacketId() = 0; +}; + +struct PacketHandshake : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(protocolVersion); + stream->WriteString(serverAddress); + stream->WriteUShort(serverPort); + stream->WriteVarInt(nextState); + } + + void FromStream(StreamInput *stream) override { + protocolVersion = stream->ReadVarInt(); + serverAddress = stream->ReadString(); + serverPort = stream->ReadUShort(); + nextState = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNameHandshakingCB::Handshake; + } + + int protocolVersion; + std::string serverAddress; + unsigned short serverPort; + int nextState; +}; + +struct PacketLoginStart : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginSB::LoginStart; + } + + std::string Username; +}; + +struct PacketLoginSuccess : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Uuid); + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Uuid = stream->ReadString(); + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginCB::LoginSuccess; + } + + std::string Uuid; + std::string Username; +}; + +struct PacketJoinGame : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(EntityId); + stream->WriteUByte(Gamemode); + stream->WriteInt(Dimension); + stream->WriteUByte(Difficulty); + stream->WriteUByte(MaxPlayers); + stream->WriteString(LevelType); + stream->WriteBool(ReducedDebugInfo); + } + + void FromStream(StreamInput *stream) override { + EntityId = stream->ReadInt(); + Gamemode = stream->ReadUByte(); + Dimension = stream->ReadInt(); + Difficulty = stream->ReadUByte(); + MaxPlayers = stream->ReadUByte(); + LevelType = stream->ReadString(); + ReducedDebugInfo = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlayCB::JoinGame; + } + + int EntityId; + unsigned char Gamemode; + int Dimension; + unsigned char Difficulty; + unsigned char MaxPlayers; + std::string LevelType; + bool ReducedDebugInfo; +}; + +struct PacketDisconnectPlay : Packet { + void ToStream(StreamOutput *stream) override { + + } + + void FromStream(StreamInput *stream) override { + Reason = stream->ReadChat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::DisconnectPlay; + } + + std::string Reason; +}; + +struct PacketSpawnPosition : Packet { + void ToStream(StreamOutput *stream) override { + + } + + void FromStream(StreamInput *stream) override { + Location = stream->ReadPosition(); + } + + int GetPacketId() override { + return PacketNamePlayCB::SpawnPosition; + } + + Vector Location; +}; + +struct PacketKeepAliveCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::KeepAliveCB; + } + + int KeepAliveId; +}; + +struct PacketKeepAliveSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::KeepAliveSB; + } + + int KeepAliveId; + + PacketKeepAliveSB(int KeepAliveId) : KeepAliveId(KeepAliveId) {} +}; + +struct PacketPlayerPositionAndLookCB : Packet { + void ToStream(StreamOutput *stream) override { + + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + Y = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + Flags = stream->ReadUByte(); + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::PlayerPositionAndLookCB; + } + + double X; + double Y; + double Z; + float Yaw; + float Pitch; + unsigned char Flags; + int TeleportId; +}; + +struct PacketTeleportConfirm : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::TeleportConfirm; + } + + int TeleportId; + + PacketTeleportConfirm(int TeleportId) : TeleportId(TeleportId) {} +}; + +struct PacketClientStatus : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(ActionId); + } + + void FromStream(StreamInput *stream) override { + ActionId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::ClientStatus; + } + + int ActionId; + + PacketClientStatus(int ActionId) : ActionId(ActionId) {} +}; + +struct PacketPlayerPositionAndLookSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPositionAndLookSB; + } + + + double X; + double FeetY; + double Z; + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerPositionAndLookSB(double X, double FeetY, double Z, + float Yaw, float Pitch, bool OnGround) : X(X), FeetY(FeetY), Z(Z), Yaw(Yaw), + Pitch(Pitch), OnGround(OnGround) {} +}; + +struct PacketChunkData : Packet { + void ToStream(StreamOutput *stream) override { + + } + + void FromStream(StreamInput *stream) override { + ChunkX = stream->ReadInt(); + ChunkZ = stream->ReadInt(); + GroundUpContinuous = stream->ReadBool(); + PrimaryBitMask = stream->ReadVarInt(); + Size = stream->ReadVarInt(); + Data = stream->ReadByteArray(Size); + NumberOfBlockEntities = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::ChunkData; + } + + int ChunkX; + int ChunkZ; + bool GroundUpContinuous; + int PrimaryBitMask; + int Size; + std::vector Data; + int NumberOfBlockEntities; + //std::vector BlockEntities; +}; + +struct PacketPlayerPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPosition; + } + + double X; + double FeetY; + double Z; + bool OnGround; + + PacketPlayerPosition(double X, double Y, double Z, bool ground) : X(X), FeetY(Y), Z(Z), OnGround(ground) {} +}; + +struct PacketPlayerLook : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerLook; + } + + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerLook(float Yaw, float Pitch, bool ground) : Yaw(Yaw), Pitch(Pitch), OnGround(ground) {} +}; + +struct PacketUpdateHealth : Packet { + void ToStream(StreamOutput *stream) override { + + } + + void FromStream(StreamInput *stream) override { + Health = stream->ReadFloat(); + Food = stream->ReadVarInt(); + FoodSaturation = stream->ReadFloat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::UpdateHealth; + } + + float Health; + int Food; + float FoodSaturation; +}; \ No newline at end of file diff --git a/src/network/Socket.cpp b/src/network/Socket.cpp new file mode 100644 index 0000000..2bbf49a --- /dev/null +++ b/src/network/Socket.cpp @@ -0,0 +1,29 @@ +#include "Socket.hpp" + +Socket::Socket(std::string address, unsigned short port) { + sf::Socket::Status connectionStatus = socket.connect(sf::IpAddress(address), port); + if (connectionStatus == sf::Socket::Status::Error) + throw std::runtime_error("Can't connect to remote server"); + else if (connectionStatus != sf::Socket::Status::Done) + throw std::runtime_error("Connection failed with unknown reason"); +} + +Socket::~Socket() { + socket.disconnect(); +} + +void Socket::Read(unsigned char *buffPtr, size_t buffLen) { + size_t received = 0; + socket.receive(buffPtr, buffLen, received); + size_t totalReceived = received; + while (totalReceived < buffLen) { + if (socket.receive(buffPtr + totalReceived, buffLen - totalReceived, received) != sf::Socket::Done) + throw std::runtime_error("Raw socket data receiving is failed"); + totalReceived += received; + } +} + +void Socket::Write(unsigned char *buffPtr, size_t buffLen) { + if (socket.send(buffPtr, buffLen) != sf::Socket::Done) + throw std::runtime_error("Raw socket data sending is failed"); +} diff --git a/src/network/Socket.hpp b/src/network/Socket.hpp new file mode 100644 index 0000000..ee449b3 --- /dev/null +++ b/src/network/Socket.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +/** + * Platform independent class for working with platform dependent hardware socket + * @brief Wrapper around raw sockets + * @warning Connection state is based on lifetime of Socket object instance, ie connected at ctor and disconnect at dtor + * @todo Replace SFML's socket with WinSock and POSIX's socket implementation + */ +class Socket { + sf::TcpSocket socket; +public: + /** + * Constructs Socket class instance from IP's string and Port number and connects to remote server + * @param[in] address IP address of remote server. String should be ANSI and contains 4 one-byte values separated by dots + * @param[in] port target port of remote server to connect + * @throw std::runtime_error if connection is failed + */ + Socket(std::string address, unsigned short port); + + /** + * Destruct Socket instance and disconnect from server + * @warning There is no way to force disconnect, except use delete for manually allocated objects and scope of visibility for variables on stack + */ + ~Socket(); + + /** + * Reads data from socket and write to buffer + * @warning This is blocking function, and execution flow will not be returned until all required data is sended + * @warning Reported buffer length must be <= actual size of buffer, or memory corruption will be caused + * @param[out] buffPtr Pointer to buffer, where data must be placed + * @param[in] buffLen Length of data, that must be readed from server and writed to buffer + */ + void Read(unsigned char *buffPtr, size_t buffLen); + + /** + * Writes data from buffer to socket + * @warning This is blocking function, and execution flow will not be returned until all required data is received + * @param[in] buffPtr Pointer to buffer that contain data to send + * @param[in] buffLen Length of buffer + */ + void Write(unsigned char *buffPtr, size_t buffLen); +}; \ No newline at end of file diff --git a/src/network/Stream.cpp b/src/network/Stream.cpp new file mode 100644 index 0000000..a0c6cb0 --- /dev/null +++ b/src/network/Stream.cpp @@ -0,0 +1,334 @@ +#include "Stream.hpp" + +const int MAX_VARINT_LENGTH = 5; + +bool StreamInput::ReadBool() { + unsigned char value; + ReadData(&value, 1); + return value != 0; +} + +signed char StreamInput::ReadByte() { + signed char value; + ReadData((unsigned char *) &value, 1); + endswap(value); + return value; +} + +unsigned char StreamInput::ReadUByte() { + unsigned char value; + ReadData(&value, 1); + endswap(value); + return value; +} + +short StreamInput::ReadShort() { + unsigned short value; + ReadData((unsigned char *) &value, 2); + endswap(value); + return value; +} + +unsigned short StreamInput::ReadUShort() { + unsigned char buff[2]; + ReadData(buff, 2); + unsigned short val = *(reinterpret_cast(buff)); + endswap(val); + return val; +} + +int StreamInput::ReadInt() { + int value; + ReadData((unsigned char *) &value, 4); + endswap(value); + return value; +} + +long long StreamInput::ReadLong() { + long long value; + ReadData((unsigned char *) &value, 8); + endswap(value); + return value; +} + +float StreamInput::ReadFloat() { + float value; + ReadData((unsigned char *) &value, 4); + endswap(value); + return value; +} + +double StreamInput::ReadDouble() { + double value; + ReadData((unsigned char *) &value, 8); + endswap(value); + return value; +} + +std::string StreamInput::ReadString() { + int strLength = ReadVarInt(); + unsigned char *buff = new unsigned char[strLength + 1]; + ReadData(buff, strLength); + buff[strLength] = 0; + std::string str((char *) buff); + delete buff; + return str; +} + +std::string StreamInput::ReadChat() { + std::string str, jsonStr = ReadString(); + nlohmann::json json; + try { + json = nlohmann::json::parse(jsonStr); + } catch (std::exception &e) { + LOG(WARNING) << "Chat json parsing failed: " << e.what(); + LOG(WARNING) << "Corrupted json: " << jsonStr; + return ""; + } + if (json.find("translate") != json.end()) + if (json["translate"].get() == "multiplayer.disconnect.kicked") + return "kicked by operator"; + for (auto &it:json["extra"]) { + str += it["text"].get(); + } + return str; +} + +int StreamInput::ReadVarInt() { + unsigned char data[MAX_VARINT_LENGTH] = {0}; + size_t dataLen = 0; + do { + ReadData(&data[dataLen], 1); + } while ((data[dataLen++] & 0x80) != 0); + + int readed = 0; + int result = 0; + char read; + do { + read = data[readed]; + int value = (read & 0b01111111); + result |= (value << (7 * readed)); + readed++; + } while ((read & 0b10000000) != 0); + + return result; +} + +long long StreamInput::ReadVarLong() { + return 0; +} + +std::vector StreamInput::ReadEntityMetadata() { + return std::vector(); +} + +std::vector StreamInput::ReadSlot() { + return std::vector(); +} + +std::vector StreamInput::ReadNbtTag() { + return std::vector(); +} + +Vector StreamInput::ReadPosition() { + unsigned long long t = ReadLong(); + int x = t >> 38; + int y = (t >> 26) & 0xFFF; + int z = t << 38 >> 38; + if (x >= pow(2, 25)) { + x -= pow(2, 26); + } + if (y >= pow(2, 11)) { + y -= pow(2, 12); + } + if (z >= pow(2, 25)) { + z -= pow(2, 26); + } + return Vector(x, y, z); +} + +unsigned char StreamInput::ReadAngle() { + return ReadUByte(); +} + +std::vector StreamInput::ReadUuid() { + unsigned char buff[16]; + ReadData(buff, 16); + endswap(buff, 16); + return std::vector(buff, buff + 16); +} + +std::vector StreamInput::ReadByteArray(size_t arrLength) { + unsigned char *buffer = new unsigned char[arrLength]; + ReadData(buffer, arrLength); + std::vector ret(buffer, buffer + arrLength); + delete buffer; + return ret; + +} + +void StreamOutput::WriteBool(bool value) { + unsigned char val = value ? 1 : 0; + endswap(val); + WriteData(&val, 1); +} + +void StreamOutput::WriteByte(signed char value) { + +} + +void StreamOutput::WriteUByte(unsigned char value) { + endswap(value); + WriteData(&value,1); +} + +void StreamOutput::WriteShort(short value) { + +} + +void StreamOutput::WriteUShort(unsigned short value) { + endswap(value); + WriteData((unsigned char *) &value, 2); +} + +void StreamOutput::WriteInt(int value) { + endswap(value); + WriteData((unsigned char *) &value, 4); +} + +void StreamOutput::WriteLong(long long value) { + +} + +void StreamOutput::WriteFloat(float value) { + endswap(value); + WriteData((unsigned char *) &value, 4); +} + +void StreamOutput::WriteDouble(double value) { + endswap(value); + WriteData((unsigned char *) &value, 8); +} + +void StreamOutput::WriteString(std::string value) { + WriteVarInt(value.size()); + WriteData((unsigned char *) value.data(), value.size()); +} + +void StreamOutput::WriteChat(std::string value) { + +} + +void StreamOutput::WriteVarInt(int value) { + unsigned char buff[5]; + size_t len = 0; + do { + unsigned char temp = (unsigned char) (value & 0b01111111); + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buff[len] = temp; + len++; + } while (value != 0); + WriteData(buff, len); +} + +void StreamOutput::WriteVarLong(long long value) { + +} + +void StreamOutput::WriteEntityMetadata(std::vector value) { + +} + +void StreamOutput::WriteSlot(std::vector value) { + +} + +void StreamOutput::WriteNbtTag(std::vector value) { + +} + +void StreamOutput::WritePosition(Vector value) { + +} + +void StreamOutput::WriteAngle(unsigned char value) { + +} + +void StreamOutput::WriteUuid(std::vector value) { + +} + +void StreamOutput::WriteByteArray(std::vector value) { + +} + +void StreamBuffer::ReadData(unsigned char *buffPtr, size_t buffLen) { + size_t bufferLengthLeft = buffer + bufferLength - bufferPtr; + if (bufferLengthLeft < buffLen) + throw std::runtime_error("Required data is more, than in buffer available"); + std::memcpy(buffPtr, bufferPtr, buffLen); + bufferPtr += buffLen; +} + +void StreamBuffer::WriteData(unsigned char *buffPtr, size_t buffLen) { + size_t bufferLengthLeft = buffer + bufferLength - bufferPtr; + if (bufferLengthLeft < buffLen) + throw std::runtime_error("Required data is more, than in buffer available"); + std::memcpy(bufferPtr, buffPtr, buffLen); + bufferPtr += buffLen; +} + +StreamBuffer::StreamBuffer(unsigned char *data, size_t dataLen) { + buffer = new unsigned char[dataLen]; + bufferPtr = buffer; + bufferLength = dataLen; + std::memcpy(buffer, data, dataLen); +} + +StreamBuffer::StreamBuffer(size_t bufferLen) { + buffer = new unsigned char[bufferLen]; + bufferPtr = buffer; + bufferLength = bufferLen; + for (unsigned char *p = buffer; p != buffer + bufferLength; ++p) + *p = 0; +} + +StreamBuffer::~StreamBuffer() { + delete buffer; +} + +std::vector StreamBuffer::GetBuffer() { + return std::vector(buffer, buffer + bufferLength); +} + +void StreamCounter::WriteData(unsigned char *buffPtr, size_t buffLen) { + size += buffLen; +} + +StreamCounter::StreamCounter(size_t initialSize) : size(initialSize) { + +} + +StreamCounter::~StreamCounter() { + +} + +size_t StreamCounter::GetCountedSize() { + return size; +} + +void StreamSocket::ReadData(unsigned char *buffPtr, size_t buffLen) { + socket->Read(buffPtr, buffLen); +} + +void StreamSocket::WriteData(unsigned char *buffPtr, size_t buffLen) { + socket->Write(buffPtr, buffLen); +} + +StreamSocket::StreamSocket(Socket *socketPtr) : socket(socketPtr) { + +} \ No newline at end of file diff --git a/src/network/Stream.hpp b/src/network/Stream.hpp new file mode 100644 index 0000000..5babb08 --- /dev/null +++ b/src/network/Stream.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "Socket.hpp" +#include "../utility/Vector.hpp" + +class Stream { +protected: + template + void endswap(T &obj) { + unsigned char *raw = reinterpret_cast(&obj); + std::reverse(raw, raw + sizeof(T)); + } + + void endswap(unsigned char *arr, size_t arrLen) { + std::reverse(arr, arr + arrLen); + } + +public: + virtual ~Stream() {}; +}; + +class StreamInput : Stream { + virtual void ReadData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamInput() = default; + bool ReadBool(); + signed char ReadByte(); + unsigned char ReadUByte(); + short ReadShort(); + unsigned short ReadUShort(); + int ReadInt(); + long long ReadLong(); + float ReadFloat(); + double ReadDouble(); + std::string ReadString(); + std::string ReadChat(); + int ReadVarInt(); + long long ReadVarLong(); + std::vector ReadEntityMetadata(); + std::vector ReadSlot(); + std::vector ReadNbtTag(); + Vector ReadPosition(); + unsigned char ReadAngle(); + std::vector ReadUuid(); + std::vector ReadByteArray(size_t arrLength); +}; + +class StreamOutput : Stream { + virtual void WriteData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamOutput() = default; + void WriteBool(bool value); + void WriteByte(signed char value); + void WriteUByte(unsigned char value); + void WriteShort(short value); + void WriteUShort(unsigned short value); + void WriteInt(int value); + void WriteLong(long long value); + void WriteFloat(float value); + void WriteDouble(double value); + void WriteString(std::string value); + void WriteChat(std::string value); + void WriteVarInt(int value); + void WriteVarLong(long long value); + void WriteEntityMetadata(std::vector value); + void WriteSlot(std::vector value); + void WriteNbtTag(std::vector value); + void WritePosition(Vector value); + void WriteAngle(unsigned char value); + void WriteUuid(std::vector value); + void WriteByteArray(std::vector value); +}; + +class StreamBuffer : public StreamInput, public StreamOutput { + unsigned char *buffer; + unsigned char *bufferPtr; + size_t bufferLength; + + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + +public: + StreamBuffer(unsigned char *data, size_t dataLen); + StreamBuffer(size_t bufferLen); + ~StreamBuffer(); + + std::vector GetBuffer(); +}; + +class StreamCounter : public StreamOutput { + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + + size_t size; +public: + StreamCounter(size_t initialSize = 0); + ~StreamCounter(); + + size_t GetCountedSize(); +}; + +class StreamSocket : public StreamInput, public StreamOutput { + Socket *socket; + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; +public: + StreamSocket(Socket *socketPtr); + ~StreamSocket() = default; +}; \ No newline at end of file -- cgit v1.2.3