diff options
author | satoshinm <snmatsutake@yahoo.co.jp> | 2017-08-27 23:10:20 +0200 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2017-08-27 23:10:20 +0200 |
commit | 6bc503151746ea05842009983c7de932fa80cd03 (patch) | |
tree | a7ce87c25b2acb9c5f76cd1a25180b77ebf16f26 /src/Protocol/ForgeHandshake.cpp | |
parent | Implement anvil chunk sparsing (diff) | |
download | cuberite-6bc503151746ea05842009983c7de932fa80cd03.tar cuberite-6bc503151746ea05842009983c7de932fa80cd03.tar.gz cuberite-6bc503151746ea05842009983c7de932fa80cd03.tar.bz2 cuberite-6bc503151746ea05842009983c7de932fa80cd03.tar.lz cuberite-6bc503151746ea05842009983c7de932fa80cd03.tar.xz cuberite-6bc503151746ea05842009983c7de932fa80cd03.tar.zst cuberite-6bc503151746ea05842009983c7de932fa80cd03.zip |
Diffstat (limited to 'src/Protocol/ForgeHandshake.cpp')
-rw-r--r-- | src/Protocol/ForgeHandshake.cpp | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/Protocol/ForgeHandshake.cpp b/src/Protocol/ForgeHandshake.cpp new file mode 100644 index 000000000..48b89baf4 --- /dev/null +++ b/src/Protocol/ForgeHandshake.cpp @@ -0,0 +1,363 @@ + +// ForgeHandshake.cpp + +// Implements Forge protocol handshaking + +#include "Globals.h" +#include "ForgeHandshake.h" +#include "json/json.h" +#include "../Server.h" +#include "../ByteBuffer.h" +#include "../Bindings/PluginManager.h" +#include "../ClientHandle.h" +#include "../Root.h" + + +/** Discriminator byte values prefixing the FML|HS packets to determine their type. */ +namespace Discriminator +{ + static const Int8 ServerHello = 0; + static const Int8 ClientHello = 1; + static const Int8 ModList = 2; + static const Int8 RegistryData = 3; + // static const Int8 HandshakeReset = -2; + static const Int8 HandshakeAck = -1; +} + +/** Client handshake state phases. */ +namespace ClientPhase +{ + static const Int8 WAITINGSERVERDATA = 2; + static const Int8 WAITINGSERVERCOMPLETE = 3; + static const Int8 PENDINGCOMPLETE = 4; + static const Int8 COMPLETE = 5; +} + +/** Server handshake state phases. */ +namespace ServerPhase +{ + static const Int8 WAITINGCACK = 2; + static const Int8 COMPLETE = 3; +} + + + + + +cForgeHandshake::cForgeHandshake(cClientHandle *a_Client) : + m_IsForgeClient(false), + m_Errored(false), + m_Client(a_Client) +{ +} + + + + + +void cForgeHandshake::SetError(const AString & message) +{ + LOGD("Forge handshake error: %s", message.c_str()); + m_Errored = true; +} + + + + + +void cForgeHandshake::AugmentServerListPing(Json::Value & a_ResponseValue) +{ + auto ProtocolVersion = m_Client->GetProtocolVersion(); + auto & Mods = cRoot::Get()->GetServer()->GetRegisteredForgeMods(ProtocolVersion); + + if (Mods.empty()) + { + return; + } + + LOGD("Received server ping from version: %d", ProtocolVersion); + + Json::Value Modinfo; + Modinfo["type"] = "FML"; + + Json::Value ModList(Json::arrayValue); + for (auto & item: Mods) + { + Json::Value Mod; + Mod["modid"] = item.first; + Mod["version"] = item.second; + ModList.append(Mod); + } + Modinfo["modList"] = ModList; + + a_ResponseValue["modinfo"] = Modinfo; +} + + + + + +void cForgeHandshake::BeginForgeHandshake(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties) +{ + ASSERT(m_IsForgeClient); + + m_Name = a_Name; + m_UUID = a_UUID; + m_Properties = a_Properties; + + static const std::array<AString, 5> Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }}; + AString ChannelsString; + for (auto & Channel: Channels) + { + ChannelsString.append(Channel); + ChannelsString.push_back('\0'); + } + + m_Client->SendPluginMessage("REGISTER", ChannelsString); + SendServerHello(); +} + + + + + +void cForgeHandshake::SendServerHello() +{ + AString Message; + cByteBuffer Buf(6); + // Discriminator | Byte | Always 0 for ServerHello + Buf.WriteBEInt8(Discriminator::ServerHello); + // FML protocol Version | Byte | Determined from NetworkRegistery. Currently 2. + Buf.WriteBEInt8(2); + // Dimension TODO + Buf.WriteBEInt32(0); + Buf.ReadAll(Message); + + m_Client->SendPluginMessage("FML|HS", Message); +} + + + + + +AStringMap cForgeHandshake::ParseModList(const char * a_Data, size_t a_Size) +{ + AStringMap Mods; + + if (a_Size < 4) + { + SetError(Printf("ParseModList invalid packet, missing length (size = " SIZE_T_FMT ")", a_Size)); + return Mods; + } + + cByteBuffer Buf(a_Size); + Buf.Write(a_Data, a_Size); + UInt32 NumMods; + if (!Buf.ReadVarInt32(NumMods)) + { + SetError("ParseModList failed to read mod count"); + return Mods; + } + + for (UInt32 i = 0; i < NumMods; ++i) + { + AString Name, Version; + if (!Buf.ReadVarUTF8String(Name)) + { + SetError(Printf("ParseModList failed to read mod name at i = %d", i)); + break; + } + if (!Buf.ReadVarUTF8String(Version)) + { + SetError(Printf("ParseModList failed to read mod version at i = %d", i)); + break; + } + Mods.insert({Name, Version}); + } + + return Mods; +} + + + + +void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +{ + if (a_Size == 2) + { + int FmlProtocolVersion = a_Data[1]; + LOGD("Received ClientHello with FML protocol version %d", FmlProtocolVersion); + if (FmlProtocolVersion != 2) + { + SetError(Printf("Unsupported FML client protocol version received in ClientHello: %d", FmlProtocolVersion)); + } + } + else + { + SetError(Printf("Received unexpected length of ClientHello: " SIZE_T_FMT, a_Size)); + } +} + + + + +void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +{ + LOGD("Received ModList"); + + auto ClientMods = ParseModList(a_Data + 1, a_Size - 1); + AString ClientModsString; + for (auto & item: ClientMods) + { + AppendPrintf(ClientModsString, "%s@%s, ", item.first.c_str(), item.second.c_str()); + } + + LOG("Client connected with " SIZE_T_FMT " mods: %s", ClientMods.size(), ClientModsString.c_str()); + + m_Client->m_ForgeMods = ClientMods; + + // Let the plugins know about this event, they may refuse the player: + if (cRoot::Get()->GetPluginManager()->CallHookLoginForge(*a_Client, ClientMods)) + { + SetError("Modded client refused by plugin"); + return; + } + + // Send server ModList + + // Send server-side Forge mods registered by plugins + const auto & ServerMods = m_Client->GetForgeMods(); + + const auto ModCount = ServerMods.size(); + + cByteBuffer Buf(256 * ModCount); + + Buf.WriteBEInt8(Discriminator::ModList); + Buf.WriteVarInt32(static_cast<UInt32>(ModCount)); + for (const auto & item: ServerMods) + { + Buf.WriteVarUTF8String(item.first); // name + Buf.WriteVarUTF8String(item.second); // version + } + AString ServerModList; + Buf.ReadAll(ServerModList); + + m_Client->SendPluginMessage("FML|HS", ServerModList); +} + + + + +void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +{ + if (a_Size != 2) + { + SetError(Printf("Unexpected HandshakeAck packet length: " SIZE_T_FMT "", a_Size)); + return; + } + + auto Phase = a_Data[1]; + LOGD("Received client HandshakeAck with phase = %d", Phase); + + switch (Phase) + { + case ClientPhase::WAITINGSERVERDATA: + { + cByteBuffer Buf(1024); + Buf.WriteBEInt8(Discriminator::RegistryData); + + // TODO: send real registry data + bool HasMore = false; + AString RegistryName = "potions"; + UInt32 NumIDs = 0; + UInt32 NumSubstitutions = 0; + UInt32 NumDummies = 0; + + Buf.WriteBool(HasMore); + Buf.WriteVarUTF8String(RegistryName); + Buf.WriteVarInt32(NumIDs); + Buf.WriteVarInt32(NumSubstitutions); + Buf.WriteVarInt32(NumDummies); + + AString RegistryData; + Buf.ReadAll(RegistryData); + m_Client->SendPluginMessage("FML|HS", RegistryData); + break; + } + + case ClientPhase::WAITINGSERVERCOMPLETE: + { + LOGD("Client finished receiving registry data; acknowledging"); + + AString Ack; + Ack.push_back(Discriminator::HandshakeAck); + Ack.push_back(ServerPhase::WAITINGCACK); + m_Client->SendPluginMessage("FML|HS", Ack); + break; + } + + case ClientPhase::PENDINGCOMPLETE: + { + LOGD("Client is pending completion; sending complete ack"); + + AString Ack; + Ack.push_back(Discriminator::HandshakeAck); + Ack.push_back(ServerPhase::COMPLETE); + m_Client->SendPluginMessage("FML|HS", Ack); + + break; + } + + case ClientPhase::COMPLETE: + { + // Now finish logging in + m_Client->FinishAuthenticate(m_Name, m_UUID, m_Properties); + break; + } + + default: + { + SetError(Printf("Received unknown phase in Forge handshake acknowledgement: %d", Phase)); + break; + } + } +} + + + + + +void cForgeHandshake::DataReceived(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +{ + if (!m_IsForgeClient) + { + SetError(Printf("Received unexpected Forge data from non-Forge client (" SIZE_T_FMT " bytes)", a_Size)); + return; + } + if (m_Errored) + { + LOGD("Received unexpected Forge data when in errored state, ignored"); + return; + } + + if (a_Size <= 1) + { + SetError(Printf("Received unexpectedly short Forge data (" SIZE_T_FMT " bytes)", a_Size)); + return; + } + + auto Discriminator = a_Data[0]; + + switch (Discriminator) + { + case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data, a_Size); break; + case Discriminator::ModList: HandleModList(a_Client, a_Data, a_Size); break; + case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data, a_Size); break; + + default: + { + SetError(Printf("Unexpected Forge packet %d received", Discriminator)); + return; + } + } +} |