1
0

1.9 / 1.9.2 / 1.9.3 / 1.9.4 protocol support (#3135)

* Semistable update to 15w31a

I'm going through snapshots in a sequential order since it should make things easier, and since protocol version history is written.

* Update to 15w34b protocol

Also, fix an issue with the Entity Equipment packet from the past version.  Clients are able to connect and do stuff!

* Partially update to 15w35e

Chunk data doesn't work, but the client joins.  I'm waiting to do chunk data because chunk data has an incomplete format until 15w36d.

* Add '/blk' debug command

This command lets one see what block they are looking at, and makes figuring out what's supposed to be where in a highly broken chunk possible.

* Fix CRLF normalization in CheckBasicStyle.lua

Normally, this doesn't cause an issue, but when running from cygwin, it detects the CR as whitespace and creates thousands of violations for every single line.  Lua, when run on windows, will normalize automatically, but when run via cygwin, it won't.

The bug was simply that gsub was returning a replaced version, but not changing the parameter, so the replaced version was ignored.

* Update to 15w40b

This includes chunk serialization.  Fully functional chunk serialization for 1.9.

I'm not completely happy with the chunk serialization as-is (correct use of palettes would be great), but cuberite also doesn't skip sending empty chunks so this performance optimization should probably come later.  The creation of a full buffer is suboptimal, but it's the easiest way to implement this code.

* Write long-by-long rather than creating a buffer

This is a bit faster and should be equivalent.  However, the code still doesn't look too good.

* Update to 15w41a protocol

This includes the new set passengers packet, which works off of the ridden entity, not the rider.  That means, among other things, that information about the previously ridden vehicle is needed when detaching.  So a new method with that info was added.

* Update to 15w45a

* 15w51b protocol

* Update to 1.9.0 protocol

Closes #3067.  There are still a few things that need to be worked out (picking up items, effects, particles, and most importantly inventory), but in general this should work.  I'll make a few more changes tomorrow to get the rest of the protocol set up, along with 1.9.1/1.9.2 (which did make a few changes).  Chunks, however, _are_ working, along with most other parts of the game (placing/breaking blocks).

* Fix item pickup packet not working

That was a silly mistake, but at least it was an easy one.

* 1.9.2 protocol support

* Fix version info found in server list ping

Thus, the client reports that it can connect rather than saying that the server is out of date.  This required creating separate classes for 1.9.1 and 1.9.2, unfortunately.

* Fix build errors generated by clang

These didn't happen in MSVC.

* Add protocol19x.cpp and protocol19x.h to CMakeLists

* Ignore warnings in protocol19x that are ignored in protocol18x

* Document BLOCK_FACE and DIG_STATUS constants

* Fix BLOCK_FACE links and add separate section for DIG_STATUS

* Fix bat animation and object spawning

The causes of both of these are explained in #3135, but the gist is that both were typos.

* Implement Use Item packet

This means that buckets, bows, fishing rods, and several other similar items now work when not looking at a block.

* Handle DIG_STATUS_SWAP_ITEM_IN_HAND

* Add support for spawn eggs and potions

The items are transformed from the 1.9 version to the 1.8 version when reading and transformed back when sending.

* Remove spammy potion debug logging

* Fix wolf collar color metadata

The wrong type was being used, causing several clientside issues (including the screen going black).

* Fix 1.9 chunk sending in the nether

The nether and the end don't send skylight.

* Fix clang build errors

* Fix water bottles becoming mundane potions

This happened because the can become splash potion bit got set incorrectly.  Water bottles and mundane potions are only differentiated by the fact that water bottles have a metadata of 0, so setting that bit made it a mundane potion.

Also add missing break statements to the read item NBT switch, which would otherwise break items with custom names and also cause incorrect "Unimplemented NBT data when parsing!" logging.

* Copy Protocol18x as Protocol19x

Aditionally, method and class names have been swapped to clean up other diffs.  This commit is only added to make the following diffs more readable; it doesn't make any other changes (beyond class names).

* Make thrown potions use the correct appearence

This was caused by potions now using metadata.

* Add missing api doc for cSplashPotionEntity::GetItem

* Fix compile error in SplashPotionEntity.cpp

* Fix fix of cSplashPotionEntity API doc

* Temporarilly disable fall damage particles

These were causing issues in 1.9 due to the changed effect ID.

* Properly send a kick packet when connecting with an invalid version

This means that the client no longer waits on the server screen with no indication whatsoever.  However, right now the server list ping isn't implemented for unknown versions, so it'll only load "Old" on the ping.

I also added a GetVarIntSize method to cByteBuffer.  This helps clean up part of the code here (and I think it could clean up other parts), but it may make sense for it to be moved elsewhere (or declared in a different way).

* Handle server list pings from unrecognized versions

This isn't the cleanest way of writing it (it feels odd to use ProtocolRecognizer to send packets, and the addition of m_InPingForUnrecognizedVersion feels like the wrong technique), but it works and I can't think of a better way (apart from creating a full separate protocol class to handle only the ping... which would be worse).

* Use cPacketizer for the disconnect packet

This also should fix clang build errors.

* Add 1.9.3 / 1.9.4 support

* Fix incorrect indentation in APIDesc
This commit is contained in:
Pokechu22 2016-05-14 12:12:42 -07:00 committed by Alexander Harkness
parent b3d4e0fca6
commit a4f327118b
35 changed files with 5375 additions and 102 deletions

View File

@ -30,6 +30,7 @@ mtilden
nesco
NiLSPACE (formerly STR_Warrior)
p-mcgowan
pokechu22
rs2k
SamJBarney
Schwertspize

View File

@ -2965,6 +2965,28 @@ end
},
Constants =
{
BLOCK_FACE_XM = { Notes = "Interacting with the X- face of the block" },
BLOCK_FACE_XP = { Notes = "Interacting with the X+ face of the block" },
BLOCK_FACE_YM = { Notes = "Interacting with the Y- face of the block" },
BLOCK_FACE_YP = { Notes = "Interacting with the Y+ face of the block" },
BLOCK_FACE_ZM = { Notes = "Interacting with the Z- face of the block" },
BLOCK_FACE_ZP = { Notes = "Interacting with the Z+ face of the block" },
BLOCK_FACE_NONE = { Notes = "Interacting with no block face - swinging the item in the air" },
BLOCK_FACE_EAST = { Notes = "(<b>DEPRECATED!</b>) Please use BLOCK_FACE_XM instead. Interacting with the eastern face of the block." },
BLOCK_FACE_WEST = { Notes = "(<b>DEPRECATED!</b>) Please use BLOCK_FACE_XP instead. Interacting with the western face of the block." },
BLOCK_FACE_BOTTOM = { Notes = "(<b>DEPRECATED!</b>) Please use BLOCK_FACE_YM instead. Interacting with the bottom face of the block." },
BLOCK_FACE_TOP = { Notes = "(<b>DEPRECATED!</b>) Please use BLOCK_FACE_YP instead. Interacting with the top face of the block." },
BLOCK_FACE_NORTH = { Notes = "(<b>DEPRECATED!</b>) Please use BLOCK_FACE_ZM instead. Interacting with the northern face of the block." },
BLOCK_FACE_SOUTH = { Notes = "(<b>DEPRECATED!</b>) Please use BLOCK_FACE_ZP instead. Interacting with the southern face of the block." },
BLOCK_FACE_MAX = { Notes = "Used for range checking - highest legal value for an {{Globals#BlockFaces|eBlockFace}}" },
BLOCK_FACE_MIN = { Notes = "Used for range checking - lowest legal value for an {{Globals#BlockFaces|eBlockFace}}" },
DIG_STATUS_STARTED = { Notes = "The player has started digging" },
DIG_STATUS_CANCELLED = { Notes = "The player has let go of the mine block key before finishing mining the block" },
DIG_STATUS_FINISHED = { Notes = "The player thinks that it has finished mining a block" },
DIG_STATUS_DROP_HELD = { Notes = "The player has dropped a single item using the Drop Item key (default: Q)" },
DIG_STATUS_DROP_STACK = { Notes = "The player has dropped a full stack of items using the Drop Item key (default: Q) while holding down a specific modifier key (in windows, control)" },
DIG_STATUS_SHOOT_EAT = { Notes = "The player has finished shooting a bow or finished eating" },
DIG_STATUS_SWAP_ITEM_IN_HAND = { Notes = "The player has swapped their held item with the item in their offhand slot (1.9)" },
esBed = { Notes = "A bed explosion. The SourceData param is the {{Vector3i|position}} of the bed." },
esEnderCrystal = { Notes = "An ender crystal entity explosion. The SourceData param is the {{cEntity|ender crystal entity}} object." },
esGhastFireball = { Notes = "A ghast fireball explosion. The SourceData param is the {{cGhastFireballEntity|ghast fireball entity}} object." },
@ -3048,6 +3070,14 @@ end
{{TakeDamageInfo}} structure, as well as in {{cEntity}}'s damage-related API functions.
]],
},
DigStatuses =
{
Include = "^DIG_STATUS_.*",
TextBefore = [[
These constants are used to describe digging statuses, but in reality cover several more cases.
They are used with {{OnPlayerLeftClick|HOOK_PLAYER_LEFT_CLICK}}.
]],
},
GameMode =
{
Include = { "^gm.*", "^eGameMode_.*" },

View File

@ -131,6 +131,7 @@ return
GetEntityEffect = { Params = "", Return = "{{cEntityEffect}}", Notes = "Returns the entity effect in this potion" },
GetEntityEffectType = { Params = "", Return = "{{cEntityEffect|Entity effect type}}", Notes = "Returns the effect type of this potion" },
GetPotionColor = { Params = "", Return = "number", Notes = "Returns the color index of the particles emitted by this potion" },
GetItem = { Params = "", Return = "{{cItem}}", Notes = "Gets the potion item that was thrown." },
SetEntityEffect = { Params = "{{cEntityEffect}}", Return = "", Notes = "Sets the entity effect for this potion" },
SetEntityEffectType = { Params = "{{cEntityEffect|Entity effect type}}", Return = "", Notes = "Sets the effect type of this potion" },
SetPotionColor = { Params = "number", Return = "", Notes = "Sets the color index of the particles for this potion" },

View File

@ -2156,3 +2156,31 @@ end
function HandleBlkCmd(a_Split, a_Player)
-- Gets info about the block the player is looking at.
local World = a_Player:GetWorld();
local Callbacks = {
OnNextBlock = function(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta)
if (a_BlockType ~= E_BLOCK_AIR) then
a_Player:SendMessage("Block at " .. a_BlockX .. ", " .. a_BlockY .. ", " .. a_BlockZ .. " is " .. a_BlockType .. ":" .. a_BlockMeta)
return true;
end
end
};
local EyePos = a_Player:GetEyePosition();
local LookVector = a_Player:GetLookVector();
LookVector:Normalize();
local End = EyePos + LookVector * 50;
cLineBlockTracer.Trace(World, Callbacks, EyePos.x, EyePos.y, EyePos.z, End.x, End.y, End.z);
return true;
end

View File

@ -202,6 +202,12 @@ g_PluginInfo =
Handler = HandleRemoveXp,
HelpString = "Remove all xp"
},
["/blk"] =
{
Permission = "debuggers",
Handler = HandleBlkCmd,
HelpString = "Gets info about the block you are looking at"
},
}, -- Commands
ConsoleCommands =

