Commit is being made to allow additions of GPL3+ code previously un-addable. With these changes, contributions back to cuberite are possible with the backporting exemtion, as well as adding stuff in minetest with minetest code properly being read through and implimented to upgrade it to GPL3 from GPL2. project still has Apache2.0 license and credits to all its contributers, but now has the freedom of GPL3+ and all the code that can be implimented and shared with it.
2965 lines
81 KiB
C++
2965 lines
81 KiB
C++
|
|
/*
|
|
* Copyright 2011-2022 Cuberite Contributors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// Connection.cpp
|
|
|
|
// Interfaces to the cConnection class representing a single pair of connected sockets
|
|
|
|
#include "Globals.h"
|
|
#include "Connection.h"
|
|
#include "Server.h"
|
|
#include <iostream>
|
|
#include "mbedTLS++/CryptoKey.h"
|
|
#include "../../src/Logger.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <direct.h> // For _mkdir()
|
|
#else
|
|
#include <sys/stat.h> // for mkdir
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/** When defined, the following macro causes a sleep after each parsed packet (DEBUG-mode only) */
|
|
// #define SLEEP_AFTER_PACKET
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(_DEBUG) && defined(SLEEP_AFTER_PACKET)
|
|
#define DebugSleep Sleep
|
|
#else
|
|
#define DebugSleep(X)
|
|
#endif // else _DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
#define HANDLE_CLIENT_PACKET_READ(Proc, Type, Var) \
|
|
Type Var; \
|
|
do { \
|
|
if (!m_ClientBuffer.Proc(Var)) \
|
|
{ \
|
|
return false; \
|
|
} \
|
|
} while(false)
|
|
|
|
#define HANDLE_SERVER_PACKET_READ(Proc, Type, Var) \
|
|
Type Var; \
|
|
do { \
|
|
if (!m_ServerBuffer.Proc(Var)) \
|
|
{ \
|
|
return false; \
|
|
} \
|
|
} while(false)
|
|
|
|
#define CLIENTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client")
|
|
#define SERVERSEND(...) SendData(m_ServerSocket, __VA_ARGS__, "Server")
|
|
#define CLIENTENCRYPTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client") // The client conn is always unencrypted
|
|
#define SERVERENCRYPTSEND(...) SendEncryptedData(m_ServerSocket, m_ServerEncryptor, __VA_ARGS__, "Server")
|
|
|
|
#define COPY_TO_SERVER() \
|
|
do { \
|
|
ContiguousByteBuffer ToServer; \
|
|
m_ClientBuffer.ReadAgain(ToServer); \
|
|
switch (m_ServerState) \
|
|
{ \
|
|
case csUnencrypted: \
|
|
{ \
|
|
SERVERSEND(ToServer); \
|
|
break; \
|
|
} \
|
|
case csEncryptedUnderstood: \
|
|
case csEncryptedUnknown: \
|
|
{ \
|
|
SERVERENCRYPTSEND(ToServer); \
|
|
break; \
|
|
} \
|
|
case csWaitingForEncryption: \
|
|
{ \
|
|
Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \
|
|
m_ServerEncryptionBuffer += ToServer; \
|
|
break; \
|
|
} \
|
|
} \
|
|
DebugSleep(50); \
|
|
} while (false)
|
|
|
|
#define COPY_TO_CLIENT() \
|
|
do { \
|
|
ContiguousByteBuffer ToClient; \
|
|
m_ServerBuffer.ReadAgain(ToClient); \
|
|
switch (m_ClientState) \
|
|
{ \
|
|
case csUnencrypted: \
|
|
{ \
|
|
CLIENTSEND(ToClient); \
|
|
break; \
|
|
} \
|
|
case csEncryptedUnderstood: \
|
|
case csEncryptedUnknown: \
|
|
{ \
|
|
CLIENTENCRYPTSEND(ToClient); \
|
|
break; \
|
|
} \
|
|
case csWaitingForEncryption: \
|
|
{ \
|
|
break; \
|
|
} \
|
|
\
|
|
} \
|
|
DebugSleep(50); \
|
|
} while (false)
|
|
|
|
#define HANDLE_CLIENT_READ(Proc) \
|
|
do { \
|
|
if (!Proc) \
|
|
{ \
|
|
ContiguousByteBuffer Leftover; \
|
|
m_ClientBuffer.ReadAgain(Leftover); \
|
|
DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \
|
|
m_ClientBuffer.ResetRead(); \
|
|
return true; \
|
|
} \
|
|
} while (false)
|
|
|
|
#define HANDLE_SERVER_READ(Proc) \
|
|
do { \
|
|
if (!Proc) \
|
|
{ \
|
|
m_ServerBuffer.ResetRead(); \
|
|
return true; \
|
|
} \
|
|
} while (false)
|
|
|
|
|
|
|
|
|
|
|
|
typedef unsigned char Byte;
|
|
|
|
|
|
|
|
|
|
// fwd declarations, to avoid clang warnings:
|
|
AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor = 32);
|
|
|
|
|
|
|
|
|
|
|
|
AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor)
|
|
{
|
|
return Printf("<%d, %d, %d> ~ {%.02f, %.02f, %.02f}",
|
|
a_X, a_Y, a_Z,
|
|
static_cast<double>(a_X) / a_Divisor, static_cast<double>(a_Y) / a_Divisor, static_cast<double>(a_Z) / a_Divisor
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct sCoords
|
|
{
|
|
int x, y, z;
|
|
|
|
sCoords(int a_X, int a_Y, int a_Z) : x(a_X), y(a_Y), z(a_Z) {}
|
|
} ;
|
|
|
|
|
|
|
|
|
|
|
|
struct sChunkMeta
|
|
{
|
|
int m_ChunkX, m_ChunkZ;
|
|
short m_PrimaryBitmap;
|
|
short m_AddBitmap;
|
|
sChunkMeta(int a_ChunkX, int a_ChunkZ, short a_PrimaryBitmap, short a_AddBitmap) :
|
|
m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_PrimaryBitmap(a_PrimaryBitmap), m_AddBitmap(a_AddBitmap)
|
|
{
|
|
}
|
|
} ;
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// cConnection:
|
|
|
|
cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) :
|
|
m_ItemIdx(0),
|
|
m_LogFile(nullptr),
|
|
m_Server(a_Server),
|
|
m_ClientSocket(a_ClientSocket),
|
|
m_ServerSocket(-1),
|
|
m_BeginTick(std::chrono::steady_clock::now()),
|
|
m_ClientState(csUnencrypted),
|
|
m_ServerState(csUnencrypted),
|
|
m_Nonce(0),
|
|
m_ClientBuffer(1024 KiB),
|
|
m_ServerBuffer(1024 KiB),
|
|
m_HasClientPinged(false),
|
|
m_ServerProtocolState(-1),
|
|
m_ClientProtocolState(-1),
|
|
m_IsServerEncrypted(false)
|
|
{
|
|
// Create the Logs subfolder, if not already created:
|
|
#if defined(_WIN32)
|
|
_mkdir("Logs");
|
|
#else
|
|
mkdir("Logs", 0777);
|
|
#endif
|
|
|
|
Printf(m_LogNameBase, "Logs/Log_%d_%d", static_cast<int>(time(nullptr)), static_cast<int>(a_ClientSocket));
|
|
AString fnam(m_LogNameBase);
|
|
fnam.append(".log");
|
|
#ifdef _WIN32
|
|
fopen_s(&m_LogFile, fnam.c_str(), "w");
|
|
#else
|
|
m_LogFile = fopen(fnam.c_str(), "w");
|
|
#endif
|
|
Log("Log file created");
|
|
printf("Connection is logged to file \"%s\"\n", fnam.c_str());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cConnection::~cConnection()
|
|
{
|
|
fclose(m_LogFile);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cConnection::Run(void)
|
|
{
|
|
if (!ConnectToServer())
|
|
{
|
|
Log("Cannot connect to server; aborting");
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
fd_set ReadFDs;
|
|
FD_ZERO(&ReadFDs);
|
|
FD_SET(m_ServerSocket, &ReadFDs);
|
|
FD_SET(m_ClientSocket, &ReadFDs);
|
|
SOCKET MaxSocket = std::max(m_ServerSocket, m_ClientSocket);
|
|
int res = select(MaxSocket + 1, &ReadFDs, nullptr, nullptr, nullptr);
|
|
if (res <= 0)
|
|
{
|
|
printf("select() failed: %d; aborting client", SocketError);
|
|
break;
|
|
}
|
|
if (FD_ISSET(m_ServerSocket, &ReadFDs))
|
|
{
|
|
if (!RelayFromServer())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (FD_ISSET(m_ClientSocket, &ReadFDs))
|
|
{
|
|
if (!RelayFromClient())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Log("Relaying ended, closing sockets");
|
|
closesocket(m_ServerSocket);
|
|
closesocket(m_ClientSocket);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cConnection::vLog(const char * a_Format, fmt::printf_args a_ArgList)
|
|
{
|
|
// Log to file:
|
|
cCSLock Lock(m_CSLog);
|
|
fmt::fprintf(m_LogFile, "[%5.3f] ", GetRelativeTime());
|
|
fmt::vfprintf(m_LogFile, a_Format, a_ArgList);
|
|
fmt::fprintf(m_LogFile, "\n");
|
|
#ifdef _DEBUG
|
|
fflush(m_LogFile);
|
|
#endif // _DEBUG
|
|
|
|
// Log to screen:
|
|
// std::cout << FullMsg;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cConnection::vDataLog(const void * a_Data, size_t a_Size, const char * a_Format, fmt::printf_args a_ArgList)
|
|
{
|
|
AString Hex;
|
|
CreateHexDump(Hex, a_Data, a_Size, 16);
|
|
|
|
// Log to file:
|
|
cCSLock Lock(m_CSLog);
|
|
fmt::fprintf(m_LogFile, "[%5.3f] ", GetRelativeTime());
|
|
fmt::vfprintf(m_LogFile, a_Format, a_ArgList);
|
|
fmt::fprintf(m_LogFile, "\n%s\n", Hex);
|
|
|
|
// Log to screen:
|
|
// std::cout << FullMsg;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cConnection::LogFlush(void)
|
|
{
|
|
fflush(m_LogFile);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::ConnectToServer(void)
|
|
{
|
|
m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (m_ServerSocket == INVALID_SOCKET)
|
|
{
|
|
return false;
|
|
}
|
|
sockaddr_in localhost;
|
|
localhost.sin_family = AF_INET;
|
|
localhost.sin_port = htons(m_Server.GetConnectPort());
|
|
localhost.sin_addr.s_addr = htonl(0x7f000001); // localhost
|
|
if (connect(m_ServerSocket, reinterpret_cast<const sockaddr *>(&localhost), sizeof(localhost)) != 0)
|
|
{
|
|
printf("connection to server failed: %d\n", SocketError);
|
|
return false;
|
|
}
|
|
Log("Connected to SERVER");
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::RelayFromServer(void)
|
|
{
|
|
char Buffer[64 KiB];
|
|
int res = static_cast<int>(recv(m_ServerSocket, Buffer, sizeof(Buffer), 0)); // recv returns int on windows, ssize_t on linux
|
|
if (res <= 0)
|
|
{
|
|
Log("Server closed the socket: %d; %d; aborting connection", res, SocketError);
|
|
return false;
|
|
}
|
|
|
|
DataLog(Buffer, static_cast<size_t>(res), "Received %d bytes from the SERVER", res);
|
|
|
|
switch (m_ServerState)
|
|
{
|
|
case csUnencrypted:
|
|
case csWaitingForEncryption:
|
|
{
|
|
return DecodeServersPackets(Buffer, res);
|
|
}
|
|
case csEncryptedUnderstood:
|
|
{
|
|
m_ServerDecryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), static_cast<size_t>(res));
|
|
DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res);
|
|
return DecodeServersPackets(Buffer, res);
|
|
}
|
|
case csEncryptedUnknown:
|
|
{
|
|
m_ServerDecryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), static_cast<size_t>(res));
|
|
DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the SERVER", res);
|
|
return CLIENTSEND({ reinterpret_cast<const std::byte *>(Buffer), static_cast<size_t>(res) });
|
|
}
|
|
}
|
|
ASSERT(!"Unhandled server state while relaying from server");
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::RelayFromClient(void)
|
|
{
|
|
char Buffer[64 KiB];
|
|
int res = static_cast<int>(recv(m_ClientSocket, Buffer, sizeof(Buffer), 0)); // recv returns int on Windows, ssize_t on Linux
|
|
if (res <= 0)
|
|
{
|
|
Log("Client closed the socket: %d; %d; aborting connection", res, SocketError);
|
|
return false;
|
|
}
|
|
|
|
DataLog(Buffer, static_cast<size_t>(res), "Received %d bytes from the CLIENT", res);
|
|
|
|
switch (m_ClientState)
|
|
{
|
|
case csUnencrypted:
|
|
case csWaitingForEncryption:
|
|
{
|
|
return DecodeClientsPackets(Buffer, res);
|
|
}
|
|
case csEncryptedUnderstood:
|
|
{
|
|
return DecodeClientsPackets(Buffer, res);
|
|
}
|
|
case csEncryptedUnknown:
|
|
{
|
|
DataLog(Buffer, static_cast<size_t>(res), "Decrypted %d bytes from the CLIENT", res);
|
|
m_ServerEncryptor.ProcessData(reinterpret_cast<std::byte *>(Buffer), static_cast<size_t>(res));
|
|
return SERVERSEND({ reinterpret_cast<const std::byte *>(Buffer), static_cast<size_t>(res) });
|
|
}
|
|
}
|
|
ASSERT(!"Unhandled server state while relaying from client");
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double cConnection::GetRelativeTime(void)
|
|
{
|
|
Int64 msec = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_BeginTick).count();
|
|
return static_cast<double>(msec) / 1000;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::SendData(SOCKET a_Socket, const ContiguousByteBufferView a_Data, const char * a_Peer)
|
|
{
|
|
DataLog(a_Data.data(), a_Data.size(), "Sending data to %s, %zu bytes", a_Peer, a_Data.size());
|
|
|
|
int res = static_cast<int>(send(a_Socket, reinterpret_cast<const char *>(a_Data.data()), a_Data.size(), 0)); // Windows uses int for a_Size, Linux uses size_t; but Windows doesn't complain. Return type is int on Windows and ssize_t on Linux
|
|
if (res <= 0)
|
|
{
|
|
Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, SocketError);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer)
|
|
{
|
|
ContiguousByteBuffer All;
|
|
a_Data.ReadAll(All);
|
|
a_Data.CommitRead();
|
|
return SendData(a_Socket, All, a_Peer);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, ContiguousByteBuffer & a_Data, const char * a_Peer)
|
|
{
|
|
DataLog(a_Data.data(), a_Data.size(), "Encrypting %zu bytes to %s", a_Data.size(), a_Peer);
|
|
|
|
a_Encryptor.ProcessData(a_Data.data(), a_Data.size());
|
|
return SendData(a_Socket, a_Data, a_Peer);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer)
|
|
{
|
|
ContiguousByteBuffer All;
|
|
a_Data.ReadAll(All);
|
|
a_Data.CommitRead();
|
|
return SendEncryptedData(a_Socket, a_Encryptor, All, a_Peer);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
|
|
{
|
|
if (!m_ClientBuffer.Write(a_Data, static_cast<size_t>(a_Size)))
|
|
{
|
|
Log("Too much queued data for the server, aborting connection");
|
|
return false;
|
|
}
|
|
|
|
while (m_ClientBuffer.CanReadBytes(1))
|
|
{
|
|
UInt32 PacketLen;
|
|
if (
|
|
!m_ClientBuffer.ReadVarInt(PacketLen) ||
|
|
!m_ClientBuffer.CanReadBytes(PacketLen)
|
|
)
|
|
{
|
|
// Not a complete packet yet
|
|
break;
|
|
}
|
|
UInt32 PacketType, PacketReadSoFar;
|
|
PacketReadSoFar = static_cast<UInt32>(m_ClientBuffer.GetReadableSpace());
|
|
VERIFY(m_ClientBuffer.ReadVarInt(PacketType));
|
|
PacketReadSoFar -= m_ClientBuffer.GetReadableSpace();
|
|
Log("Decoding client's packets, there are now %u bytes in the queue; next packet is 0x%02x, %u bytes long",
|
|
static_cast<unsigned>(m_ClientBuffer.GetReadableSpace()), PacketType, PacketLen
|
|
);
|
|
switch (m_ClientProtocolState)
|
|
{
|
|
case -1:
|
|
{
|
|
// No initial handshake received yet
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_CLIENT_READ(HandleClientHandshake()); break;
|
|
default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
}
|
|
break;
|
|
} // case -1
|
|
|
|
case 1:
|
|
{
|
|
// Status query
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_CLIENT_READ(HandleClientStatusRequest()); break;
|
|
case 0x01: HANDLE_CLIENT_READ(HandleClientStatusPing()); break;
|
|
default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
// Login
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_CLIENT_READ(HandleClientLoginStart()); break;
|
|
case 0x01: HANDLE_CLIENT_READ(HandleClientLoginEncryptionKeyResponse()); break;
|
|
default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
// Game:
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_CLIENT_READ(HandleClientKeepAlive()); break;
|
|
case 0x01: HANDLE_CLIENT_READ(HandleClientChatMessage()); break;
|
|
case 0x02: HANDLE_CLIENT_READ(HandleClientUseEntity()); break;
|
|
case 0x03: HANDLE_CLIENT_READ(HandleClientPlayerOnGround()); break;
|
|
case 0x04: HANDLE_CLIENT_READ(HandleClientPlayerPosition()); break;
|
|
case 0x05: HANDLE_CLIENT_READ(HandleClientPlayerLook()); break;
|
|
case 0x06: HANDLE_CLIENT_READ(HandleClientPlayerPositionLook()); break;
|
|
case 0x07: HANDLE_CLIENT_READ(HandleClientBlockDig()); break;
|
|
case 0x08: HANDLE_CLIENT_READ(HandleClientBlockPlace()); break;
|
|
case 0x09: HANDLE_CLIENT_READ(HandleClientSlotSelect()); break;
|
|
case 0x0a: HANDLE_CLIENT_READ(HandleClientAnimation()); break;
|
|
case 0x0b: HANDLE_CLIENT_READ(HandleClientEntityAction()); break;
|
|
case 0x0d: HANDLE_CLIENT_READ(HandleClientWindowClose()); break;
|
|
case 0x0e: HANDLE_CLIENT_READ(HandleClientWindowClick()); break;
|
|
case 0x10: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction()); break;
|
|
case 0x12: HANDLE_CLIENT_READ(HandleClientUpdateSign()); break;
|
|
case 0x13: HANDLE_CLIENT_READ(HandleClientPlayerAbilities()); break;
|
|
case 0x14: HANDLE_CLIENT_READ(HandleClientTabCompletion()); break;
|
|
case 0x15: HANDLE_CLIENT_READ(HandleClientLocaleAndView()); break;
|
|
case 0x16: HANDLE_CLIENT_READ(HandleClientClientStatuses()); break;
|
|
case 0x17: HANDLE_CLIENT_READ(HandleClientPluginMessage()); break;
|
|
default: HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
}
|
|
break;
|
|
} // case 3 - Game
|
|
|
|
default:
|
|
{
|
|
Log("Receiving server packets while in an unknown protocol state (%d)!", m_ClientProtocolState);
|
|
HANDLE_CLIENT_READ(HandleClientUnknownPacket(PacketType, PacketLen, PacketReadSoFar));
|
|
break;
|
|
}
|
|
} // switch (m_ProtocolState)
|
|
m_ClientBuffer.CommitRead();
|
|
} // while (true)
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size)
|
|
{
|
|
if (!m_ServerBuffer.Write(a_Data, static_cast<size_t>(a_Size)))
|
|
{
|
|
Log("Too much queued data for the client, aborting connection");
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
(m_ServerState == csEncryptedUnderstood) &&
|
|
(m_ClientState == csUnencrypted)
|
|
)
|
|
{
|
|
// Client hasn't finished encryption handshake yet, don't send them any data yet
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
UInt32 PacketLen;
|
|
if (
|
|
!m_ServerBuffer.ReadVarInt(PacketLen) ||
|
|
!m_ServerBuffer.CanReadBytes(PacketLen)
|
|
)
|
|
{
|
|
// Not a complete packet yet
|
|
m_ServerBuffer.ResetRead();
|
|
break;
|
|
}
|
|
if (PacketLen == 0)
|
|
{
|
|
m_ServerBuffer.ResetRead();
|
|
ContiguousByteBuffer All;
|
|
m_ServerBuffer.ReadAll(All);
|
|
DataLog(All.data(), All.size(), "====== Received a bad packet length? Inspect the contents below ======");
|
|
m_ServerBuffer.CommitRead(); // Try to recover by marking everything as read
|
|
break;
|
|
}
|
|
UInt32 PacketType, PacketReadSoFar;
|
|
PacketReadSoFar = static_cast<UInt32>(m_ServerBuffer.GetReadableSpace());
|
|
VERIFY(m_ServerBuffer.ReadVarInt(PacketType));
|
|
PacketReadSoFar -= m_ServerBuffer.GetReadableSpace();
|
|
Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%0x, %u bytes long", m_ServerBuffer.GetReadableSpace(), PacketType, PacketLen);
|
|
LogFlush();
|
|
switch (m_ServerProtocolState)
|
|
{
|
|
case -1:
|
|
{
|
|
Log("Receiving data from the server without an initial handshake message!");
|
|
HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar));
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
// Status query:
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_SERVER_READ(HandleServerStatusResponse()); break;
|
|
case 0x01: HANDLE_SERVER_READ(HandleServerStatusPing()); break;
|
|
default: HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
// Login:
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_SERVER_READ(HandleServerLoginDisconnect()); break;
|
|
case 0x01: HANDLE_SERVER_READ(HandleServerLoginEncryptionKeyRequest()); break;
|
|
case 0x02: HANDLE_SERVER_READ(HandleServerLoginSuccess()); break;
|
|
default: HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
// Game:
|
|
switch (PacketType)
|
|
{
|
|
case 0x00: HANDLE_SERVER_READ(HandleServerKeepAlive()); break;
|
|
case 0x01: HANDLE_SERVER_READ(HandleServerJoinGame()); break;
|
|
case 0x02: HANDLE_SERVER_READ(HandleServerChatMessage()); break;
|
|
case 0x03: HANDLE_SERVER_READ(HandleServerTimeUpdate()); break;
|
|
case 0x04: HANDLE_SERVER_READ(HandleServerEntityEquipment()); break;
|
|
case 0x05: HANDLE_SERVER_READ(HandleServerCompass()); break;
|
|
case 0x06: HANDLE_SERVER_READ(HandleServerUpdateHealth()); break;
|
|
case 0x07: HANDLE_SERVER_READ(HandleServerRespawn()); break;
|
|
case 0x08: HANDLE_SERVER_READ(HandleServerPlayerPositionLook()); break;
|
|
case 0x09: HANDLE_SERVER_READ(HandleServerSlotSelect()); break;
|
|
case 0x0a: HANDLE_SERVER_READ(HandleServerUseBed()); break;
|
|
case 0x0b: HANDLE_SERVER_READ(HandleServerPlayerAnimation()); break;
|
|
case 0x0c: HANDLE_SERVER_READ(HandleServerSpawnNamedEntity()); break;
|
|
case 0x0d: HANDLE_SERVER_READ(HandleServerCollectPickup()); break;
|
|
case 0x0e: HANDLE_SERVER_READ(HandleServerSpawnObjectVehicle()); break;
|
|
case 0x0f: HANDLE_SERVER_READ(HandleServerSpawnMob()); break;
|
|
case 0x10: HANDLE_SERVER_READ(HandleServerSpawnPainting()); break;
|
|
case 0x11: HANDLE_SERVER_READ(HandleServerSpawnExperienceOrbs()); break;
|
|
case 0x12: HANDLE_SERVER_READ(HandleServerEntityVelocity()); break;
|
|
case 0x13: HANDLE_SERVER_READ(HandleServerDestroyEntities()); break;
|
|
case 0x14: HANDLE_SERVER_READ(HandleServerEntity()); break;
|
|
case 0x15: HANDLE_SERVER_READ(HandleServerEntityRelativeMove()); break;
|
|
case 0x16: HANDLE_SERVER_READ(HandleServerEntityLook()); break;
|
|
case 0x17: HANDLE_SERVER_READ(HandleServerEntityRelativeMoveLook()); break;
|
|
case 0x18: HANDLE_SERVER_READ(HandleServerEntityTeleport()); break;
|
|
case 0x19: HANDLE_SERVER_READ(HandleServerEntityHeadLook()); break;
|
|
case 0x1a: HANDLE_SERVER_READ(HandleServerEntityStatus()); break;
|
|
case 0x1b: HANDLE_SERVER_READ(HandleServerAttachEntity()); break;
|
|
case 0x1c: HANDLE_SERVER_READ(HandleServerEntityMetadata()); break;
|
|
case 0x1f: HANDLE_SERVER_READ(HandleServerSetExperience()); break;
|
|
case 0x20: HANDLE_SERVER_READ(HandleServerEntityProperties()); break;
|
|
case 0x21: HANDLE_SERVER_READ(HandleServerMapChunk()); break;
|
|
case 0x22: HANDLE_SERVER_READ(HandleServerMultiBlockChange()); break;
|
|
case 0x23: HANDLE_SERVER_READ(HandleServerBlockChange()); break;
|
|
case 0x24: HANDLE_SERVER_READ(HandleServerBlockAction()); break;
|
|
case 0x26: HANDLE_SERVER_READ(HandleServerMapChunkBulk()); break;
|
|
case 0x27: HANDLE_SERVER_READ(HandleServerExplosion()); break;
|
|
case 0x28: HANDLE_SERVER_READ(HandleServerSoundEffect()); break;
|
|
case 0x29: HANDLE_SERVER_READ(HandleServerNamedSoundEffect()); break;
|
|
case 0x2b: HANDLE_SERVER_READ(HandleServerChangeGameState()); break;
|
|
case 0x2d: HANDLE_SERVER_READ(HandleServerWindowOpen()); break;
|
|
case 0x2e: HANDLE_SERVER_READ(HandleServerWindowClose()); break;
|
|
case 0x2f: HANDLE_SERVER_READ(HandleServerSetSlot()); break;
|
|
case 0x30: HANDLE_SERVER_READ(HandleServerWindowContents()); break;
|
|
case 0x33: HANDLE_SERVER_READ(HandleServerUpdateSign()); break;
|
|
case 0x35: HANDLE_SERVER_READ(HandleServerUpdateTileEntity()); break;
|
|
case 0x37: HANDLE_SERVER_READ(HandleServerStatistics()); break;
|
|
case 0x38: HANDLE_SERVER_READ(HandleServerPlayerListItem()); break;
|
|
case 0x39: HANDLE_SERVER_READ(HandleServerPlayerAbilities()); break;
|
|
case 0x3a: HANDLE_SERVER_READ(HandleServerTabCompletion()); break;
|
|
case 0x3f: HANDLE_SERVER_READ(HandleServerPluginMessage()); break;
|
|
case 0x40: HANDLE_SERVER_READ(HandleServerKick()); break;
|
|
default: HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar)); break;
|
|
} // switch (PacketType)
|
|
break;
|
|
} // case 3 - Game
|
|
|
|
// TODO: Move this elsewhere
|
|
default:
|
|
{
|
|
Log("Received a packet from the server while in an unknown state: %d", m_ServerProtocolState);
|
|
HANDLE_SERVER_READ(HandleServerUnknownPacket(PacketType, PacketLen, PacketReadSoFar));
|
|
break;
|
|
}
|
|
} // switch (m_ProtocolState)
|
|
|
|
m_ServerBuffer.CommitRead();
|
|
} // while (CanReadBytes(1))
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// packet handling, client-side, initial handshake:
|
|
|
|
bool cConnection::HandleClientHandshake(void)
|
|
{
|
|
// Read the packet from the client:
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarInt, UInt32, ProtocolVersion);
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, ServerHost);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, ServerPort);
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarInt, UInt32, NextState);
|
|
m_ClientBuffer.CommitRead();
|
|
|
|
Log("Received an initial handshake packet from the client:");
|
|
Log(" ProtocolVersion = %u", ProtocolVersion);
|
|
Log(" ServerHost = \"%s\"", ServerHost.c_str());
|
|
Log(" ServerPort = %u", ServerPort);
|
|
Log(" NextState = %u", NextState);
|
|
|
|
// Send the same packet to the server, but with our port:
|
|
cByteBuffer Packet(512);
|
|
Packet.WriteVarInt32(0); // Packet type - initial handshake
|
|
Packet.WriteVarInt32(ProtocolVersion);
|
|
Packet.WriteVarUTF8String(ServerHost);
|
|
Packet.WriteBEUInt16(m_Server.GetConnectPort());
|
|
Packet.WriteVarInt32(NextState);
|
|
ContiguousByteBuffer Pkt;
|
|
Packet.ReadAll(Pkt);
|
|
cByteBuffer ToServer(512);
|
|
ToServer.WriteVarInt32(static_cast<UInt32>(Pkt.size()));
|
|
ToServer.Write(Pkt.data(), Pkt.size());
|
|
SERVERSEND(ToServer);
|
|
|
|
m_ClientProtocolState = static_cast<int>(NextState);
|
|
m_ServerProtocolState = static_cast<int>(NextState);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// packet handling, client-side, login:
|
|
|
|
bool cConnection::HandleClientLoginEncryptionKeyResponse(void)
|
|
{
|
|
Log("Client: Unexpected packet: encryption key response");
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientLoginStart(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, UserName);
|
|
Log("Received a login start packet from the client:");
|
|
Log(" Username = \"%s\"", UserName.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// packet handling, client-side, game:
|
|
|
|
bool cConnection::HandleClientAnimation(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt8, Int8, Animation);
|
|
Log("Received a PACKET_ANIMATION from the client:");
|
|
Log(" EntityID: %u", EntityID);
|
|
Log(" Animation: %d", Animation);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientBlockDig(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, Status);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, BlockY);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, BlockFace);
|
|
Log("Received a PACKET_BLOCK_DIG from the client:");
|
|
Log(" Status = %u (0x%02x)", Status, Status);
|
|
Log(" Pos = <%d, %u, %d>", BlockX, BlockY, BlockZ);
|
|
Log(" BlockFace = %u (0x%02x)", BlockFace, BlockFace);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientBlockPlace(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, BlockY);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, Face);
|
|
AString Desc;
|
|
if (!ParseSlot(m_ClientBuffer, Desc))
|
|
{
|
|
return false;
|
|
}
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, CursorX);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, CursorY);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, CursorZ);
|
|
Log("Received a PACKET_BLOCK_PLACE from the client:");
|
|
Log(" Block = {%d, %u, %d}", BlockX, BlockY, BlockZ);
|
|
Log(" Face = %u (0x%02x)", Face, Face);
|
|
Log(" Item = %s", Desc.c_str());
|
|
Log(" Cursor = <%u, %u, %u>", CursorX, CursorY, CursorZ);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientChatMessage(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Message);
|
|
Log("Received a PACKET_CHAT_MESSAGE from the client:");
|
|
Log(" Message = \"%s\"", Message.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientClientStatuses(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, Statuses);
|
|
Log("Received a PACKET_CLIENT_STATUSES from the CLIENT:");
|
|
Log(" Statuses = %u (0x%02x)", Statuses, Statuses);
|
|
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientCreativeInventoryAction(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, SlotNum);
|
|
AString Item;
|
|
if (!ParseSlot(m_ClientBuffer, Item))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_CREATIVE_INVENTORY_ACTION from the client:");
|
|
Log(" SlotNum = %u", SlotNum);
|
|
Log(" Item = %s", Item.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientDisconnect(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Reason);
|
|
Log("Received a PACKET_DISCONNECT from the client:");
|
|
Log(" Reason = \"%s\"", Reason.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientEntityAction(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, PlayerID);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, ActionType);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, HorseJumpBoost);
|
|
Log("Received a PACKET_ENTITY_ACTION from the client:");
|
|
Log(" PlayerID = %d", PlayerID);
|
|
Log(" ActionType = %u", ActionType);
|
|
Log(" HorseJumpBoost = %d (0x%08x)", HorseJumpBoost, HorseJumpBoost);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientKeepAlive(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, ID);
|
|
Log("Received a PACKET_KEEPALIVE from the client");
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientLocaleAndView(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Locale);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt8, Int8, ViewDistance);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt8, Int8, ChatFlags);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt8, Int8, Unused);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt8, Int8, Difficulty);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt8, Int8, ShowCape);
|
|
Log("Received a PACKET_LOCALE_AND_VIEW from the client");
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPing(void)
|
|
{
|
|
m_HasClientPinged = true;
|
|
Log("Received a PACKET_PING from the client");
|
|
m_ClientBuffer.ResetRead();
|
|
SERVERSEND(m_ClientBuffer);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPlayerAbilities(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, Flags);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
|
|
Log("Receives a PACKET_PLAYER_ABILITIES from the client:");
|
|
Log(" Flags = %u (0x%02x)", Flags, Flags);
|
|
Log(" FlyingSpeed = %f", FlyingSpeed);
|
|
Log(" WalkingSpeed = %f", WalkingSpeed);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPlayerLook(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, OnGround);
|
|
Log("Received a PACKET_PLAYER_LOOK from the client");
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPlayerOnGround(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, OnGround);
|
|
Log("Received a PACKET_PLAYER_ON_GROUND from the client");
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPlayerPosition(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, IsOnGround);
|
|
Log("Received a PACKET_PLAYER_POSITION from the client");
|
|
|
|
// TODO: list packet contents
|
|
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPlayerPositionLook(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, IsOnGround);
|
|
Log("Received a PACKET_PLAYER_POSITION_LOOK from the client");
|
|
Log(" Pos = {%.03f, %.03f, %.03f}", PosX, PosY, PosZ);
|
|
Log(" Stance = %.03f", Stance);
|
|
Log(" Yaw, Pitch = <%.03f, %.03f>", Yaw, Pitch);
|
|
Log(" IsOnGround = %s", IsOnGround ? "true" : "false");
|
|
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientPluginMessage(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, Length);
|
|
ContiguousByteBuffer Data;
|
|
if (!m_ClientBuffer.ReadSome(Data, Length))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_PLUGIN_MESSAGE from the client");
|
|
Log(" ChannelName = \"%s\"", ChannelName.c_str());
|
|
DataLog(Data.data(), Length, " Data: %u bytes", Length);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientSlotSelect(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt16, Int16, SlotNum);
|
|
Log("Received a PACKET_SLOT_SELECT from the client");
|
|
Log(" SlotNum = %d", SlotNum);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientStatusPing(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt64, Int64, Time);
|
|
Log("Received the status ping packet from the client:");
|
|
Log(" Time = %lld", Time);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientStatusRequest(void)
|
|
{
|
|
Log("Received the status request packet from the client");
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientTabCompletion(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Query);
|
|
Log("Received a PACKET_TAB_COMPLETION query from the client");
|
|
Log(" Query = \"%s\"", Query.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientUpdateSign(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt16, Int16, BlockY);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line1);
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line2);
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line3);
|
|
HANDLE_CLIENT_PACKET_READ(ReadVarUTF8String, AString, Line4);
|
|
Log("Received a PACKET_UPDATE_SIGN from the client:");
|
|
Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ);
|
|
Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientUseEntity(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, MouseButton);
|
|
Log("Received a PACKET_USE_ENTITY from the client:");
|
|
Log(" EntityID = %d", EntityID);
|
|
Log(" MouseButton = %u", MouseButton);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientWindowClick(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, WindowID);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, SlotNum);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, Button);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt16, UInt16, TransactionID);
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, Mode);
|
|
AString Item;
|
|
if (!ParseSlot(m_ClientBuffer, Item))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_WINDOW_CLICK from the client");
|
|
Log(" WindowID = %u", WindowID);
|
|
Log(" SlotNum = %u", SlotNum);
|
|
Log(" Button = %u, Mode = %u", Button, Mode);
|
|
Log(" TransactionID = 0x%x", TransactionID);
|
|
Log(" ClickedItem = %s", Item.c_str());
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientWindowClose(void)
|
|
{
|
|
HANDLE_CLIENT_PACKET_READ(ReadBEUInt8, UInt8, WindowID);
|
|
Log("Received a PACKET_WINDOW_CLOSE from the client:");
|
|
Log(" WindowID = %u", WindowID);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleClientUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar)
|
|
{
|
|
ContiguousByteBuffer Data;
|
|
if (!m_ClientBuffer.ReadSome(Data, a_PacketLen - a_PacketReadSoFar))
|
|
{
|
|
return false;
|
|
}
|
|
DataLog(Data.data(), Data.size(), "****************** Unknown packet 0x%x from the client; relaying and ignoring", a_PacketType);
|
|
COPY_TO_SERVER();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// packet handling, server-side, login:
|
|
|
|
bool cConnection::HandleServerLoginDisconnect(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Reason);
|
|
Log("Received a login-disconnect packet from the server:");
|
|
Log(" Reason = \"%s\"", Reason.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerLoginEncryptionKeyRequest(void)
|
|
{
|
|
// Read the packet from the server:
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ServerID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PublicKeyLength);
|
|
ContiguousByteBuffer PublicKey;
|
|
if (!m_ServerBuffer.ReadSome(PublicKey, PublicKeyLength))
|
|
{
|
|
return false;
|
|
}
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NonceLength);
|
|
ContiguousByteBuffer Nonce;
|
|
if (!m_ServerBuffer.ReadSome(Nonce, NonceLength))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:");
|
|
Log(" ServerID = %s", ServerID.c_str());
|
|
DataLog(PublicKey.data(), PublicKey.size(), " Public key (%u bytes)", static_cast<unsigned>(PublicKey.size()));
|
|
|
|
// Reply to the server:
|
|
SendEncryptionKeyResponse({ reinterpret_cast<const char *>(PublicKey.data()), PublicKey.size() }, { reinterpret_cast<const char *>(Nonce.data()), Nonce.size() });
|
|
|
|
// Do not send to client - we want the client connection open
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerLoginSuccess(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, UUID);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Username);
|
|
Log("Received a login success packet from the server:");
|
|
Log(" UUID = \"%s\"", UUID.c_str());
|
|
Log(" Username = \"%s\"", Username.c_str());
|
|
|
|
Log("Server is now in protocol state Game.");
|
|
m_ServerProtocolState = 3;
|
|
|
|
if (m_IsServerEncrypted)
|
|
{
|
|
Log("Server communication is now encrypted");
|
|
m_ServerState = csEncryptedUnderstood;
|
|
DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size());
|
|
SERVERENCRYPTSEND(m_ServerEncryptionBuffer);
|
|
m_ServerEncryptionBuffer.clear();
|
|
}
|
|
COPY_TO_CLIENT();
|
|
Log("Client is now in protocol state Game.");
|
|
m_ClientProtocolState = 3;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// packet handling, server-side, game:
|
|
|
|
bool cConnection::HandleServerAttachEntity(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, VehicleID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsOnLeash);
|
|
Log("Received a PACKET_ATTACH_ENTITY from the server:");
|
|
Log(" EntityID = %u (0x%x)", EntityID, EntityID);
|
|
Log(" VehicleID = %u (0x%x)", VehicleID, VehicleID);
|
|
Log(" IsOnLeash = %s", IsOnLeash ? "true" : "false");
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerBlockAction(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, BlockY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Byte1);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Byte2);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, BlockID);
|
|
Log("Received a PACKET_BLOCK_ACTION from the server:");
|
|
Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ);
|
|
Log(" Bytes = (%u, %u) == (0x%x, 0x%x)", Byte1, Byte2, Byte1, Byte2);
|
|
Log(" BlockID = %u", BlockID);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerBlockChange(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, BlockY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, BlockType);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, BlockMeta);
|
|
Log("Received a PACKET_BLOCK_CHANGE from the server");
|
|
Log(" Pos = {%d, %u, %d}", BlockX, BlockY, BlockZ);
|
|
Log(" BlockType = %u (0x%x", BlockType, BlockType);
|
|
Log(" BlockMeta = %u", BlockMeta);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerChangeGameState(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Reason);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Data);
|
|
Log("Received a PACKET_CHANGE_GAME_STATE from the server:");
|
|
Log(" Reason = %u", Reason);
|
|
Log(" Data = %f", Data);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerChatMessage(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Message);
|
|
Log("Received a PACKET_CHAT_MESSAGE from the server:");
|
|
Log(" Message = \"%s\"", Message.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerCollectPickup(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CollectedID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CollectorID);
|
|
Log("Received a PACKET_COLLECT_PICKUP from the server:");
|
|
Log(" CollectedID = %u", CollectedID);
|
|
Log(" CollectorID = %u", CollectorID);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerCompass(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, SpawnX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, SpawnY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, SpawnZ);
|
|
Log("Received PACKET_COMPASS from the server:");
|
|
Log(" Spawn = {%d, %d, %d}", SpawnX, SpawnY, SpawnZ);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerDestroyEntities(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, NumEntities);
|
|
if (!m_ServerBuffer.SkipRead(static_cast<size_t>(NumEntities) * 4))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received PACKET_DESTROY_ENTITIES from the server:");
|
|
Log(" NumEntities = %u", NumEntities);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntity(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
Log("Received a PACKET_ENTITY from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityEquipment(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, SlotNum);
|
|
AString Item;
|
|
if (!ParseSlot(m_ServerBuffer, Item))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_ENTITY_EQUIPMENT from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" SlotNum = %u", SlotNum);
|
|
Log(" Item = %s", Item.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityHeadLook(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, HeadYaw);
|
|
Log("Received a PACKET_ENTITY_HEAD_LOOK from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" HeadYaw = %u", HeadYaw);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityLook(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
Log("Received a PACKET_ENTITY_LOOK from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" Yaw = %u", Yaw);
|
|
Log(" Pitch = %u", Pitch);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityMetadata(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
AString Metadata;
|
|
if (!ParseMetadata(m_ServerBuffer, Metadata))
|
|
{
|
|
return false;
|
|
}
|
|
AString HexDump;
|
|
CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32);
|
|
Log("Received a PACKET_ENTITY_METADATA from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
auto len = static_cast<unsigned>(Metadata.length());
|
|
Log(" Metadata, length = %u (0x%x):\n%s", len, len, HexDump.c_str());
|
|
LogMetadata(Metadata, 4);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityProperties(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, Count);
|
|
Log("Received a PACKET_ENTITY_PROPERTIES from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" Count = %u", Count);
|
|
|
|
for (UInt32 i = 0; i < Count; i++)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Key);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Value);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, ListLength);
|
|
Log(" \"%s\" = %f; %u modifiers", Key.c_str(), Value, ListLength);
|
|
for (UInt16 j = 0; j < ListLength; j++)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt64, UInt64, UUIDHi);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt64, UInt64, UUIDLo);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, DblVal);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, ByteVal);
|
|
Log(" [%d] = {0x%08llx%08llx, %f, %u}", j, UUIDHi, UUIDLo, DblVal, ByteVal);
|
|
}
|
|
} // for i
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityRelativeMove(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, dx);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, dy);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, dz);
|
|
Log("Received a PACKET_ENTITY_RELATIVE_MOVE from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" RelMove = %s", PrintableAbsIntTriplet(dx, dy, dz).c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityRelativeMoveLook(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, dx);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, dy);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, dz);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
Log("Received a PACKET_ENTITY_RELATIVE_MOVE_LOOK from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" RelMove = %s", PrintableAbsIntTriplet(dx, dy, dz).c_str());
|
|
Log(" Yaw = %u", Yaw);
|
|
Log(" Pitch = %u", Pitch);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityStatus(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Status);
|
|
Log("Received a PACKET_ENTITY_STATUS from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" Status = %u (0x%02x)", Status, Status);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityTeleport(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, AbsX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, AbsY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, AbsZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
Log("Received a PACKET_ENTITY_TELEPORT from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(AbsX, AbsY, AbsZ).c_str());
|
|
Log(" Yaw = %u", Yaw);
|
|
Log(" Pitch = %u", Pitch);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerEntityVelocity(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, VelocityX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, VelocityY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, VelocityZ);
|
|
Log("Received a PACKET_ENTITY_VELOCITY from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerExplosion(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, NumRecords);
|
|
std::vector<sCoords> Records;
|
|
Records.reserve(NumRecords);
|
|
int PosXI = static_cast<int>(PosX), PosYI = static_cast<int>(PosY), PosZI = static_cast<int>(PosZ);
|
|
for (UInt32 i = 0; i < NumRecords; i++)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, rx);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, ry);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt8, Int8, rz);
|
|
Records.push_back(sCoords(PosXI + rx, PosYI + ry, PosZI + rz));
|
|
}
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionZ);
|
|
Log("Received a PACKET_EXPLOSION from the server:");
|
|
Log(" Pos = {%.02f, %.02f, %.02f}", PosX, PosY, PosZ);
|
|
Log(" Force = %.02f", Force);
|
|
Log(" NumRecords = %u", NumRecords);
|
|
for (UInt32 i = 0; i < NumRecords; i++)
|
|
{
|
|
Log(" Records[%u] = {%d, %d, %d}", i, Records[i].x, Records[i].y, Records[i].z);
|
|
}
|
|
Log(" Player motion = <%.02f, %.02f, %.02f>", PlayerMotionX, PlayerMotionY, PlayerMotionZ);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerIncrementStatistic(void)
|
|
{
|
|
// 0xc8
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, StatisticID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, Amount);
|
|
Log("Received a PACKET_INCREMENT_STATISTIC from the server:");
|
|
Log(" StatisticID = %u (0x%x)", StatisticID, StatisticID);
|
|
Log(" Amount = %d", Amount);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerJoinGame(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, GameMode);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Dimension);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Difficulty);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, MaxPlayers);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, LevelType);
|
|
Log("Received a PACKET_LOGIN from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" GameMode = %u", GameMode);
|
|
Log(" Dimension = %u", Dimension);
|
|
Log(" Difficulty = %u", Difficulty);
|
|
Log(" MaxPlayers = %u", MaxPlayers);
|
|
Log(" LevelType = \"%s\"", LevelType.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerKeepAlive(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, PingID);
|
|
Log("Received a PACKET_KEEP_ALIVE from the server:");
|
|
Log(" ID = %u", PingID);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerKick(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Reason);
|
|
Log("Received PACKET_KICK from the SERVER:");
|
|
if (m_HasClientPinged)
|
|
{
|
|
Log(" This was a std reply to client's PING");
|
|
AStringVector Split;
|
|
|
|
// Split by NULL chars (StringSplit() won't work here):
|
|
size_t Last = 0;
|
|
size_t Len = Reason.size();
|
|
for (size_t i = 0; i < Len; i++)
|
|
{
|
|
if (Reason[i] == 0)
|
|
{
|
|
Split.push_back(Reason.substr(Last, i - Last));
|
|
Last = i + 1;
|
|
}
|
|
}
|
|
if (Last < Len)
|
|
{
|
|
Split.push_back(Reason.substr(Last));
|
|
}
|
|
|
|
if (Split.size() == 6)
|
|
{
|
|
Log(" Preamble: \"%s\"", Split[0].c_str());
|
|
Log(" Protocol version: \"%s\"", Split[1].c_str());
|
|
Log(" Server version: \"%s\"", Split[2].c_str());
|
|
Log(" MOTD: \"%s\"", Split[3].c_str());
|
|
Log(" Cur players: \"%s\"", Split[4].c_str());
|
|
Log(" Max players: \"%s\"", Split[5].c_str());
|
|
|
|
// Modify the MOTD to show that it's being ProtoProxied:
|
|
Reason.assign(Split[0]);
|
|
Reason.push_back(0);
|
|
Reason.append(Split[1]);
|
|
Reason.push_back(0);
|
|
Reason.append(Split[2]);
|
|
Reason.push_back(0);
|
|
Reason.append(Printf("ProtoProxy: %s", Split[3].c_str()));
|
|
Reason.push_back(0);
|
|
Reason.append(Split[4]);
|
|
Reason.push_back(0);
|
|
Reason.append(Split[5]);
|
|
auto ReasonBE16 = UTF8ToRawBEUTF16(Reason);
|
|
AString PacketStart("\xff");
|
|
PacketStart.push_back(static_cast<char>(ReasonBE16.size() / 256));
|
|
PacketStart.push_back(static_cast<char>(ReasonBE16.size() % 256));
|
|
CLIENTSEND({ reinterpret_cast<const std::byte *>(PacketStart.data()), PacketStart.size() });
|
|
CLIENTSEND({ reinterpret_cast<const std::byte *>(ReasonBE16.data()), ReasonBE16.size() * sizeof(char16_t) });
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
DataLog(Reason.data(), Reason.size(), " Unknown reply format, dumping hex:");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log(" Reason = \"%s\"", Reason.c_str());
|
|
}
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerMapChunk(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, IsContiguous);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, PrimaryBitmap);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, AdditionalBitmap);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize);
|
|
ContiguousByteBuffer CompressedData;
|
|
if (!m_ServerBuffer.ReadSome(CompressedData, CompressedSize))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_MAP_CHUNK from the server:");
|
|
Log(" ChunkPos = [%d, %d]", ChunkX, ChunkZ);
|
|
Log(" Compressed size = %u (0x%x)", CompressedSize, CompressedSize);
|
|
|
|
// TODO: Save the compressed data into a file for later analysis
|
|
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerMapChunkBulk(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, ChunkCount);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, CompressedSize);
|
|
HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent);
|
|
ContiguousByteBuffer CompressedData;
|
|
if (!m_ServerBuffer.ReadSome(CompressedData, CompressedSize))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Read individual chunk metas.
|
|
// Need to read them first and only then start logging (in case we don't have the full packet yet)
|
|
typedef std::vector<sChunkMeta> sChunkMetas;
|
|
sChunkMetas ChunkMetas;
|
|
ChunkMetas.reserve(ChunkCount);
|
|
for (short i = 0; i < ChunkCount; i++)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, PrimaryBitmap);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, AddBitmap);
|
|
ChunkMetas.push_back(sChunkMeta(ChunkX, ChunkZ, PrimaryBitmap, AddBitmap));
|
|
}
|
|
|
|
Log("Received a PACKET_MAP_CHUNK_BULK from the server:");
|
|
Log(" ChunkCount = %u", ChunkCount);
|
|
Log(" Compressed size = %u (0x%x)", CompressedSize, CompressedSize);
|
|
Log(" IsSkyLightSent = %s", IsSkyLightSent ? "true" : "false");
|
|
|
|
// Log individual chunk coords:
|
|
int idx = 0;
|
|
for (sChunkMetas::iterator itr = ChunkMetas.begin(), end = ChunkMetas.end(); itr != end; ++itr, ++idx)
|
|
{
|
|
Log(" [%d]: [%d, %d], primary bitmap 0x%02x, add bitmap 0x%02x",
|
|
idx, itr->m_ChunkX, itr->m_ChunkZ, itr->m_PrimaryBitmap, itr->m_AddBitmap
|
|
);
|
|
} // for itr - ChunkMetas[]
|
|
|
|
// TODO: Save the compressed data into a file for later analysis
|
|
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerMultiBlockChange(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, ChunkZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NumBlocks);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, DataSize);
|
|
ContiguousByteBuffer BlockChangeData;
|
|
if (!m_ServerBuffer.ReadSome(BlockChangeData, DataSize))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_MULTI_BLOCK_CHANGE packet from the server:");
|
|
Log(" Chunk = [%d, %d]", ChunkX, ChunkZ);
|
|
Log(" NumBlocks = %u", NumBlocks);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerNamedSoundEffect(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, SoundName);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Volume);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
Log("Received a PACKET_NAMED_SOUND_EFFECT from the server:");
|
|
Log(" SoundName = \"%s\"", SoundName.c_str());
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ, 8).c_str());
|
|
Log(" Volume = %f", Volume);
|
|
Log(" Pitch = %u", Pitch);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerPlayerAbilities(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Flags);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
|
|
Log("Received a PACKET_PLAYER_ABILITIES from the server:");
|
|
Log(" Flags = %u (0x%02x)", Flags, Flags);
|
|
Log(" FlyingSpeed = %f", FlyingSpeed);
|
|
Log(" WalkingSpeed = %f", WalkingSpeed);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerPlayerAnimation(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, PlayerID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, AnimationID);
|
|
Log("Received a PACKET_PLAYER_ANIMATION from the server:");
|
|
Log(" PlayerID: %u (0x%x)", PlayerID, PlayerID);
|
|
Log(" Animation: %u", AnimationID);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerPlayerListItem(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, PlayerName);
|
|
HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsOnline);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Ping);
|
|
Log("Received a PACKET_PLAYERLIST_ITEM from the server:");
|
|
Log(" PlayerName = \"%s\" (%s)", PlayerName.c_str(), IsOnline ? "online" : "offline");
|
|
Log(" Ping = %d", Ping);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerPlayerPositionLook(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Pitch);
|
|
HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsOnGround);
|
|
Log("Received a PACKET_PLAYER_POSITION_LOOK from the server");
|
|
|
|
// TODO: list packet contents
|
|
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerPluginMessage(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ChannelName);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Length);
|
|
ContiguousByteBuffer Data;
|
|
if (!m_ServerBuffer.ReadSome(Data, Length))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_PLUGIN_MESSAGE from the server");
|
|
Log(" ChannelName = \"%s\"", ChannelName.c_str());
|
|
DataLog(Data.data(), Length, " Data: %u bytes", Length);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerRespawn(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, Dimension);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Difficulty);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, GameMode);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, LevelType);
|
|
Log("Received a respawn packet from the server:");
|
|
Log(" Dimension = %d", Dimension);
|
|
Log(" Difficulty = %u", Difficulty);
|
|
Log(" GameMode = %u", GameMode);
|
|
Log(" LevelType = \"%s\"", LevelType.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSetExperience(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, ExperienceBar);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Level);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, TotalExperience);
|
|
Log("Received a PACKET_SET_EXPERIENCE from the server:");
|
|
Log(" ExperienceBar = %.05f", ExperienceBar);
|
|
Log(" Level = %u", Level);
|
|
Log(" TotalExperience = %u", TotalExperience);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSetSlot(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, WindowID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, SlotNum);
|
|
AString Item;
|
|
if (!ParseSlot(m_ServerBuffer, Item))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_SET_SLOT from the server:");
|
|
Log(" WindowID = %u", WindowID);
|
|
Log(" SlotNum = %u", SlotNum);
|
|
Log(" Item = %s", Item.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSlotSelect(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, SlotNum);
|
|
Log("Received a PACKET_SLOT_SELECT from the server:");
|
|
Log(" SlotNum = %u", SlotNum);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSoundEffect(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, EffectID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, Data);
|
|
HANDLE_SERVER_PACKET_READ(ReadBool, bool, NoVolumeDecrease);
|
|
Log("Received a PACKET_SOUND_EFFECT from the server:");
|
|
Log(" EffectID = %d", EffectID);
|
|
Log(" Pos = {%d, %d, %d}", PosX, PosY, PosZ);
|
|
Log(" Data = %d", Data);
|
|
Log(" NoVolumeDecrease = %s", NoVolumeDecrease ? "true" : "false");
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSpawnExperienceOrbs(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, Count);
|
|
Log("Received a SPAWN_EXPERIENCE_ORBS packet from the server:");
|
|
Log(" EntityID = %u (0x%x)", EntityID, EntityID);
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
|
|
Log(" Count = %u", Count);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSpawnMob(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, MobType);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, HeadYaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, VelocityX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, VelocityY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, VelocityZ);
|
|
AString Metadata;
|
|
if (!ParseMetadata(m_ServerBuffer, Metadata))
|
|
{
|
|
return false;
|
|
}
|
|
AString HexDump;
|
|
CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32);
|
|
Log("Received a PACKET_SPAWN_MOB from the server:");
|
|
Log(" EntityID = %u (0x%x)", EntityID, EntityID);
|
|
Log(" MobType = %u (0x%x)", MobType, MobType);
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
|
|
Log(" Angles = [%u, %u, %u]", Yaw, Pitch, HeadYaw);
|
|
Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str());
|
|
auto len = static_cast<unsigned>(Metadata.length());
|
|
Log(" Metadata, length = %u (0x%x):\n%s", len, len, HexDump.c_str());
|
|
LogMetadata(Metadata, 4);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct sSpawnData
|
|
{
|
|
AString m_Name;
|
|
AString m_Value;
|
|
AString m_Signature;
|
|
sSpawnData(const AString & a_Name, const AString & a_Value, const AString & a_Signature) :
|
|
m_Name(a_Name),
|
|
m_Value(a_Value),
|
|
m_Signature(a_Signature)
|
|
{
|
|
}
|
|
};
|
|
|
|
typedef std::vector<sSpawnData> sSpawnDatas;
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSpawnNamedEntity(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, EntityUUID);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, EntityName);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, DataCount);
|
|
sSpawnDatas Data;
|
|
for (UInt32 i = 0; i < DataCount; i++)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Name);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Value);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Signature);
|
|
Data.push_back(sSpawnData(Name, Value, Signature));
|
|
}
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, SelectedItem);
|
|
AString Metadata;
|
|
if (!ParseMetadata(m_ServerBuffer, Metadata))
|
|
{
|
|
return false;
|
|
}
|
|
AString HexDump;
|
|
CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32);
|
|
Log("Received a PACKET_SPAWN_NAMED_ENTITY from the server:");
|
|
Log(" EntityID = %u (0x%x)", EntityID, EntityID);
|
|
Log(" UUID = \"%s\"", EntityUUID.c_str());
|
|
Log(" Name = \"%s\"", EntityName.c_str());
|
|
Log(" NumData = %u", DataCount);
|
|
for (sSpawnDatas::const_iterator itr = Data.begin(), end = Data.end(); itr != end; ++itr)
|
|
{
|
|
Log(" Name = \"%s\", Value = \"%s\", Signature = \"%s\"",
|
|
itr->m_Name.c_str(), itr->m_Value.c_str(), itr->m_Signature.c_str()
|
|
);
|
|
} // for itr - Data[]
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
|
|
Log(" Rotation = <yaw %u, pitch %u>", Yaw, Pitch);
|
|
Log(" SelectedItem = %u", SelectedItem);
|
|
auto len = static_cast<unsigned>(Metadata.length());
|
|
Log(" Metadata, length = %u (0x%x):\n%s", len, len, HexDump.c_str());
|
|
LogMetadata(Metadata, 4);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSpawnObjectVehicle(void)
|
|
{
|
|
#ifdef _DEBUG
|
|
// DEBUG:
|
|
// This packet is still troublesome when DataIndicator != 0
|
|
ContiguousByteBuffer Buffer;
|
|
m_ServerBuffer.ResetRead();
|
|
m_ServerBuffer.ReadAll(Buffer);
|
|
m_ServerBuffer.ResetRead();
|
|
UInt32 PacketLen, PacketType;
|
|
m_ServerBuffer.ReadVarInt(PacketLen);
|
|
m_ServerBuffer.ReadVarInt(PacketType);
|
|
if (Buffer.size() > 128)
|
|
{
|
|
// Only log up to 128 bytes
|
|
Buffer.erase(128, AString::npos);
|
|
}
|
|
DataLog(Buffer.data(), Buffer.size(), "Buffer while parsing the PACKET_SPAWN_OBJECT_VEHICLE packet (%u bytes):", static_cast<unsigned>(Buffer.size()));
|
|
#endif // _DEBUG
|
|
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, ObjType);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, DataIndicator);
|
|
AString ExtraData;
|
|
Int16 VelocityX = 0;
|
|
Int16 VelocityY = 0;
|
|
Int16 VelocityZ = 0;
|
|
if (DataIndicator != 0)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, SpeedX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, SpeedY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, SpeedZ);
|
|
VelocityX = SpeedX; VelocityY = SpeedY; VelocityZ = SpeedZ; // Speed vars are local to this scope, but we need them available later
|
|
/*
|
|
// This doesn't seem to work - for a falling block I'm getting no extra data at all
|
|
int ExtraLen = 0;
|
|
switch (ObjType)
|
|
{
|
|
case OBJECT_FALLING_BLOCK: ExtraLen = 4; break; // int: BlockType | (BlockMeta << 12)
|
|
case OBJECT_ARROW:
|
|
case OBJECT_SNOWBALL:
|
|
case OBJECT_EGG:
|
|
case OBJECT_EYE_OF_ENDER:
|
|
case OBJECT_DRAGON_EGG:
|
|
case OBJECT_FISHING_FLOAT:
|
|
{
|
|
ExtraLen = 4; break; // int: EntityID of the thrower
|
|
}
|
|
// TODO: Splash potions
|
|
}
|
|
if ((ExtraLen > 0) && !m_ServerBuffer.ReadSome(ExtraData, ExtraLen))
|
|
{
|
|
return false;
|
|
}
|
|
*/
|
|
}
|
|
Log("Received a PACKET_SPAWN_OBJECT_VEHICLE from the server:");
|
|
Log(" EntityID = %u (0x%x)", EntityID, EntityID);
|
|
Log(" ObjType = %u (0x%x)", ObjType, ObjType);
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
|
|
Log(" Rotation = <yaw %u, pitch %u>", Yaw, Pitch);
|
|
Log(" DataIndicator = %u (0x%x)", DataIndicator, DataIndicator);
|
|
if (DataIndicator != 0)
|
|
{
|
|
Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str());
|
|
DataLog(ExtraData.data(), ExtraData.size(), " ExtraData size = %u:", static_cast<unsigned>(ExtraData.size()));
|
|
}
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSpawnPainting(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, ImageName);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, Direction);
|
|
Log("Received a PACKET_SPAWN_PAINTING from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" ImageName = \"%s\"", ImageName.c_str());
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
|
|
Log(" Direction = %d", Direction);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerSpawnPickup(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
AString ItemDesc;
|
|
if (!ParseSlot(m_ServerBuffer, ItemDesc))
|
|
{
|
|
return false;
|
|
}
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, PosZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Yaw);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Pitch);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Roll);
|
|
Log("Received a PACKET_SPAWN_PICKUP from the server:");
|
|
Log(" EntityID = %d", EntityID);
|
|
Log(" Item = %s", ItemDesc.c_str());
|
|
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
|
|
Log(" Angles = [%u, %u, %u]", Yaw, Pitch, Roll);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerStatistics(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, NumEntries);
|
|
for (UInt32 i = 0; i < NumEntries; i++)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, StatName);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, StatValue);
|
|
}
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerStatusPing(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, Time);
|
|
Log("Received server's ping response:");
|
|
Log(" Time = %lld", Time);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerStatusResponse(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Response);
|
|
Log("Received server's status response:");
|
|
Log(" Response: %s", Response.c_str());
|
|
|
|
// Modify the response to show that it's being proto-proxied:
|
|
const char DescSearch[] = "\"description\":{\"text\":\"";
|
|
size_t idx = Response.find(DescSearch);
|
|
if (idx != AString::npos)
|
|
{
|
|
Response.assign(Response.substr(0, idx + sizeof(DescSearch) - 1) + "ProtoProxy: " + Response.substr(idx + sizeof(DescSearch) - 1));
|
|
}
|
|
else
|
|
{
|
|
Log("Cannot find the description json element, ProtoProxy signature not inserted");
|
|
}
|
|
cByteBuffer Packet(Response.size() + 50);
|
|
Packet.WriteVarInt32(0); // Packet type - status response
|
|
Packet.WriteVarUTF8String(Response);
|
|
ContiguousByteBuffer Pkt;
|
|
Packet.ReadAll(Pkt);
|
|
cByteBuffer ToClient(Response.size() + 50);
|
|
ToClient.WriteVarInt32(static_cast<UInt32>(Pkt.size()));
|
|
ToClient.Write(Pkt.data(), Pkt.size());
|
|
CLIENTSEND(ToClient);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerTabCompletion(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, NumResults);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Results);
|
|
Log("Received a PACKET_TAB_COMPLETION from the server, results given:");
|
|
|
|
// Parse the zero-terminated list of results:
|
|
size_t len = Results.size();
|
|
size_t last = 0;
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
if (Results[i] == 0)
|
|
{
|
|
Log(" \"%s\"", Results.substr(last, i - last).c_str());
|
|
last = i + 1;
|
|
}
|
|
}
|
|
if (last < len)
|
|
{
|
|
Log(" \"%s\"", Results.substr(last, len - last).c_str());
|
|
}
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerTimeUpdate(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt64, UInt64, WorldAge);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt64, UInt64, TimeOfDay);
|
|
Log("Received a PACKET_TIME_UPDATE from the server");
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerUpdateHealth(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Health);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, Food);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Saturation);
|
|
Log("Received a PACKET_UPDATE_HEALTH from the server");
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerUpdateSign(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, BlockY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line1);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line2);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line3);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Line4);
|
|
Log("Received a PACKET_UPDATE_SIGN from the server:");
|
|
Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ);
|
|
Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str());
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerUpdateTileEntity(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt16, Int16, BlockY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BlockZ);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, Action);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, DataLength);
|
|
|
|
ContiguousByteBuffer Data;
|
|
if ((DataLength > 0) && !m_ServerBuffer.ReadSome(Data, DataLength))
|
|
{
|
|
return false;
|
|
}
|
|
Log("Received a PACKET_UPDATE_TILE_ENTITY from the server:");
|
|
Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ);
|
|
Log(" Action = %u", Action);
|
|
DataLog(Data.data(), Data.size(), " Data (%u bytes)", DataLength);
|
|
|
|
// Save metadata to a file:
|
|
AString fnam;
|
|
Printf(fnam, "%s_tile_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++);
|
|
FILE * f = fopen(fnam.c_str(), "wb");
|
|
if (f != nullptr)
|
|
{
|
|
fwrite(Data.data(), 1, Data.size(), f);
|
|
fclose(f);
|
|
Log("(saved to file \"%s\")", fnam.c_str());
|
|
}
|
|
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerUseBed(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, EntityID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BedX);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, BedY);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEInt32, Int32, BedZ);
|
|
Log("Received a use bed packet from the server:");
|
|
Log(" EntityID = %u", EntityID);
|
|
Log(" Bed = {%d, %u, %d}", BedX, BedY, BedZ);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerWindowClose(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, WindowID);
|
|
Log("Received a PACKET_WINDOW_CLOSE from the server:");
|
|
Log(" WindowID = %u", WindowID);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerWindowContents(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, WindowID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt16, UInt16, NumSlots);
|
|
Log("Received a PACKET_WINDOW_CONTENTS from the server:");
|
|
Log(" WindowID = %u", WindowID);
|
|
Log(" NumSlots = %u", NumSlots);
|
|
AStringVector Items;
|
|
for (UInt16 i = 0; i < NumSlots; i++)
|
|
{
|
|
AString Item;
|
|
if (!ParseSlot(m_ServerBuffer, Item))
|
|
{
|
|
return false;
|
|
}
|
|
Log(" %u: %s", i, Item.c_str());
|
|
}
|
|
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerWindowOpen(void)
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, WindowID);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, WindowType);
|
|
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Title);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, NumSlots);
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt8, UInt8, UseProvidedTitle);
|
|
UInt32 HorseEntityID = 0;
|
|
if (WindowType == 11) // Horse / Donkey / Mule
|
|
{
|
|
HANDLE_SERVER_PACKET_READ(ReadBEUInt32, UInt32, intHorseInt);
|
|
HorseEntityID = intHorseInt;
|
|
}
|
|
Log("Received a PACKET_WINDOW_OPEN from the server:");
|
|
Log(" WindowID = %u", WindowID);
|
|
Log(" WindowType = %u (0x%02x)", WindowType, WindowType);
|
|
Log(" Title = \"%s\", Use = %u", Title.c_str(), UseProvidedTitle);
|
|
Log(" NumSlots = %u", NumSlots);
|
|
if (WindowType == 11)
|
|
{
|
|
Log(" HorseEntityID = %u (0x%08x)", HorseEntityID, HorseEntityID);
|
|
}
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::HandleServerUnknownPacket(UInt32 a_PacketType, UInt32 a_PacketLen, UInt32 a_PacketReadSoFar)
|
|
{
|
|
ContiguousByteBuffer Data;
|
|
ASSERT(a_PacketLen >= a_PacketReadSoFar);
|
|
if (!m_ServerBuffer.ReadSome(Data, a_PacketLen - a_PacketReadSoFar))
|
|
{
|
|
return false;
|
|
}
|
|
DataLog(Data.data(), Data.size(), "****************** Unknown packet 0x%x from the server; relaying and ignoring", a_PacketType);
|
|
COPY_TO_CLIENT();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc)
|
|
{
|
|
short ItemType;
|
|
if (!a_Buffer.ReadBEInt16(ItemType))
|
|
{
|
|
return false;
|
|
}
|
|
if (ItemType <= 0)
|
|
{
|
|
a_ItemDesc = "<empty>";
|
|
return true;
|
|
}
|
|
if (!a_Buffer.CanReadBytes(5))
|
|
{
|
|
return false;
|
|
}
|
|
Int8 ItemCount;
|
|
Int16 ItemDamage;
|
|
UInt16 MetadataLength;
|
|
a_Buffer.ReadBEInt8(ItemCount); // We already know we can read these bytes - we checked before.
|
|
a_Buffer.ReadBEInt16(ItemDamage);
|
|
a_Buffer.ReadBEUInt16(MetadataLength);
|
|
Printf(a_ItemDesc, "%d:%d * %d", ItemType, ItemDamage, ItemCount);
|
|
if (MetadataLength <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
AString Metadata;
|
|
Metadata.resize(MetadataLength);
|
|
if (!a_Buffer.ReadBuf(const_cast<char *>(Metadata.data()), MetadataLength))
|
|
{
|
|
return false;
|
|
}
|
|
AString MetaHex;
|
|
CreateHexDump(MetaHex, Metadata.data(), Metadata.size(), 16);
|
|
AppendPrintf(a_ItemDesc, "; %u bytes of meta:\n%s", MetadataLength, MetaHex.c_str());
|
|
|
|
// Save metadata to a file:
|
|
AString fnam;
|
|
Printf(fnam, "%s_item_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++);
|
|
FILE * f = fopen(fnam.c_str(), "wb");
|
|
if (f != nullptr)
|
|
{
|
|
fwrite(Metadata.data(), 1, Metadata.size(), f);
|
|
fclose(f);
|
|
AppendPrintf(a_ItemDesc, "\n (saved to file \"%s\")", fnam.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata)
|
|
{
|
|
UInt8 x;
|
|
if (!a_Buffer.ReadBEUInt8(x))
|
|
{
|
|
return false;
|
|
}
|
|
a_Metadata.push_back(static_cast<char>(x));
|
|
while (x != 0x7f)
|
|
{
|
|
// int Index = static_cast<unsigned>(x) & 0x1f; // Lower 5 bits = index
|
|
int Type = static_cast<unsigned>(x) >> 5; // Upper 3 bits = type
|
|
|
|
// Get the length of the data for this item:
|
|
UInt32 Length = 0;
|
|
switch (Type)
|
|
{
|
|
case 0: Length = 1; break; // Byte
|
|
case 1: Length = 2; break; // short
|
|
case 2: Length = 4; break; // int
|
|
case 3: Length = 4; break; // float
|
|
case 4: // UTF-8 string with VarInt length
|
|
{
|
|
UInt32 Len;
|
|
int rs = static_cast<int>(a_Buffer.GetReadableSpace());
|
|
if (!a_Buffer.ReadVarInt(Len))
|
|
{
|
|
return false;
|
|
}
|
|
rs = rs - static_cast<int>(a_Buffer.GetReadableSpace());
|
|
cByteBuffer LenBuf(8);
|
|
LenBuf.WriteVarInt32(Len);
|
|
ContiguousByteBuffer VarLen;
|
|
LenBuf.ReadAll(VarLen);
|
|
a_Metadata += { reinterpret_cast<const char *>(VarLen.data()), VarLen.size() };
|
|
Length = Len;
|
|
break;
|
|
}
|
|
case 5: // Item, in "slot" format
|
|
{
|
|
size_t Before = a_Buffer.GetReadableSpace();
|
|
AString ItemDesc;
|
|
if (!ParseSlot(a_Buffer, ItemDesc))
|
|
{
|
|
return false;
|
|
}
|
|
size_t After = a_Buffer.GetReadableSpace();
|
|
a_Buffer.ResetRead();
|
|
a_Buffer.SkipRead(a_Buffer.GetReadableSpace() - Before);
|
|
Length = static_cast<UInt32>(Before - After);
|
|
break;
|
|
}
|
|
case 6: Length = 12; break; // 3 * int
|
|
case 7:
|
|
default:
|
|
{
|
|
ASSERT(!"Unknown metadata type");
|
|
break;
|
|
}
|
|
} // switch (Type)
|
|
|
|
// Read the data in this item:
|
|
ContiguousByteBuffer data;
|
|
if (!a_Buffer.ReadSome(data, Length))
|
|
{
|
|
return false;
|
|
}
|
|
a_Metadata += { reinterpret_cast<const char *>(data.data()), data.size() };
|
|
if (!a_Buffer.ReadBEUInt8(x))
|
|
{
|
|
return false;
|
|
}
|
|
a_Metadata.push_back(static_cast<char>(x));
|
|
} // while (x != 0x7f)
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount)
|
|
{
|
|
AString Indent(a_IndentCount, ' ');
|
|
size_t pos = 0;
|
|
while (a_Metadata[pos] != 0x7f)
|
|
{
|
|
unsigned Index = static_cast<unsigned>(static_cast<unsigned char>(a_Metadata[pos])) & 0x1f; // Lower 5 bits = index
|
|
unsigned Type = static_cast<unsigned>(static_cast<unsigned char>(a_Metadata[pos])) >> 5; // Upper 3 bits = type
|
|
// int Length = 0;
|
|
switch (Type)
|
|
{
|
|
case 0:
|
|
{
|
|
Log("%sbyte[%u] = %u", Indent.c_str(), Index, static_cast<unsigned char>(a_Metadata[pos + 1]));
|
|
pos += 1;
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
Log("%sshort[%u] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]);
|
|
pos += 2;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
Log("%sint[%u] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]);
|
|
pos += 4;
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
Log("%sfloat[%u] = 0x%x", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]);
|
|
pos += 4;
|
|
break;
|
|
}
|
|
case 4: // UTF-8 string with VarInt length
|
|
{
|
|
cByteBuffer bb(10);
|
|
size_t RestLen = a_Metadata.size() - pos - 1;
|
|
if (RestLen > 8)
|
|
{
|
|
RestLen = 8;
|
|
}
|
|
bb.Write(a_Metadata.data() + pos + 1, RestLen);
|
|
UInt32 Length;
|
|
size_t rs = bb.GetReadableSpace();
|
|
if (!bb.ReadVarInt(Length))
|
|
{
|
|
Log("Invalid metadata value, was supposed to be a varint-prefixed string, but cannot read the varint");
|
|
break;
|
|
}
|
|
rs = rs - bb.GetReadableSpace();
|
|
Log("%sstring[%u] = \"%*s\"", Indent.c_str(), Index, Length, a_Metadata.c_str() + pos + rs + 1);
|
|
pos += Length + rs + 2;
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
size_t BytesLeft = a_Metadata.size() - pos - 1;
|
|
cByteBuffer bb(BytesLeft);
|
|
bb.Write(a_Metadata.data() + pos + 1, BytesLeft);
|
|
AString ItemDesc;
|
|
if (!ParseSlot(bb, ItemDesc))
|
|
{
|
|
ASSERT(!"Cannot parse item description from metadata");
|
|
return;
|
|
}
|
|
// size_t After = bb.GetReadableSpace();
|
|
size_t BytesConsumed = BytesLeft - bb.GetReadableSpace();
|
|
|
|
Log("%sslot[%u] = %s (%u bytes)", Indent.c_str(), Index, ItemDesc.c_str(), static_cast<unsigned>(BytesConsumed));
|
|
pos += BytesConsumed;
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
Log("%spos[%u] = <%d, %d, %d>", Indent.c_str(), Index,
|
|
(a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4],
|
|
(a_Metadata[pos + 5] << 24) | (a_Metadata[pos + 6] << 16) | (a_Metadata[pos + 7] << 8) | a_Metadata[pos + 8],
|
|
(a_Metadata[pos + 9] << 24) | (a_Metadata[pos + 10] << 16) | (a_Metadata[pos + 11] << 8) | a_Metadata[pos + 12]
|
|
);
|
|
pos += 12;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(!"Unknown metadata type");
|
|
break;
|
|
}
|
|
} // switch (Type)
|
|
pos += 1;
|
|
} // while (x != 0x7f)
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce)
|
|
{
|
|
// Generate the shared secret and encrypt using the server's public key
|
|
Byte SharedSecret[16];
|
|
Byte EncryptedSecret[128];
|
|
memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret
|
|
cCryptoKey PubKey(a_ServerPublicKey);
|
|
int res = PubKey.Encrypt(SharedSecret, sizeof(SharedSecret), EncryptedSecret, sizeof(EncryptedSecret));
|
|
if (res < 0)
|
|
{
|
|
Log("Shared secret encryption failed: %d (0x%x)", res, res);
|
|
return;
|
|
}
|
|
|
|
m_ServerEncryptor.Init(SharedSecret, SharedSecret);
|
|
m_ServerDecryptor.Init(SharedSecret, SharedSecret);
|
|
|
|
// Encrypt the nonce:
|
|
Byte EncryptedNonce[128];
|
|
res = PubKey.Encrypt(reinterpret_cast<const Byte *>(a_Nonce.data()), a_Nonce.size(), EncryptedNonce, sizeof(EncryptedNonce));
|
|
if (res < 0)
|
|
{
|
|
Log("Nonce encryption failed: %d (0x%x)", res, res);
|
|
return;
|
|
}
|
|
|
|
// Send the packet to the server:
|
|
Log("Sending PACKET_ENCRYPTION_KEY_RESPONSE to the SERVER");
|
|
cByteBuffer ToServer(1024);
|
|
ToServer.WriteBEUInt8(0x01); // To server: Encryption key response
|
|
ToServer.WriteBEUInt16(static_cast<UInt16>(sizeof(EncryptedSecret)));
|
|
ToServer.WriteBuf(EncryptedSecret, sizeof(EncryptedSecret));
|
|
ToServer.WriteBEUInt16(static_cast<UInt16>(sizeof(EncryptedNonce)));
|
|
ToServer.WriteBuf(EncryptedNonce, sizeof(EncryptedNonce));
|
|
DataLog(EncryptedSecret, sizeof(EncryptedSecret), "Encrypted secret (%u bytes)", static_cast<unsigned>(sizeof(EncryptedSecret)));
|
|
DataLog(EncryptedNonce, sizeof(EncryptedNonce), "Encrypted nonce (%u bytes)", static_cast<unsigned>(sizeof(EncryptedNonce)));
|
|
cByteBuffer Len(5);
|
|
Len.WriteVarInt32(static_cast<UInt32>(ToServer.GetReadableSpace()));
|
|
SERVERSEND(Len);
|
|
SERVERSEND(ToServer);
|
|
m_ServerState = csEncryptedUnderstood;
|
|
m_IsServerEncrypted = true;
|
|
}
|