summaryrefslogtreecommitdiffstats
path: root/src/Protocol
diff options
context:
space:
mode:
Diffstat (limited to 'src/Protocol')
-rw-r--r--src/Protocol/CMakeLists.txt2
-rw-r--r--src/Protocol/ForgeHandshake.cpp363
-rw-r--r--src/Protocol/ForgeHandshake.h60
-rw-r--r--src/Protocol/Protocol_1_10.cpp2
-rw-r--r--src/Protocol/Protocol_1_11.cpp2
-rw-r--r--src/Protocol/Protocol_1_12.cpp2
-rw-r--r--src/Protocol/Protocol_1_8.cpp1
-rw-r--r--src/Protocol/Protocol_1_9.cpp49
8 files changed, 471 insertions, 10 deletions
diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt
index f21c81f83..00ffeb255 100644
--- a/src/Protocol/CMakeLists.txt
+++ b/src/Protocol/CMakeLists.txt
@@ -5,6 +5,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
SET (SRCS
Authenticator.cpp
ChunkDataSerializer.cpp
+ ForgeHandshake.cpp
MojangAPI.cpp
Packetizer.cpp
Protocol_1_8.cpp
@@ -18,6 +19,7 @@ SET (SRCS
SET (HDRS
Authenticator.h
ChunkDataSerializer.h
+ ForgeHandshake.h
MojangAPI.h
Packetizer.h
Protocol.h
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;
+ }
+ }
+}
diff --git a/src/Protocol/ForgeHandshake.h b/src/Protocol/ForgeHandshake.h
new file mode 100644
index 000000000..f7be9e958
--- /dev/null
+++ b/src/Protocol/ForgeHandshake.h
@@ -0,0 +1,60 @@
+
+// ForgeHandshake.h
+
+// Implements Forge protocol handshaking
+
+#pragma once
+
+#include <stddef.h>
+#include "UUID.h"
+#include "json/json.h"
+
+// fwd:
+class cClientHandle;
+
+
+
+
+
+class cForgeHandshake
+{
+public:
+ /** True if the client advertised itself as a Forge client. */
+ bool m_IsForgeClient;
+
+ cForgeHandshake(cClientHandle * client);
+
+ /** Add the registered Forge mods to the server ping list packet. */
+ void AugmentServerListPing(Json::Value & ResponseValue);
+
+ /** Begin the Forge Modloader Handshake (FML|HS) sequence. */
+ void BeginForgeHandshake(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties);
+
+ /** Send the ServerHello packet in the Forge handshake. */
+ void SendServerHello();
+
+ /** Process received data from the client advancing the Forge handshake. */
+ void DataReceived(cClientHandle * a_Client, const char * a_Data, size_t a_Size);
+
+private:
+ /** True if the Forge handshake is in an errored state. */
+ bool m_Errored;
+
+ /** The client handle undergoing this Forge handshake. */
+ cClientHandle * m_Client;
+
+ /** Values saved from BeginForgeHandshake() for continuing the normal handshake after Forge completes. */
+ AString m_Name;
+ cUUID m_UUID;
+ Json::Value m_Properties;
+
+ void HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size);
+ void HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size);
+ void HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size);
+
+ /** Set errored state to prevent further handshake message processing. */
+ void SetError(const AString & message);
+
+ /** Parse the client ModList packet of installed Forge mods and versions. */
+ AStringMap ParseModList(const char * a_Data, size_t a_Size);
+};
diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp
index 67b76872a..7f86d4bdc 100644
--- a/src/Protocol/Protocol_1_10.cpp
+++ b/src/Protocol/Protocol_1_10.cpp
@@ -15,6 +15,7 @@ Implements the 1.10 protocol classes:
#include "../Root.h"
#include "../Server.h"
+#include "../ClientHandle.h"
#include "../WorldStorage/FastNBT.h"
@@ -346,6 +347,7 @@ void cProtocol_1_10_0::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp
index c562503bd..b9b6e9ac3 100644
--- a/src/Protocol/Protocol_1_11.cpp
+++ b/src/Protocol/Protocol_1_11.cpp
@@ -586,6 +586,7 @@ void cProtocol_1_11_0::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
@@ -1209,6 +1210,7 @@ void cProtocol_1_11_1::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp
index 43ab682eb..a8e38a4e0 100644
--- a/src/Protocol/Protocol_1_12.cpp
+++ b/src/Protocol/Protocol_1_12.cpp
@@ -399,6 +399,7 @@ void cProtocol_1_12::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
@@ -1667,6 +1668,7 @@ void cProtocol_1_12_1::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp
index 0e7abfd7a..c77af1029 100644
--- a/src/Protocol/Protocol_1_8.cpp
+++ b/src/Protocol/Protocol_1_8.cpp
@@ -2169,6 +2169,7 @@ void cProtocol_1_8_0::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp
index 7fc6cf5f1..c6e007984 100644
--- a/src/Protocol/Protocol_1_9.cpp
+++ b/src/Protocol/Protocol_1_9.cpp
@@ -124,21 +124,46 @@ cProtocol_1_9_0::cProtocol_1_9_0(cClientHandle * a_Client, const AString & a_Ser
m_IsEncrypted(false)
{
- // BungeeCord handling:
- // If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field:
- // hostname\00ip-address\00uuid\00profile-properties-as-json
AStringVector Params;
- if (cRoot::Get()->GetServer()->ShouldAllowBungeeCord() && SplitZeroTerminatedStrings(a_ServerAddress, Params) && (Params.size() == 4))
+ SplitZeroTerminatedStrings(a_ServerAddress, Params);
+
+ if (Params.size() >= 2)
{
- LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
m_ServerAddress = Params[0];
- m_Client->SetIPString(Params[1]);
- cUUID UUID;
- UUID.FromString(Params[2]);
- m_Client->SetUUID(UUID);
+ if (Params[1] == "FML")
+ {
+ LOGD("Forge client connected!");
+ m_Client->SetIsForgeClient();
+ }
+ else if (Params.size() == 4)
+ {
+ if (cRoot::Get()->GetServer()->ShouldAllowBungeeCord())
+ {
+ // BungeeCord handling:
+ // If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field:
+ // hostname\00ip-address\00uuid\00profile-properties-as-json
+
+ LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
+
+ m_Client->SetIPString(Params[1]);
+
+ cUUID UUID;
+ UUID.FromString(Params[2]);
+ m_Client->SetUUID(UUID);
- m_Client->SetProperties(Params[3]);
+ m_Client->SetProperties(Params[3]);
+ }
+ else
+ {
+ LOG("BungeeCord is disabled, but client sent additional data, set AllowBungeeCord=1 if you want to allow it");
+ }
+ }
+ else
+ {
+ LOG("Unknown additional data sent in server address (BungeeCord/FML?): " SIZE_T_FMT " parameters", Params.size());
+ // TODO: support FML + BungeeCord? (what parameters does it send in that case?) https://github.com/SpigotMC/BungeeCord/issues/899
+ }
}
// Create the comm log file, if so requested:
@@ -2194,6 +2219,7 @@ void cProtocol_1_9_0::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
@@ -4204,6 +4230,7 @@ void cProtocol_1_9_1::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
@@ -4261,6 +4288,7 @@ void cProtocol_1_9_2::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
@@ -4318,6 +4346,7 @@ void cProtocol_1_9_4::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
+ m_Client->ForgeAugmentServerListPing(ResponseValue);
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());