View File

@ -1034,3 +1034,21 @@ void cByteBuffer::CheckValid(void) const
size_t cByteBuffer::GetVarIntSize(UInt32 a_Value)
{
size_t Count = 0;
do
{
// If the value cannot be expressed in 7 bits, it needs to take up another byte
Count++;
a_Value >>= 7;
} while (a_Value != 0);
return Count;
}

View File

@ -129,6 +129,9 @@ public:
/** Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs */
void CheckValid(void) const;
/** Gets the number of bytes that are needed to represent the given VarInt */
static size_t GetVarIntSize(UInt32 a_Value);
protected:
char * m_Buffer;
size_t m_BufferSize; // Total size of the ringbuffer

View File

@ -237,7 +237,7 @@ local function ProcessFile(a_FileName)
local lineCounter = 1
local lastIndentLevel = 0
local isLastLineControl = false
all:gsub("\r\n", "\n") -- normalize CRLF into LF-only
all = all:gsub("\r\n", "\n") -- normalize CRLF into LF-only
string.gsub(all .. "\n", "[^\n]*\n", -- Iterate over each line, while preserving empty lines
function(a_Line)
-- Check against each violation pattern:

View File

@ -2785,7 +2785,7 @@ cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) con
void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
for (auto ClientHandle : m_LoadedByClient)
{
@ -2883,6 +2883,18 @@ void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandl
void cChunk::BroadcastDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
for (auto ClientHandle : m_LoadedByClient)
{
ClientHandle->SendDetachEntity(a_Entity, a_PreviousVehicle);
} // for itr - LoadedByClient[]
}
void cChunk::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude)
{
for (auto itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)

View File

@ -340,12 +340,13 @@ public:
// Broadcast various packets to all clients of this chunk:
// (Please keep these alpha-sorted)
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle);
void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle);
void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);

View File

@ -338,7 +338,7 @@ cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ)
void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
@ -442,6 +442,22 @@ void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHa
void cChunkMap::BroadcastDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == nullptr)
{
return;
}
// It's perfectly legal to broadcast packets even to invalid chunks!
Chunk->BroadcastDetachEntity(a_Entity, a_PreviousVehicle);
}
void cChunkMap::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);

View File

@ -71,13 +71,14 @@ public:
// Broadcast respective packets to all clients of the chunk where the event is taking place
// (Please keep these alpha-sorted)
void BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle);
void BroadcastAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle);
void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude);
void BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle);
void BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);

View File

