mirror of
https://github.com/celisej567/mcpe.git
synced 2026-09-04 19:36:43 +03:00
* Improve the level sending mechanism in the game.
Instead of sending a request for each chunk one by one, and in sequence, send all the level data in one block. I know this is not the best solution since we are working with UDP here, but it's way faster than loading the level chunk by chunk. In a blink of an eye, the level has already loaded.
This commit is contained in:
@@ -373,6 +373,11 @@ uint8_t* ZlibInflateToMemory(uint8_t* pInput, size_t compressedSize, size_t deco
|
||||
}
|
||||
|
||||
uint8_t* ZlibDeflateToMemory(uint8_t* pInput, size_t sizeBytes, size_t* compressedSizeOut)
|
||||
{
|
||||
return ZlibDeflateToMemoryLvl(pInput, sizeBytes, compressedSizeOut, Z_DEFAULT_COMPRESSION);
|
||||
}
|
||||
|
||||
uint8_t* ZlibDeflateToMemoryLvl(uint8_t* pInput, size_t sizeBytes, size_t* compressedSizeOut, int level)
|
||||
{
|
||||
z_stream strm;
|
||||
memset(&strm, 0, sizeof strm);
|
||||
@@ -384,7 +389,7 @@ uint8_t* ZlibDeflateToMemory(uint8_t* pInput, size_t sizeBytes, size_t* compress
|
||||
strm.opaque = Z_NULL;
|
||||
|
||||
// initialize deflation state machine
|
||||
ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
|
||||
ret = deflateInit(&strm, level);
|
||||
if (ret != Z_OK)
|
||||
return nullptr;
|
||||
|
||||
@@ -394,7 +399,7 @@ uint8_t* ZlibDeflateToMemory(uint8_t* pInput, size_t sizeBytes, size_t* compress
|
||||
uint8_t* pOut = new uint8_t[sizeBytes + ZLIB_PADDING_BYTES];
|
||||
strm.avail_in = sizeBytes;
|
||||
strm.next_in = pInput;
|
||||
strm.avail_in = sizeBytes + ZLIB_PADDING_BYTES;
|
||||
strm.avail_out = sizeBytes + ZLIB_PADDING_BYTES;
|
||||
strm.next_out = pOut;
|
||||
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
|
||||
@@ -567,6 +567,7 @@ bool DeleteDirectory(const std::string& name, bool unused);
|
||||
// compress and decompress stuff with zlib: ( you must SAFE_DELETE_ARRAY what it returns )
|
||||
uint8_t* ZlibInflateToMemory(uint8_t* pInput, size_t compressedSize, size_t decompressedSize);
|
||||
uint8_t* ZlibDeflateToMemory(uint8_t* pInput, size_t sizeBytes, size_t *compressedSizeOut);
|
||||
uint8_t* ZlibDeflateToMemoryLvl(uint8_t* pInput, size_t sizeBytes, size_t* compressedSizeOut, int level);
|
||||
|
||||
// things that we added:
|
||||
#ifndef ORIGINAL_CODE
|
||||
|
||||
@@ -26,6 +26,8 @@ ClientSideNetworkHandler::ClientSideNetworkHandler(Minecraft* pMinecraft, RakNet
|
||||
m_pMinecraft = pMinecraft;
|
||||
m_pRakNetInstance = pRakNetInstance;
|
||||
m_pServerPeer = m_pRakNetInstance->getPeer();
|
||||
m_chunksRequested = 0;
|
||||
m_serverProtocolVersion = 0;
|
||||
}
|
||||
|
||||
void ClientSideNetworkHandler::levelGenerated(Level* level)
|
||||
@@ -108,6 +110,8 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, StartGa
|
||||
|
||||
m_pLevel->setTime(pStartGamePkt->m_time);
|
||||
|
||||
m_serverProtocolVersion = pStartGamePkt->m_version;
|
||||
|
||||
m_pMinecraft->setLevel(m_pLevel, "ClientSideNetworkHandler -> setLevel", pLocalPlayer);
|
||||
}
|
||||
|
||||
@@ -326,10 +330,13 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, ChunkDa
|
||||
|
||||
pChunk->m_bUnsaved = true;
|
||||
|
||||
if (areAllChunksLoaded())
|
||||
flushAllBufferedUpdates();
|
||||
else
|
||||
requestNextChunk();
|
||||
if (m_serverProtocolVersion < 2)
|
||||
{
|
||||
if (areAllChunksLoaded())
|
||||
flushAllBufferedUpdates();
|
||||
else
|
||||
requestNextChunk();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, PlayerEquipmentPacket* pPlayerEquipmentPkt)
|
||||
@@ -355,6 +362,99 @@ void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& rakGuid, PlayerE
|
||||
pPlayer->m_pInventory->selectItemById(pPlayerEquipmentPkt->m_itemID);
|
||||
}
|
||||
|
||||
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LevelDataPacket* packet)
|
||||
{
|
||||
int uncompMagic = 12847812, compMagic = 58712758, chunkSepMagic = 284787658;
|
||||
RakNet::BitStream* bs = &packet->m_data, bs2;
|
||||
|
||||
int thing = 0;
|
||||
bs->Read(thing);
|
||||
if (thing != compMagic && thing != uncompMagic)
|
||||
{
|
||||
LogMsg("Error, invalid level data packet with magic %d", thing);
|
||||
return;
|
||||
}
|
||||
|
||||
if (thing == compMagic)
|
||||
{
|
||||
int uncompSize = 0, compSize = 0;
|
||||
bs->Read(uncompSize);
|
||||
bs->Read(compSize);
|
||||
|
||||
LogMsg("Decompressing level data. Compressed, uncompressed sizes: %d bytes, %d bytes", compSize, uncompSize);
|
||||
|
||||
uint8_t* pCompData = new uint8_t[compSize];
|
||||
bs->Read((char*)pCompData, compSize);
|
||||
|
||||
uint8_t* pUncompData = ZlibInflateToMemory(pCompData, compSize, uncompSize);
|
||||
delete[] pCompData;
|
||||
|
||||
if (!pUncompData)
|
||||
{
|
||||
LogMsg("Error, can't decompress level data");
|
||||
return;
|
||||
}
|
||||
|
||||
bs2.Reset();
|
||||
bs2.Write((const char*)pUncompData, uncompSize);
|
||||
bs2.ResetReadPointer();
|
||||
bs = &bs2;
|
||||
|
||||
bs->Read(thing);
|
||||
}
|
||||
|
||||
int chunksX = 0, chunksZ = 0;
|
||||
bs->Read(chunksX);
|
||||
bs->Read(chunksZ);
|
||||
|
||||
if (chunksX != C_MAX_CHUNKS_X || chunksZ != C_MAX_CHUNKS_Z)
|
||||
{
|
||||
LogMsg("Error, we don't yet support a level of size %d x %d chunks. Some chunks may disappear or be regenerated.", chunksX, chunksZ);
|
||||
}
|
||||
|
||||
for (int x = 0; x < chunksX; x++)
|
||||
{
|
||||
for (int z = 0; z < chunksZ; z++)
|
||||
{
|
||||
uint8_t ptype = 0;
|
||||
bs->Read(thing);
|
||||
|
||||
int dataSize = 0;
|
||||
bs->Read(dataSize);
|
||||
|
||||
if (thing != chunkSepMagic)
|
||||
{
|
||||
_FAIL_BECAUSE_INVALID:
|
||||
LogMsg("Error, aborting because level data is invalid. Reading chunk %d,%d. Thing=%d PType=%d",x,z,thing,ptype);
|
||||
return;
|
||||
}
|
||||
|
||||
LevelChunk* pChunk = m_pLevel->getChunk(x, z);
|
||||
if (!pChunk)
|
||||
LogMsg("No chunk at %d,%d", x, z);
|
||||
|
||||
RakNet::BitStream bs2;
|
||||
bs2.Write(*bs, 8 * dataSize);
|
||||
|
||||
bs2.Read(ptype);
|
||||
if (ptype != PACKET_CHUNK_DATA)
|
||||
goto _FAIL_BECAUSE_INVALID;
|
||||
|
||||
ChunkDataPacket cdp(x, z, pChunk);
|
||||
cdp.read(&bs2);
|
||||
LogMsg("Chunk %d,%d", cdp.m_x, cdp.m_z);
|
||||
|
||||
if (pChunk)
|
||||
handle(guid, &cdp);
|
||||
|
||||
while (m_pLevel->updateLights());
|
||||
}
|
||||
}
|
||||
|
||||
m_chunksRequested = 256;// all chunks are loaded!!
|
||||
flushAllBufferedUpdates();
|
||||
}
|
||||
|
||||
bool ClientSideNetworkHandler::areAllChunksLoaded()
|
||||
{
|
||||
return m_chunksRequested > 255;
|
||||
@@ -368,8 +468,15 @@ void ClientSideNetworkHandler::requestNextChunk()
|
||||
// @BUG: The return value of areAllChunksLoaded() is actually true even before the
|
||||
// 256th chunk is loaded.
|
||||
|
||||
m_pRakNetInstance->send(new RequestChunkPacket(m_chunksRequested % 16, m_chunksRequested / 16));
|
||||
m_chunksRequested++;
|
||||
if (m_serverProtocolVersion < 2)
|
||||
{
|
||||
m_pRakNetInstance->send(new RequestChunkPacket(m_chunksRequested % 16, m_chunksRequested / 16));
|
||||
m_chunksRequested++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pRakNetInstance->send(new RequestChunkPacket(-9999, -9999));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientSideNetworkHandler::flushAllBufferedUpdates()
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
void handle(const RakNet::RakNetGUID&, UpdateBlockPacket*) override;
|
||||
void handle(const RakNet::RakNetGUID&, ChunkDataPacket*) override;
|
||||
void handle(const RakNet::RakNetGUID&, PlayerEquipmentPacket*) override;
|
||||
void handle(const RakNet::RakNetGUID&, LevelDataPacket*) override;
|
||||
|
||||
bool areAllChunksLoaded();
|
||||
void requestNextChunk();
|
||||
@@ -56,6 +57,7 @@ private:
|
||||
RakNet::RakNetGUID m_serverGUID;
|
||||
int m_field_24;
|
||||
std::vector<SBufferedBlockUpdate> m_bufferedBlockUpdates;
|
||||
int m_chunksRequested = 0;
|
||||
int m_chunksRequested;
|
||||
int m_serverProtocolVersion;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@ Packet* MinecraftPackets::createPacket(int type)
|
||||
return new ChunkDataPacket;
|
||||
case PACKET_PLAYER_EQUIPMENT:
|
||||
return new PlayerEquipmentPacket;
|
||||
|
||||
case PACKET_LEVEL_DATA:
|
||||
return new LevelDataPacket;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -76,3 +76,7 @@ void NetEventCallback::handle(const RakNet::RakNetGUID& guid, ChunkDataPacket* p
|
||||
void NetEventCallback::handle(const RakNet::RakNetGUID& guid, PlayerEquipmentPacket* packet)
|
||||
{
|
||||
}
|
||||
|
||||
void NetEventCallback::handle(const RakNet::RakNetGUID& guid, LevelDataPacket* packet)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ class UpdateBlockPacket;
|
||||
class RequestChunkPacket;
|
||||
class ChunkDataPacket;
|
||||
class PlayerEquipmentPacket;
|
||||
class LevelDataPacket;
|
||||
#include "world/level/Level.hpp"
|
||||
class Level;
|
||||
class LevelChunk;
|
||||
|
||||
class NetEventCallback
|
||||
{
|
||||
@@ -47,5 +49,6 @@ public:
|
||||
virtual void handle(const RakNet::RakNetGUID&, RequestChunkPacket*);
|
||||
virtual void handle(const RakNet::RakNetGUID&, ChunkDataPacket*);
|
||||
virtual void handle(const RakNet::RakNetGUID&, PlayerEquipmentPacket*);
|
||||
virtual void handle(const RakNet::RakNetGUID&, LevelDataPacket*);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "NetEventCallback.hpp"
|
||||
|
||||
class NetEventCallback;
|
||||
class Level;
|
||||
class LevelChunk;
|
||||
|
||||
enum ePacketType : uint8_t
|
||||
@@ -32,6 +33,8 @@ enum ePacketType : uint8_t
|
||||
PACKET_REQUEST_CHUNK,
|
||||
PACKET_CHUNK_DATA,
|
||||
PACKET_PLAYER_EQUIPMENT,
|
||||
|
||||
PACKET_LEVEL_DATA = 200,
|
||||
};
|
||||
|
||||
class Packet
|
||||
@@ -227,6 +230,19 @@ public:
|
||||
LevelChunk* m_pChunk;
|
||||
};
|
||||
|
||||
class LevelDataPacket : public Packet
|
||||
{
|
||||
public:
|
||||
LevelDataPacket() {}
|
||||
LevelDataPacket(Level* level) : m_pLevel(level) {}
|
||||
void handle(const RakNet::RakNetGUID&, NetEventCallback* pCallback) override;
|
||||
void write(RakNet::BitStream*);
|
||||
void read(RakNet::BitStream*);
|
||||
public:
|
||||
RakNet::BitStream m_data;
|
||||
Level* m_pLevel;
|
||||
};
|
||||
|
||||
class PlayerEquipmentPacket : public Packet
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -113,7 +113,7 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, LoginPacke
|
||||
sgp.field_10 = pPlayer->m_pos.x;
|
||||
sgp.field_14 = pPlayer->m_pos.y - pPlayer->field_84;
|
||||
sgp.field_18 = pPlayer->m_pos.z;
|
||||
sgp.m_version = 1;
|
||||
sgp.m_version = 2;
|
||||
sgp.m_time = m_pLevel->getTime();
|
||||
|
||||
RakNet::BitStream sgpbs;
|
||||
@@ -292,6 +292,12 @@ void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& guid, RequestChu
|
||||
{
|
||||
puts_ignorable("RequestChunkPacket");
|
||||
|
||||
if (packet->m_x == -9999)
|
||||
{
|
||||
m_pRakNetInstance->send(guid, new LevelDataPacket(m_pLevel));
|
||||
return;
|
||||
}
|
||||
|
||||
LevelChunk* pChunk = m_pLevel->getChunk(packet->m_x, packet->m_z);
|
||||
if (!pChunk)
|
||||
{
|
||||
|
||||
@@ -44,6 +44,7 @@ void ChunkDataPacket::write(RakNet::BitStream* bs)
|
||||
}
|
||||
}
|
||||
|
||||
m_data.ResetReadPointer();
|
||||
bs->Write(m_data);
|
||||
}
|
||||
|
||||
@@ -52,4 +53,5 @@ void ChunkDataPacket::read(RakNet::BitStream* bs)
|
||||
bs->Read(m_x);
|
||||
bs->Read(m_z);
|
||||
bs->Read(m_data);
|
||||
m_data.ResetReadPointer();
|
||||
}
|
||||
|
||||
86
source/client/network/packets/LevelDataPacket.cpp
Normal file
86
source/client/network/packets/LevelDataPacket.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/********************************************************************
|
||||
Minecraft: Pocket Edition - Decompilation Project
|
||||
Copyright (C) 2023 iProgramInCpp
|
||||
|
||||
The following code is licensed under the BSD 1 clause license.
|
||||
SPDX-License-Identifier: BSD-1-Clause
|
||||
********************************************************************/
|
||||
|
||||
#include "../Packet.hpp"
|
||||
#include "world/level/levelgen/chunk/LevelChunk.hpp"
|
||||
|
||||
void LevelDataPacket::handle(const RakNet::RakNetGUID& guid, NetEventCallback* pCallback)
|
||||
{
|
||||
pCallback->handle(guid, this);
|
||||
}
|
||||
|
||||
void LevelDataPacket::write(RakNet::BitStream* pbs)
|
||||
{
|
||||
// @TODO: Maybe offload this to a different 'worker thread'? Or maybe just the compression job?
|
||||
|
||||
// send a crapton of them in a raw packet(why? Laziness)
|
||||
int chunksX = C_MAX_CHUNKS_X;
|
||||
int chunksZ = C_MAX_CHUNKS_Z;
|
||||
int minus9999 = -9999;
|
||||
RakNet::BitStream bs;
|
||||
pbs->Write(PACKET_LEVEL_DATA);
|
||||
|
||||
int uncompMagic = 12847812, compMagic = 58712758, chunkSepMagic = 284787658;
|
||||
bs.Write(uncompMagic);
|
||||
bs.Write(chunksX);
|
||||
bs.Write(chunksZ);
|
||||
for (int x = 0; x < chunksX; x++)
|
||||
{
|
||||
for (int z = 0; z < chunksZ; z++)
|
||||
{
|
||||
bs.Write(chunkSepMagic);
|
||||
|
||||
RakNet::BitStream bs2;
|
||||
LevelChunk* pChunk = m_pLevel->getChunk(x, z);
|
||||
ChunkDataPacket cdp(x, z, pChunk);
|
||||
cdp.write(&bs2);
|
||||
|
||||
int dataSize = int(bs2.GetNumberOfBytesUsed());
|
||||
bs.Write(dataSize);
|
||||
bs.Write((const char*)bs2.GetData(), dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
// compress it
|
||||
size_t compSize = 0;
|
||||
size_t uncompSize = bs.GetNumberOfBytesUsed();
|
||||
uint8_t* pCompressedData = nullptr;
|
||||
|
||||
if (uncompSize > 1024)
|
||||
{
|
||||
// takes about 5ms on release to compress everything on L4, compression ratio is around 14-15%.
|
||||
// Increase or decrease this depending on your server's load.
|
||||
pCompressedData = ZlibDeflateToMemoryLvl(bs.GetData(), uncompSize, &compSize, 4);
|
||||
}
|
||||
|
||||
if (pCompressedData)
|
||||
{
|
||||
float ratio = 100.0f * float(compSize) / float(uncompSize);
|
||||
LogMsg("Compression ratio: %.2f (%d comp, %d uncomp)", ratio, int(compSize), int(uncompSize));
|
||||
|
||||
int cs2 = int(compSize), us2 = int(uncompSize);
|
||||
bs.Reset();
|
||||
bs.Write(compMagic);
|
||||
bs.Write(us2);
|
||||
bs.Write(cs2);
|
||||
bs.Write((const char*)pCompressedData, compSize);
|
||||
SAFE_DELETE_ARRAY(pCompressedData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg("Level not compressed.");
|
||||
}
|
||||
|
||||
pbs->Write(bs);
|
||||
return;
|
||||
}
|
||||
|
||||
void LevelDataPacket::read(RakNet::BitStream* bs)
|
||||
{
|
||||
bs->Read(m_data);
|
||||
}
|
||||
@@ -433,6 +433,7 @@
|
||||
<ClCompile Include="..\source\client\network\NetEventCallback.cpp" />
|
||||
<ClCompile Include="..\source\client\network\packets\AddPlayerPacket.cpp" />
|
||||
<ClCompile Include="..\source\client\network\packets\ChunkDataPacket.cpp" />
|
||||
<ClCompile Include="..\source\client\network\packets\LevelDataPacket.cpp" />
|
||||
<ClCompile Include="..\source\client\network\packets\LoginPacket.cpp" />
|
||||
<ClCompile Include="..\source\client\network\packets\MessagePacket.cpp" />
|
||||
<ClCompile Include="..\source\client\network\packets\MovePlayerPacket.cpp" />
|
||||
|
||||
Reference in New Issue
Block a user