@ -257,7 +257,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cCli
{
return;
}
cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap);
cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap, m_World.GetDimension());
for (const auto client : a_Clients)
{

View File

@ -1119,6 +1119,12 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
return;
}
case DIG_STATUS_SWAP_ITEM_IN_HAND:
{
// TODO: Not yet implemented
return;
}
default:
{
ASSERT(!"Unhandled DIG_STATUS");
@ -2095,7 +2101,7 @@ void cClientHandle::ServerTick(float a_Dt)
void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
}
@ -2319,6 +2325,15 @@ void cClientHandle::SendDestroyEntity(const cEntity & a_Entity)
void cClientHandle::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
m_Protocol->SendDetachEntity(a_Entity, a_PreviousVehicle);
}
void cClientHandle::SendDisconnect(const AString & a_Reason)
{
// Destruction (Destroy()) is called when the client disconnects, not when a disconnect packet (or anything else) is sent

View File

@ -144,7 +144,7 @@ public: // tolua_export
// The following functions send the various packets:
// (Please keep these alpha-sorted)
void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle);
void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType);
void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
@ -158,6 +158,7 @@ public: // tolua_export
void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player);
void SendDestroyEntity (const cEntity & a_Entity);
void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle);
void SendDisconnect (const AString & a_Reason);
void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display);
void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ);

View File

@ -60,12 +60,13 @@ enum eBlockFace
/** PlayerDigging status constants */
enum
{
DIG_STATUS_STARTED = 0,
DIG_STATUS_CANCELLED = 1,
DIG_STATUS_FINISHED = 2,
DIG_STATUS_DROP_STACK= 3,
DIG_STATUS_DROP_HELD = 4,
DIG_STATUS_SHOOT_EAT = 5,
DIG_STATUS_STARTED = 0,
DIG_STATUS_CANCELLED = 1,
DIG_STATUS_FINISHED = 2,
DIG_STATUS_DROP_STACK = 3,
DIG_STATUS_DROP_HELD = 4,
DIG_STATUS_SHOOT_EAT = 5,
DIG_STATUS_SWAP_ITEM_IN_HAND = 6,
} ;

View File

@ -1927,10 +1927,13 @@ void cEntity::AttachTo(cEntity * a_AttachTo)
Detach();
}
// Attach to the new entity:
// Update state information
m_AttachedTo = a_AttachTo;
a_AttachTo->m_Attachee = this;
m_World->BroadcastAttachEntity(*this, a_AttachTo);
if (a_AttachTo != nullptr)
{
m_World->BroadcastAttachEntity(*this, *a_AttachTo);
}
}
@ -1941,12 +1944,13 @@ void cEntity::Detach(void)
{
if (m_AttachedTo == nullptr)
{
// Attached to no entity, our work is done
// Already not attached to any entity, our work is done
return;
}
m_World->BroadcastDetachEntity(*this, *m_AttachedTo);
m_AttachedTo->m_Attachee = nullptr;
m_AttachedTo = nullptr;
m_World->BroadcastAttachEntity(*this, nullptr);
}

View File

@ -418,8 +418,9 @@ void cPawn::HandleFalling(void)
TakeDamage(dtFalling, nullptr, Damage, Damage, 0);
// Fall particles
int ParticleSize = static_cast<int>((std::min(15, Damage) - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f);
GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_FALL_PARTICLES, POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT, ParticleSize);
// TODO: Re-enable this when effects in 1.9 aren't broken (right now this uses the wrong effect ID in 1.9 and the right one in 1.8 and 1.7)
// int ParticleSize = static_cast<int>((std::min(15, Damage) - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f);
// GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_FALL_PARTICLES, POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT, ParticleSize);
}
m_bTouchGround = true;

View File

@ -80,6 +80,7 @@ cSplashPotionEntity::cSplashPotionEntity(
const cItem & a_Item
) :
super(pkSplashPotion, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
m_Item(a_Item),
m_DestroyTimer(-1)
{
SetSpeed(a_Speed);

View File

@ -41,6 +41,7 @@ public:
cEntityEffect::eType GetEntityEffectType(void) const { return m_EntityEffectType; }
cEntityEffect GetEntityEffect(void) const { return m_EntityEffect; }
int GetPotionColor(void) const { return m_PotionColor; }
const cItem & GetItem(void) const { return m_Item; }
void SetEntityEffectType(cEntityEffect::eType a_EntityEffectType) { m_EntityEffectType = a_EntityEffectType; }
void SetEntityEffect(cEntityEffect a_EntityEffect) { m_EntityEffect = a_EntityEffect; }
@ -53,6 +54,7 @@ protected:
cEntityEffect::eType m_EntityEffectType;
cEntityEffect m_EntityEffect;
int m_PotionColor;
cItem m_Item;
// cProjectileEntity overrides:

View File

@ -25,6 +25,7 @@ public:
bool IsBlowing(void) const {return m_bIsBlowing; }
bool IsCharged(void) const {return m_bIsCharged; }
bool IsBurnedWithFlintAndSteel(void) const {return m_BurnedWithFlintAndSteel; }
private:

View File

@ -11,6 +11,7 @@ SET (SRCS
Packetizer.cpp
Protocol17x.cpp
Protocol18x.cpp
Protocol19x.cpp
ProtocolRecognizer.cpp
)
@ -22,10 +23,12 @@ SET (HDRS
Protocol.h
Protocol17x.h
Protocol18x.h
Protocol19x.h
ProtocolRecognizer.h
)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
set_source_files_properties(Protocol19x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=old-style-cast -Wno-error=sign-conversion -Wno-error=conversion -Wno-error=switch-enum -Wno-error=switch")
set_source_files_properties(Protocol18x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=old-style-cast -Wno-error=sign-conversion -Wno-error=conversion -Wno-error=switch-enum -Wno-error=switch")
set_source_files_properties(Protocol17x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum ")
endif()

View File

@ -10,6 +10,7 @@
#include "zlib/zlib.h"
#include "ByteBuffer.h"
#include "Protocol18x.h"
#include "Protocol19x.h"
@ -19,13 +20,15 @@ cChunkDataSerializer::cChunkDataSerializer(
const cChunkDef::BlockNibbles & a_BlockMetas,
const cChunkDef::BlockNibbles & a_BlockLight,
const cChunkDef::BlockNibbles & a_BlockSkyLight,
const unsigned char * a_BiomeData
const unsigned char * a_BiomeData,
const eDimension a_Dimension
) :
m_BlockTypes(a_BlockTypes),
m_BlockMetas(a_BlockMetas),
m_BlockLight(a_BlockLight),
m_BlockSkyLight(a_BlockSkyLight),
m_BiomeData(a_BiomeData)
m_BiomeData(a_BiomeData),
m_Dimension(a_Dimension)
{
}
@ -45,6 +48,8 @@ const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int
{
case RELEASE_1_3_2: Serialize39(data); break;
case RELEASE_1_8_0: Serialize47(data, a_ChunkX, a_ChunkZ); break;
case RELEASE_1_9_0: Serialize107(data, a_ChunkX, a_ChunkZ); break;
case RELEASE_1_9_4: Serialize110(data, a_ChunkX, a_ChunkZ); break;
// TODO: Other protocol versions may serialize the data differently; implement here
default:
@ -189,3 +194,292 @@ void cChunkDataSerializer::Serialize47(AString & a_Data, int a_ChunkX, int a_Chu
void cChunkDataSerializer::Serialize107(AString & a_Data, int a_ChunkX, int a_ChunkZ)
{
// This function returns the fully compressed packet (including packet size), not the raw packet!
// Create the packet:
cByteBuffer Packet(512 KiB);
Packet.WriteVarInt32(0x20); // Packet id (Chunk Data packet)
Packet.WriteBEInt32(a_ChunkX);
Packet.WriteBEInt32(a_ChunkZ);
Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
Packet.WriteVarInt32(0x0000ffff); // We're aways sending the full chunk with no additional data, so the bitmap is 0xffff
// Write the chunk size:
const size_t NumChunkSections = 16;
const size_t ChunkSectionBlocks = 16 * 16 * 16;
const size_t BitsPerEntry = 13;
const size_t Mask = (1 << BitsPerEntry) - 1; // Creates a mask that is 13 bits long, ie 0b1111111111111
const size_t ChunkSectionDataArraySize = (ChunkSectionBlocks * BitsPerEntry) / 8 / 8; // Convert from bit count to long count
size_t ChunkSectionSize = (
1 + // Bits per block - set to 13, so the global palette is used and the palette has a length of 0
1 + // Palette length
2 + // Data array length VarInt - 2 bytes for the current value
ChunkSectionDataArraySize * 8 + // Actual block data - multiplied by 8 because first number is longs
sizeof(m_BlockLight) / NumChunkSections // Block light
);
if (m_Dimension == dimOverworld)
{
// Sky light is only sent in the overworld.
ChunkSectionSize += sizeof(m_BlockSkyLight) / NumChunkSections;
}
const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
size_t ChunkSize = (
ChunkSectionSize * 16 +
BiomeDataSize
);
Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize));
// Write each chunk section...
for (size_t SectionIndex = 0; SectionIndex < 16; SectionIndex++)
{
Packet.WriteBEUInt8(BitsPerEntry);
Packet.WriteVarInt32(0); // Palette length is 0
Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize));
size_t StartIndex = SectionIndex * ChunkSectionBlocks;
UInt64 TempLong = 0; // Temporary value that will be stored into
UInt64 CurrentlyWrittenIndex = 0; // "Index" of the long that would be written to
for (size_t Index = 0; Index < ChunkSectionBlocks; Index++)
{
UInt64 Value = static_cast<UInt64>(m_BlockTypes[StartIndex + Index] << 4);
if (Index % 2 == 0)
{
Value |= m_BlockMetas[(StartIndex + Index) / 2] & 0x0f;
}
else
{
Value |= m_BlockMetas[(StartIndex + Index) / 2] >> 4;
}
Value &= Mask; // It shouldn't go out of bounds, but it's still worth being careful
// Painful part where we write data into the long array. Based off of the normal code.
size_t BitPosition = Index * BitsPerEntry;
size_t FirstIndex = BitPosition / 64;
size_t SecondIndex = ((Index + 1) * BitsPerEntry - 1) / 64;
size_t BitOffset = BitPosition % 64;
if (FirstIndex != CurrentlyWrittenIndex)
{
// Write the current data before modifiying it.
Packet.WriteBEUInt64(TempLong);
TempLong = 0;
CurrentlyWrittenIndex = FirstIndex;
}
TempLong |= (Value << BitOffset);
if (FirstIndex != SecondIndex)
{
// Part of the data is now in the second long; write the first one first
Packet.WriteBEUInt64(TempLong);
CurrentlyWrittenIndex = SecondIndex;
TempLong = (Value >> (64 - BitOffset));
}
}
// The last long will generally not be written
Packet.WriteBEUInt64(TempLong);
// Light - stored as a nibble, so we need half sizes
// As far as I know, there isn't a method to only write a range of the array
for (size_t Index = 0; Index < ChunkSectionBlocks / 2; Index++)
{
Packet.WriteBEUInt8(m_BlockLight[(StartIndex / 2) + Index]);
}
if (m_Dimension == dimOverworld)
{
// Skylight is only sent in the overworld; the nether and end do not use it
for (size_t Index = 0; Index < ChunkSectionBlocks / 2; Index++)
{
Packet.WriteBEUInt8(m_BlockSkyLight[(StartIndex / 2) + Index]);
}
}
}
// Write the biome data
Packet.WriteBuf(m_BiomeData, BiomeDataSize);
AString PacketData;
Packet.ReadAll(PacketData);
Packet.CommitRead();
cByteBuffer Buffer(20);
if (PacketData.size() >= 256)
{
if (!cProtocol190::CompressPacket(PacketData, a_Data))
{
ASSERT(!"Packet compression failed.");
a_Data.clear();
return;
}
}
else
{
AString PostData;
Buffer.WriteVarInt32(static_cast<UInt32>(Packet.GetUsedSpace() + 1));
Buffer.WriteVarInt32(0);
Buffer.ReadAll(PostData);
Buffer.CommitRead();
a_Data.clear();
a_Data.reserve(PostData.size() + PacketData.size());
a_Data.append(PostData.data(), PostData.size());
a_Data.append(PacketData.data(), PacketData.size());
}
}
void cChunkDataSerializer::Serialize110(AString & a_Data, int a_ChunkX, int a_ChunkZ)
{
// This function returns the fully compressed packet (including packet size), not the raw packet!
// Create the packet:
cByteBuffer Packet(512 KiB);
Packet.WriteVarInt32(0x20); // Packet id (Chunk Data packet)
Packet.WriteBEInt32(a_ChunkX);
Packet.WriteBEInt32(a_ChunkZ);
Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
Packet.WriteVarInt32(0x0000ffff); // We're aways sending the full chunk with no additional data, so the bitmap is 0xffff
// Write the chunk size:
const size_t NumChunkSections = 16;
const size_t ChunkSectionBlocks = 16 * 16 * 16;
const size_t BitsPerEntry = 13;
const size_t Mask = (1 << BitsPerEntry) - 1; // Creates a mask that is 13 bits long, ie 0b1111111111111
const size_t ChunkSectionDataArraySize = (ChunkSectionBlocks * BitsPerEntry) / 8 / 8; // Convert from bit count to long count
size_t ChunkSectionSize = (
1 + // Bits per block - set to 13, so the global palette is used and the palette has a length of 0
1 + // Palette length
2 + // Data array length VarInt - 2 bytes for the current value
ChunkSectionDataArraySize * 8 + // Actual block data - multiplied by 8 because first number is longs
sizeof(m_BlockLight) / NumChunkSections // Block light
);
if (m_Dimension == dimOverworld)
{
// Sky light is only sent in the overworld.
ChunkSectionSize += sizeof(m_BlockSkyLight) / NumChunkSections;
}
const size_t BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
size_t ChunkSize = (
ChunkSectionSize * 16 +
BiomeDataSize
);
Packet.WriteVarInt32(static_cast<UInt32>(ChunkSize));
// Write each chunk section...
for (size_t SectionIndex = 0; SectionIndex < 16; SectionIndex++)
{
Packet.WriteBEUInt8(BitsPerEntry);
Packet.WriteVarInt32(0); // Palette length is 0
Packet.WriteVarInt32(static_cast<UInt32>(ChunkSectionDataArraySize));
size_t StartIndex = SectionIndex * ChunkSectionBlocks;
UInt64 TempLong = 0; // Temporary value that will be stored into
UInt64 CurrentlyWrittenIndex = 0; // "Index" of the long that would be written to
for (size_t Index = 0; Index < ChunkSectionBlocks; Index++)
{
UInt64 Value = static_cast<UInt64>(m_BlockTypes[StartIndex + Index] << 4);
if (Index % 2 == 0)
{
Value |= m_BlockMetas[(StartIndex + Index) / 2] & 0x0f;
}
else
{
Value |= m_BlockMetas[(StartIndex + Index) / 2] >> 4;
}
Value &= Mask; // It shouldn't go out of bounds, but it's still worth being careful
// Painful part where we write data into the long array. Based off of the normal code.
size_t BitPosition = Index * BitsPerEntry;
size_t FirstIndex = BitPosition / 64;
size_t SecondIndex = ((Index + 1) * BitsPerEntry - 1) / 64;
size_t BitOffset = BitPosition % 64;
if (FirstIndex != CurrentlyWrittenIndex)
{
// Write the current data before modifiying it.
Packet.WriteBEUInt64(TempLong);
TempLong = 0;
CurrentlyWrittenIndex = FirstIndex;
}
TempLong |= (Value << BitOffset);
if (FirstIndex != SecondIndex)
{
// Part of the data is now in the second long; write the first one first
Packet.WriteBEUInt64(TempLong);
CurrentlyWrittenIndex = SecondIndex;
TempLong = (Value >> (64 - BitOffset));
}
}
// The last long will generally not be written
Packet.WriteBEUInt64(TempLong);
// Light - stored as a nibble, so we need half sizes
// As far as I know, there isn't a method to only write a range of the array
for (size_t Index = 0; Index < ChunkSectionBlocks / 2; Index++)
{
Packet.WriteBEUInt8(m_BlockLight[(StartIndex / 2) + Index]);
}
if (m_Dimension == dimOverworld)
{
// Skylight is only sent in the overworld; the nether and end do not use it
for (size_t Index = 0; Index < ChunkSectionBlocks / 2; Index++)
{
Packet.WriteBEUInt8(m_BlockSkyLight[(StartIndex / 2) + Index]);
}
}
}
// Write the biome data
Packet.WriteBuf(m_BiomeData, BiomeDataSize);
// Identify 1.9.4's tile entity list as empty
Packet.WriteBEUInt8(0);
AString PacketData;
Packet.ReadAll(PacketData);
Packet.CommitRead();
cByteBuffer Buffer(20);
if (PacketData.size() >= 256)
{
if (!cProtocol190::CompressPacket(PacketData, a_Data))
{
ASSERT(!"Packet compression failed.");
a_Data.clear();
return;
}
}
else
{
AString PostData;
Buffer.WriteVarInt32(static_cast<UInt32>(Packet.GetUsedSpace() + 1));
Buffer.WriteVarInt32(0);
Buffer.ReadAll(PostData);
Buffer.CommitRead();
a_Data.clear();
a_Data.reserve(PostData.size() + PacketData.size());
a_Data.append(PostData.data(), PostData.size());
a_Data.append(PacketData.data(), PacketData.size());
}
}

View File

@ -17,6 +17,7 @@ protected:
const cChunkDef::BlockNibbles & m_BlockLight;
const cChunkDef::BlockNibbles & m_BlockSkyLight;
const unsigned char * m_BiomeData;
const eDimension m_Dimension;
typedef std::map<int, AString> Serializations;
@ -24,12 +25,16 @@ protected:
void Serialize39(AString & a_Data); // Release 1.3.1 to 1.7.10
void Serialize47(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.8
void Serialize107(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.9
void Serialize110(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.9.4
public:
enum
{
RELEASE_1_3_2 = 39,
RELEASE_1_8_0 = 47,
RELEASE_1_9_0 = 107,
RELEASE_1_9_4 = 110,
} ;
cChunkDataSerializer(
@ -37,7 +42,8 @@ public:
const cChunkDef::BlockNibbles & a_BlockMetas,
const cChunkDef::BlockNibbles & a_BlockLight,
const cChunkDef::BlockNibbles & a_BlockSkyLight,
const unsigned char * a_BiomeData
const unsigned char * a_BiomeData,
const eDimension a_Dimension
);
const AString & Serialize(int a_Version, int a_ChunkX, int a_ChunkZ); // Returns one of the internal m_Serializations[]

View File

@ -64,7 +64,7 @@ public:
virtual void DataReceived(const char * a_Data, size_t a_Size) = 0;
// Sending stuff to clients (alphabetically sorted):
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) = 0;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
@ -74,6 +74,7 @@ public:
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) = 0;
virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) = 0;
virtual void SendDisconnect (const AString & a_Reason) = 0;
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+)
virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) = 0;

View File

@ -160,13 +160,13 @@ void cProtocol172::DataReceived(const char * a_Data, size_t a_Size)
void cProtocol172::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cProtocol172::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt32((a_Vehicle != nullptr) ? a_Vehicle->GetUniqueID() : 0);
Pkt.WriteBEUInt32(a_Vehicle.GetUniqueID());
Pkt.WriteBool(false);
}
@ -324,6 +324,20 @@ void cProtocol172::SendDestroyEntity(const cEntity & a_Entity)
void cProtocol172::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt32(0);
Pkt.WriteBool(false);
}
void cProtocol172::SendDisconnect(const AString & a_Reason)
{
switch (m_State)

View File

@ -61,7 +61,7 @@ public:
virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) override;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
@ -71,6 +71,7 @@ public:
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override;
virtual void SendDisconnect (const AString & a_Reason) override;
virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)

View File

@ -171,13 +171,13 @@ void cProtocol180::DataReceived(const char * a_Data, size_t a_Size)
void cProtocol180::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cProtocol180::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt32((a_Vehicle != nullptr) ? a_Vehicle->GetUniqueID() : 0);
Pkt.WriteBEUInt32(a_Vehicle.GetUniqueID());
Pkt.WriteBool(false);
}
@ -317,6 +317,20 @@ void cProtocol180::SendDestroyEntity(const cEntity & a_Entity)
void cProtocol180::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
Pkt.WriteBEUInt32(0);
Pkt.WriteBool(false);
}
void cProtocol180::SendDisconnect(const AString & a_Reason)
{
switch (m_State)

View File

@ -60,7 +60,7 @@ public:
virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) override;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
@ -70,6 +70,7 @@ public:
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override;
virtual void SendDisconnect (const AString & a_Reason) override;
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;

4306
src/Protocol/Protocol19x.cpp Normal file

File diff suppressed because it is too large Load Diff

360
src/Protocol/Protocol19x.h Normal file
View File

@ -0,0 +1,360 @@
// Protocol19x.h
/*
Declares the 1.9.x protocol classes:
- cProtocol190
- release 1.9.0 protocol (#107)
- cProtocol191
- release 1.9.1 protocol (#108)
- cProtocol192
- release 1.9.2 protocol (#109)
- cProtocol194
- release 1.9.4 protocol (#110)
(others may be added later in the future for the 1.9 release series)
*/
#pragma once
#include "Protocol.h"
#include "../ByteBuffer.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#pragma warning(disable:4244)
#pragma warning(disable:4231)
#pragma warning(disable:4189)
#pragma warning(disable:4702)
#endif
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "PolarSSL++/AesCfb128Decryptor.h"
#include "PolarSSL++/AesCfb128Encryptor.h"
// fwd:
namespace Json
{
class Value;
}
class cProtocol190 :
public cProtocol
{
typedef cProtocol super;
public:
cProtocol190(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
/** Called when client sends some data: */
virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) override;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override;
virtual void SendDisconnect (const AString & a_Reason) override;
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
virtual void SendEntityLook (const cEntity & a_Entity) override;
virtual void SendEntityMetadata (const cEntity & a_Entity) override;
virtual void SendEntityProperties (const cEntity & a_Entity) override;
virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
virtual void SendEntityVelocity (const cEntity & a_Entity) override;
virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
virtual void SendGameMode (eGameMode a_GameMode) override;
virtual void SendHealth (void) override;
virtual void SendHideTitle (void) override;
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (UInt32 a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
virtual void SendLoginSuccess (void) override;
virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override;
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override;
virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override;
virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override;
virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override;
virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) override;
virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override;
virtual void SendPlayerMaxSpeed (void) override;
virtual void SendPlayerMoveLook (void) override;
virtual void SendPlayerPosition (void) override;
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendResetTitle (void) override;
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
virtual void SendSetSubTitle (const cCompositeChat & a_SubTitle) override;
virtual void SendSetRawSubTitle (const AString & a_SubTitle) override;
virtual void SendSetTitle (const cCompositeChat & a_Title) override;
virtual void SendSetRawTitle (const AString & a_Title) override;
virtual void SendSoundParticleEffect (const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
virtual void SendSpawnMob (const cMonster & a_Mob) override;
virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
virtual void SendStatistics (const cStatManager & a_Manager) override;
virtual void SendTabCompletionResults (const AStringVector & a_Results) override;
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void SendWeather (eWeather a_Weather) override;
virtual void SendWholeInventory (const cWindow & a_Window) override;
virtual void SendWindowClose (const cWindow & a_Window) override;
virtual void SendWindowOpen (const cWindow & a_Window) override;
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
virtual AString GetAuthServerID(void) override { return m_AuthServerID; }
/** Compress the packet. a_Packet must be without packet length.
a_Compressed will be set to the compressed packet includes packet length and data length.
If compression fails, the function returns false. */
static bool CompressPacket(const AString & a_Packet, AString & a_Compressed);
/** The 1.8 protocol use a particle id instead of a string. This function converts the name to the id. If the name is incorrect, it returns 0. */
static int GetParticleID(const AString & a_ParticleName);
/** Minecraft 1.8 use other locations to spawn the item frame. This function converts the 1.7 positions to 1.8 positions. */
static void FixItemFramePositions(int a_ObjectData, double & a_PosX, double & a_PosZ, double & a_Yaw);
protected:
AString m_ServerAddress;
UInt16 m_ServerPort;
AString m_AuthServerID;
/** State of the protocol. 1 = status, 2 = login, 3 = game */
UInt32 m_State;
/** Buffer for the received data */
cByteBuffer m_ReceivedData;
bool m_IsEncrypted;
cAesCfb128Decryptor m_Decryptor;
cAesCfb128Encryptor m_Encryptor;
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
cFile m_CommLogFile;
/** The dimension that was last sent to a player in a Respawn or Login packet.
Used to avoid Respawning into the same dimension, which confuses the client. */
eDimension m_LastSentDimension;
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
void AddReceivedData(const char * a_Data, size_t a_Size);
/** Reads and handles the packet. The packet length and type have already been read.
Returns true if the packet was understood, false if it was an unknown packet
*/
bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType);
// Packet handlers while in the Status state (m_State == 1):
virtual void HandlePacketStatusPing(cByteBuffer & a_ByteBuffer);
virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer);
// Packet handlers while in the Login state (m_State == 2):
void HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer);
void HandlePacketLoginStart(cByteBuffer & a_ByteBuffer);
// Packet handlers while in the Game state (m_State == 3):
void HandlePacketAnimation (cByteBuffer & a_ByteBuffer);
void HandlePacketBlockDig (cByteBuffer & a_ByteBuffer);
void HandlePacketBlockPlace (cByteBuffer & a_ByteBuffer);
void HandlePacketChatMessage (cByteBuffer & a_ByteBuffer);
void HandlePacketClientSettings (cByteBuffer & a_ByteBuffer);
void HandlePacketClientStatus (cByteBuffer & a_ByteBuffer);
void HandleConfirmTeleport (cByteBuffer & a_ByteBuffer);
void HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffer);
void HandlePacketEntityAction (cByteBuffer & a_ByteBuffer);
void HandlePacketKeepAlive (cByteBuffer & a_ByteBuffer);
void HandlePacketPlayer (cByteBuffer & a_ByteBuffer);
void HandlePacketPlayerAbilities (cByteBuffer & a_ByteBuffer);
void HandlePacketPlayerLook (cByteBuffer & a_ByteBuffer);
void HandlePacketPlayerPos (cByteBuffer & a_ByteBuffer);
void HandlePacketPlayerPosLook (cByteBuffer & a_ByteBuffer);
void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer);
void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer);
void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer);
void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer);
void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer);
void HandlePacketUseEntity (cByteBuffer & a_ByteBuffer);
void HandlePacketUseItem (cByteBuffer & a_ByteBuffer);
void HandlePacketEnchantItem (cByteBuffer & a_ByteBuffer);
void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer);
void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer);
/** Parses Vanilla plugin messages into specific ClientHandle calls.
The message payload is still in the bytebuffer, the handler reads it specifically for each handled channel */
void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel);
/** Sends the data to the client, encrypting them if needed. */
virtual void SendData(const char * a_Data, size_t a_Size) override;
/** Sends the packet to the client. Called by the cPacketizer's destructor. */
virtual void SendPacket(cPacketizer & a_Packet) override;
void SendCompass(const cWorld & a_World);
/** Reads an item out of the received data, sets a_Item to the values read.
Returns false if not enough received data.
a_KeepRemainingBytes tells the function to keep that many bytes at the end of the buffer. */
virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes = 0);
/** Parses item metadata as read by ReadItem(), into the item enchantments. */
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
void StartEncryption(const Byte * a_Key);
/** Converts the BlockFace received by the protocol into eBlockFace constants.
If the received value doesn't match any of our eBlockFace constants, BLOCK_FACE_NONE is returned. */
eBlockFace FaceIntToBlockFace(UInt32 a_FaceInt);
/** Writes the item data into a packet. */
void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item);
/** Writes the metadata for the specified entity, not including the terminating 0xff. */
void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity);
/** Writes the mob-specific metadata for the specified mob */
void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob);
/** Writes the entity properties for the specified entity, including the Count field. */
void WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity);
/** Writes the block entity data for the specified block entity into the packet. */
void WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity & a_BlockEntity);
/** Types used within metadata */
enum eMetadataType
{
METADATA_TYPE_BYTE = 0,
METADATA_TYPE_VARINT = 1,
METADATA_TYPE_FLOAT = 2,
METADATA_TYPE_STRING = 3,
METADATA_TYPE_CHAT = 4,
METADATA_TYPE_ITEM = 5,
METADATA_TYPE_BOOL = 6,
METADATA_TYPE_ROTATION = 7,
METADATA_TYPE_POSITION = 8,
METADATA_TYPE_OPTIONAL_POSITION = 9,
METADATA_TYPE_DIRECTION = 10,
METADATA_TYPE_OPTIONAL_UUID = 11,
METADATA_TYPE_BLOCKID = 12
} ;
} ;
/** The version 108 protocol, used by 1.9.1. Uses an int rather than a byte for dimension in join game. */
class cProtocol191 :
public cProtocol190
{
typedef cProtocol190 super;
public:
cProtocol191(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
// cProtocol190 overrides:
virtual void SendLogin(const cPlayer & a_Player, const cWorld & a_World) override;
virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override;
} ;
/** The version 109 protocol, used by 1.9.2. Same as 1.9.1, except the server list ping version number changed with the protocol number. */
class cProtocol192 :
public cProtocol191
{
typedef cProtocol191 super;
public:
cProtocol192(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
// cProtocol190 overrides:
virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override;
} ;
/** The version 110 protocol, used by 1.9.3 and 1.9.4. */
class cProtocol194 :
public cProtocol192
{
typedef cProtocol192 super;
public:
cProtocol194(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
// cProtocol190 overrides:
virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
virtual void SendEntityProperties(const cEntity & a_Entity) override;
virtual void SendPlayerMaxSpeed (void) override;
virtual void SendTeleportEntity (const cEntity & a_Entity) override;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override;
} ;

View File

@ -9,6 +9,8 @@
#include "ProtocolRecognizer.h"
#include "Protocol17x.h"
#include "Protocol18x.h"
#include "Protocol19x.h"
#include "Packetizer.h"
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
@ -23,7 +25,8 @@
cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
super(a_Client),
m_Protocol(nullptr),
m_Buffer(8192) // We need a larger buffer to support BungeeCord - it sends one huge packet at the start
m_Buffer(8192), // We need a larger buffer to support BungeeCord - it sends one huge packet at the start
m_InPingForUnrecognizedVersion(false)
{
}
@ -48,6 +51,10 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
case PROTO_VERSION_1_7_2: return "1.7.2";
case PROTO_VERSION_1_7_6: return "1.7.6";
case PROTO_VERSION_1_8_0: return "1.8";
case PROTO_VERSION_1_9_0: return "1.9";
case PROTO_VERSION_1_9_1: return "1.9.1";
case PROTO_VERSION_1_9_2: return "1.9.2";
case PROTO_VERSION_1_9_4: return "1.9.4";
}
ASSERT(!"Unknown protocol version");
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
@ -67,6 +74,33 @@ void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size)
return;
}
if (m_InPingForUnrecognizedVersion)
{
// We already know the verison; handle it here.
UInt32 PacketLen;
UInt32 PacketID;
if (!m_Buffer.ReadVarInt32(PacketLen))
{
return;
}
if (!m_Buffer.ReadVarInt32(PacketID))
{
return;
}
ASSERT(PacketID == 0x01); // Ping packet
ASSERT(PacketLen == 9); // Payload of the packet ID and a UInt64
Int64 Data;
if (!m_Buffer.ReadBEInt64(Data))
{
return;
}
cPacketizer Pkt(*this, 0x01); // Pong packet
Pkt.WriteBEInt64(Data);
return;
}
if (!TryRecognizeProtocol())
{
return;
@ -88,7 +122,7 @@ void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size)
void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
ASSERT(m_Protocol != nullptr);
m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
@ -188,6 +222,16 @@ void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity)
void cProtocolRecognizer::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
ASSERT(m_Protocol != nullptr);
m_Protocol->SendDetachEntity(a_Entity, a_PreviousVehicle);
}
void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
{
if (m_Protocol != nullptr)
@ -196,14 +240,9 @@ void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
}
else
{
// This is used when the client sends a server-ping, respond with the default packet:
static const int Packet = 0xff; // PACKET_DISCONNECT
SendData(reinterpret_cast<const char *>(&Packet), 1); // WriteByte()
auto UTF16 = UTF8ToRawBEUTF16(a_Reason);
static const u_short Size = htons(static_cast<u_short>(UTF16.size()));
SendData(reinterpret_cast<const char *>(&Size), 2); // WriteShort()
SendData(reinterpret_cast<const char *>(UTF16.data()), UTF16.size() * sizeof(char16_t)); // WriteString()
AString Message = Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str());
cPacketizer Pkt(*this, 0x00); // Disconnect packet (in login state)
Pkt.WriteString(Message);
}
}
@ -967,88 +1006,159 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
return false;
}
m_Client->SetProtocolVersion(ProtocolVersion);
AString ServerAddress;
UInt16 ServerPort;
UInt32 NextState;
if (!m_Buffer.ReadVarUTF8String(ServerAddress))
{
return false;
}
if (!m_Buffer.ReadBEUInt16(ServerPort))
{
return false;
}
if (!m_Buffer.ReadVarInt(NextState))
{
return false;
}
m_Buffer.CommitRead();
switch (ProtocolVersion)
{
case PROTO_VERSION_1_7_2:
{
AString ServerAddress;
UInt16 ServerPort;
UInt32 NextState;
if (!m_Buffer.ReadVarUTF8String(ServerAddress))
{
break;
}
if (!m_Buffer.ReadBEUInt16(ServerPort))
{
break;
}
if (!m_Buffer.ReadVarInt(NextState))
{
break;
}
m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
case PROTO_VERSION_1_7_6:
{
AString ServerAddress;
UInt16 ServerPort;
UInt32 NextState;
if (!m_Buffer.ReadVarUTF8String(ServerAddress))
{
break;
}
if (!m_Buffer.ReadBEUInt16(ServerPort))
{
break;
}
if (!m_Buffer.ReadVarInt(NextState))
{
break;
}
m_Buffer.CommitRead();
m_Protocol = new cProtocol176(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
case PROTO_VERSION_1_8_0:
{
AString ServerAddress;
UInt16 ServerPort;
UInt32 NextState;
if (!m_Buffer.ReadVarUTF8String(ServerAddress))
{
break;
}
if (!m_Buffer.ReadBEUInt16(ServerPort))
{
break;
}
if (!m_Buffer.ReadVarInt(NextState))
{
break;
}
m_Buffer.CommitRead();
m_Protocol = new cProtocol180(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
case PROTO_VERSION_1_9_0:
{
m_Protocol = new cProtocol190(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
case PROTO_VERSION_1_9_1:
{
m_Protocol = new cProtocol191(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
case PROTO_VERSION_1_9_2:
{
m_Protocol = new cProtocol192(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
case PROTO_VERSION_1_9_4:
{
m_Protocol = new cProtocol194(m_Client, ServerAddress, ServerPort, NextState);
return true;
}
default:
{
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))",
m_Client->GetIPString().c_str(), ProtocolVersion, ProtocolVersion
);
if (NextState != 1)
{
m_Client->Kick(Printf("Unsupported protocol version %u, please use one of these versions:\n" MCS_CLIENT_VERSIONS, ProtocolVersion));
return false;
}
else
{
m_InPingForUnrecognizedVersion = true;
UInt32 PacketLen;
UInt32 PacketID;
if (!m_Buffer.ReadVarInt32(PacketLen))
{
return false;
}
if (!m_Buffer.ReadVarInt32(PacketID))
{
return false;
}
ASSERT(PacketID == 0x00); // Request packet
ASSERT(PacketLen == 1); // No payload except for packet ID
SendPingStatusResponse();
}
return false;
}
}
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))",
m_Client->GetIPString().c_str(), ProtocolVersion, ProtocolVersion
);
m_Client->Kick("Unsupported protocol version");
return false;
}
void cProtocolRecognizer::SendPacket(cPacketizer & a_Pkt)
{
// This function should never be called - it needs to exists so that cProtocolRecognizer can be instantiated,
// but the actual sending is done by the internal m_Protocol itself.
LOGWARNING("%s: This function shouldn't ever be called.", __FUNCTION__);
ASSERT(!"Function not to be called");
// Writes out the packet normally.
UInt32 PacketLen = static_cast<UInt32>(m_OutPacketBuffer.GetUsedSpace());
AString PacketData, CompressedPacket;
m_OutPacketBuffer.ReadAll(PacketData);
m_OutPacketBuffer.CommitRead();
// Compression doesn't apply to this state, send raw data:
m_OutPacketLenBuffer.WriteVarInt32(PacketLen);
AString LengthData;
m_OutPacketLenBuffer.ReadAll(LengthData);
SendData(LengthData.data(), LengthData.size());
// Send the packet's payload
m_OutPacketLenBuffer.CommitRead();
SendData(PacketData.data(), PacketData.size());
}
void cProtocolRecognizer::SendPingStatusResponse(void)
{
cServer * Server = cRoot::Get()->GetServer();
AString ServerDescription = Server->GetDescription();
int NumPlayers = Server->GetNumPlayers();
int MaxPlayers = Server->GetMaxPlayers();
AString Favicon = Server->GetFaviconData();
cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
// Version:
Json::Value Version;
Version["name"] = "Cuberite " MCS_CLIENT_VERSIONS;
Version["protocol"] = 0; // Force client to think this is an invalid version (no other good default)
// Players:
Json::Value Players;
Players["online"] = NumPlayers;
Players["max"] = MaxPlayers;
// TODO: Add "sample"
// Description:
Json::Value Description;
Description["text"] = ServerDescription.c_str();
// Create the response:
Json::Value ResponseValue;
ResponseValue["version"] = Version;
ResponseValue["players"] = Players;
ResponseValue["description"] = Description;
if (!Favicon.empty())
{
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
}
Json::StyledWriter Writer;
AString Response = Writer.write(ResponseValue);
cPacketizer Pkt(*this, 0x00); // Response packet
Pkt.WriteString(Response);
}

View File

@ -18,8 +18,8 @@
// Adjust these if a new protocol is added or an old one is removed:
#define MCS_CLIENT_VERSIONS "1.7.x, 1.8.x"
#define MCS_PROTOCOL_VERSIONS "4, 5, 47"
#define MCS_CLIENT_VERSIONS "1.7.x, 1.8.x, 1.9.x"
#define MCS_PROTOCOL_VERSIONS "4, 5, 47, 107, 108, 109, 110"
@ -36,6 +36,10 @@ public:
PROTO_VERSION_1_7_2 = 4,
PROTO_VERSION_1_7_6 = 5,
PROTO_VERSION_1_8_0 = 47,
PROTO_VERSION_1_9_0 = 107,
PROTO_VERSION_1_9_1 = 108,
PROTO_VERSION_1_9_2 = 109,
PROTO_VERSION_1_9_4 = 110,
} ;
cProtocolRecognizer(cClientHandle * a_Client);
@ -48,7 +52,7 @@ public:
virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle) override;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
@ -58,6 +62,7 @@ public:
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override;
virtual void SendDisconnect (const AString & a_Reason) override;
virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
@ -133,6 +138,8 @@ public:
virtual void SendData(const char * a_Data, size_t a_Size) override;
void SendPingStatusResponse(void);
protected:
/** The recognized protocol */
cProtocol * m_Protocol;
@ -140,6 +147,9 @@ protected:
/** Buffer for the incoming data until we recognize the protocol */
cByteBuffer m_Buffer;
/** Is a server list ping for an unrecognized version currently occuring? */
bool m_InPingForUnrecognizedVersion;
/** Tries to recognize protocol based on m_Buffer contents; returns true if recognized */
bool TryRecognizeProtocol(void);

View File

@ -2263,9 +2263,9 @@ bool cWorld::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height)
void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
return m_ChunkMap->BroadcastAttachEntity(a_Entity, a_Vehicle);
m_ChunkMap->BroadcastAttachEntity(a_Entity, a_Vehicle);
}
@ -2353,6 +2353,15 @@ void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandl
void cWorld::BroadcastDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
m_ChunkMap->BroadcastDetachEntity(a_Entity, a_PreviousVehicle);
}
void cWorld::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration, a_Exclude);

View File

@ -173,7 +173,7 @@ public:
// Broadcast respective packets to all clients of the chunk where the event is taking place
// (Please keep these alpha-sorted)
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle);
void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, Byte a_Byte1, Byte a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr); // tolua_export
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude
@ -191,6 +191,7 @@ public:
void BroadcastCollectEntity (const cEntity & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle);
void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);