mirror of
https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1.git
synced 2026-03-20 15:03:32 +00:00
927 lines
28 KiB
C++
Executable File
927 lines
28 KiB
C++
Executable File
|
|
#include "ClientSideNetworkHandler.h"
|
|
#include "client/Options.h"
|
|
#include "packet/PacketInclude.h"
|
|
#include "RakNetInstance.h"
|
|
#include "../world/level/chunk/ChunkSource.h"
|
|
#include "../world/level/Level.h"
|
|
#include "../world/level/storage/LevelStorageSource.h"
|
|
#include "../world/entity/player/Player.h"
|
|
#include "../world/entity/player/Inventory.h"
|
|
#include "../client/Minecraft.h"
|
|
#include "../client/gamemode/GameMode.h"
|
|
#ifndef STANDALONE_SERVER
|
|
#include "../client/gui/screens/DisconnectionScreen.h"
|
|
#endif
|
|
#include "../client/player/LocalPlayer.h"
|
|
#include "../client/multiplayer/MultiPlayerLevel.h"
|
|
#include "../client/player/input/KeyboardInput.h"
|
|
#include "../client/sound/SoundEngine.h"
|
|
#include "../world/entity/MobFactory.h"
|
|
#include "../raknet/RakPeerInterface.h"
|
|
#include "../world/level/Explosion.h"
|
|
#include "../world/level/tile/entity/FurnaceTileEntity.h"
|
|
#include "../world/inventory/BaseContainerMenu.h"
|
|
#ifndef STANDALONE_SERVER
|
|
#include "../client/particle/TakeAnimationParticle.h"
|
|
#endif
|
|
#include "../world/entity/EntityFactory.h"
|
|
#include "../world/entity/item/PrimedTnt.h"
|
|
#include "../world/entity/projectile/Arrow.h"
|
|
#include "../world/level/tile/entity/ChestTileEntity.h"
|
|
#include "../client/player/RemotePlayer.h"
|
|
#include "../world/level/tile/LevelEvent.h"
|
|
#include "../world/entity/item/FallingTile.h"
|
|
|
|
static MultiPlayerLevel* mpcast(Level* l) { return (MultiPlayerLevel*) l; }
|
|
|
|
ClientSideNetworkHandler::ClientSideNetworkHandler(Minecraft* minecraft, IRakNetInstance* raknetInstance)
|
|
: minecraft(minecraft),
|
|
raknetInstance(raknetInstance),
|
|
level(NULL),
|
|
requestNextChunkPosition(0),
|
|
requestNextChunkIndex(0)
|
|
{
|
|
rakPeer = raknetInstance->getPeer();
|
|
}
|
|
|
|
ClientSideNetworkHandler::~ClientSideNetworkHandler()
|
|
{
|
|
}
|
|
|
|
void ClientSideNetworkHandler::requestNextChunk()
|
|
{
|
|
if (requestNextChunkIndex < NumRequestChunks)
|
|
{
|
|
IntPair& chunk = requestNextChunkIndexList[requestNextChunkIndex];
|
|
RequestChunkPacket packet(chunk.x, chunk.y);
|
|
raknetInstance->send(packet);
|
|
|
|
//LOGI("requesting chunks @ (%d, %d)\n", chunk.x, chunk.y);
|
|
|
|
//raknetInstance->send(new RequestChunkPacket(requestNextChunkPosition % CHUNK_CACHE_WIDTH, requestNextChunkPosition / CHUNK_CACHE_WIDTH));
|
|
requestNextChunkIndex++;
|
|
requestNextChunkPosition++;
|
|
}
|
|
}
|
|
|
|
bool ClientSideNetworkHandler::areAllChunksLoaded()
|
|
{
|
|
return (requestNextChunkPosition >= (CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH));
|
|
}
|
|
|
|
bool ClientSideNetworkHandler::isChunkLoaded(int x, int z)
|
|
{
|
|
if (x < 0 || x >= CHUNK_CACHE_WIDTH || z < 0 || z >= CHUNK_CACHE_WIDTH) {
|
|
LOGE("Error: Tried to request chunk (%d, %d)\n", x, z);
|
|
return true;
|
|
}
|
|
return chunksLoaded[x * CHUNK_CACHE_WIDTH + z];
|
|
//return areAllChunksLoaded();
|
|
}
|
|
|
|
void ClientSideNetworkHandler::onConnect(const RakNet::RakNetGUID& hostGuid)
|
|
{
|
|
LOGI("onConnect, server guid: %s, local guid: %s\n", hostGuid.ToString(), rakPeer->GetMyGUID().ToString());
|
|
serverGuid = hostGuid;
|
|
|
|
clearChunksLoaded();
|
|
LoginPacket packet(minecraft->options.getStringValue(OPTIONS_USERNAME).c_str(), SharedConstants::NetworkProtocolVersion);
|
|
raknetInstance->send(packet);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::onUnableToConnect()
|
|
{
|
|
LOGI("onUnableToConnect\n");
|
|
}
|
|
|
|
void ClientSideNetworkHandler::onDisconnect(const RakNet::RakNetGUID& guid)
|
|
{
|
|
LOGI("onDisconnect\n");
|
|
if (level)
|
|
{
|
|
level->isClientSide = false;
|
|
for (int i = (int)level->players.size()-1; i >= 0; --i ) {
|
|
Player* p = level->players[i];
|
|
if (p != minecraft->player) {
|
|
p->reallyRemoveIfPlayer = true;
|
|
level->removeEntity(p);
|
|
}
|
|
}
|
|
}
|
|
#ifndef STANDALONE_SERVER
|
|
minecraft->gui.addMessage("Disconnected from server");
|
|
#endif
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LoginStatusPacket* packet) {
|
|
if (packet->status == LoginStatus::Success) {
|
|
raknetInstance->setIsLoggedIn(true);
|
|
return;
|
|
}
|
|
|
|
if (packet->status == LoginStatus::Failed_ClientOld) {
|
|
LOGI("Disconnect! Client is outdated!\n");
|
|
#ifndef STANDALONE_SERVER
|
|
minecraft->setScreen(new DisconnectionScreen("Could not connect: Outdated client!"));
|
|
#endif
|
|
}
|
|
if (packet->status == LoginStatus::Failed_ServerOld) {
|
|
LOGI("Disconnect! Server is outdated!\n");
|
|
#ifndef STANDALONE_SERVER
|
|
minecraft->setScreen(new DisconnectionScreen("Could not connect: Outdated server!"));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, StartGamePacket* packet)
|
|
{
|
|
LOGI("StartGamePacket\n");
|
|
|
|
#ifdef RPI
|
|
if (packet->gameType != GameType::Creative) {
|
|
minecraft->setScreen(new DisconnectionScreen("Could not connect: Incompatible server!"));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
const std::string& levelId = LevelStorageSource::TempLevelId;
|
|
LevelStorageSource* storageSource = minecraft->getLevelSource();
|
|
storageSource->deleteLevel(levelId);
|
|
//level = new Level(storageSource->selectLevel(levelId, true), "temp", packet->levelSeed, SharedConstants::StorageVersion);
|
|
MultiPlayerLevel* level = new MultiPlayerLevel(
|
|
storageSource->selectLevel(levelId, true),
|
|
"temp",
|
|
LevelSettings(packet->levelSeed, LevelSettings::validateGameType(packet->gameType)),
|
|
SharedConstants::StorageVersion);
|
|
level->isClientSide = true;
|
|
|
|
bool isCreative = (packet->gameType == GameType::Creative);
|
|
LocalPlayer* player = new LocalPlayer(minecraft, level, minecraft->options.getStringValue(OPTIONS_USERNAME), level->dimension->id, isCreative);
|
|
player->owner = rakPeer->GetMyGUID();
|
|
player->entityId = packet->entityId;
|
|
player->moveTo(packet->x, packet->y, packet->z, player->yRot, player->xRot);
|
|
|
|
LOGI("new pos: %f, %f [%f - %f]\n", player->x, player->z, player->bb.y0, player->bb.y1);
|
|
|
|
minecraft->setLevel(level, "ClientSideNetworkHandler -> setLevel", player);
|
|
minecraft->setIsCreativeMode(isCreative);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MessagePacket* packet)
|
|
{
|
|
LOGI("MessagePacket\n");
|
|
#ifndef STANDALONE_SERVER
|
|
minecraft->gui.addMessage(packet->message.C_String());
|
|
#endif
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetTimePacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
LOGI("SetTimePacket\n");
|
|
level->setTime(packet->time);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddEntityPacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
Entity* e = EntityFactory::CreateEntity(packet->type, level);
|
|
if (!e)
|
|
return;
|
|
|
|
e->entityId = packet->entityId;
|
|
e->setPos(packet->x, packet->y, packet->z);
|
|
|
|
// Entity Specific stuff here
|
|
switch (packet->type)
|
|
{
|
|
case EntityTypes::IdFallingTile: {
|
|
int data = -packet->data();
|
|
FallingTile* ft = (FallingTile*) e;
|
|
ft->tile = data & 0xff;
|
|
ft->data = data >> 16;
|
|
}
|
|
case EntityTypes::IdArrow: {
|
|
Entity* owner = level->getEntity(packet->data());
|
|
if (owner && owner->isMob())
|
|
((Arrow*)e)->ownerId = owner->entityId;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (packet->hasMovementData()) {
|
|
/*
|
|
e->xd = packet->xd;
|
|
e->yd = packet->yd;
|
|
e->zd = packet->zd;
|
|
*/
|
|
e->lerpMotion(packet->xd, packet->yd, packet->zd);
|
|
//LOGI("Client: reading entity with %f, %f, %f\n", e->xd, e->yd, e->zd);
|
|
}
|
|
|
|
mpcast(level)->putEntity(packet->entityId, e);
|
|
}
|
|
void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, AddPaintingPacket* packet ) {
|
|
if (!level)
|
|
return;
|
|
|
|
Painting* painting = new Painting(level, packet->xTile, packet->yTile, packet->zTile, packet->dir, packet->motive);
|
|
mpcast(level)->putEntity(packet->entityId, painting);
|
|
}
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddMobPacket* packet)
|
|
{
|
|
LOGI("AddMobPacket (%p)\n", level);
|
|
|
|
if (!level) {
|
|
LOGW("Trying to add a mob with no level!\n");
|
|
//we skip this since we will get this player anyway when we request level
|
|
return;
|
|
}
|
|
if (!packet->type) {
|
|
LOGE("Trying to add a mob without a type id\n");
|
|
return;
|
|
}
|
|
|
|
Mob* mob = MobFactory::CreateMob(packet->type, level);
|
|
if (!mob) {
|
|
LOGE("Server tried to add an unknown mob type! :%d\n", packet->type);
|
|
return;
|
|
}
|
|
|
|
mob->entityId = packet->entityId;
|
|
mob->moveTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot);
|
|
mob->getEntityData()->assignValues(&packet->unpack);
|
|
level->addEntity(mob);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddPlayerPacket* packet)
|
|
{
|
|
if (!level) {
|
|
//we skip this since we will get this player anyway when we request level
|
|
return;
|
|
}
|
|
LOGI("AddPlayerPacket\n");
|
|
|
|
Player* player = new RemotePlayer(level, minecraft->isCreativeMode());
|
|
minecraft->gameMode->initAbilities(player->abilities);
|
|
player->entityId = packet->entityId;
|
|
level->addEntity(player);
|
|
|
|
player->moveTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot);
|
|
player->name = packet->name.C_String();
|
|
player->owner = packet->owner;
|
|
player->getEntityData()->assignValues(&packet->unpack);
|
|
int slot = Inventory::MAX_SELECTION_SIZE;
|
|
if (packet->carriedItemId == 0) {
|
|
player->inventory->clearSlot(slot);
|
|
} else {
|
|
ItemInstance newItem(packet->carriedItemId, 1, packet->carriedItemAuxValue);
|
|
player->inventory->replaceSlot(slot, &newItem);
|
|
}
|
|
player->inventory->moveToSelectedSlot(slot, true);
|
|
//player->resetPos();
|
|
|
|
std::string message = packet->name.C_String();
|
|
message += " joined the game";
|
|
#ifndef STANDALONE_SERVER
|
|
minecraft->gui.addMessage(message);
|
|
#endif
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemovePlayerPacket* packet) {
|
|
if (!level || source == minecraft->player->owner) return;
|
|
|
|
if (Player* player = findPlayer(level, packet->entityId, &packet->owner)) {
|
|
player->reallyRemoveIfPlayer = true;
|
|
level->removeEntity(player);
|
|
}
|
|
|
|
//for (int i = (int)level->players.size()-1; i >= 0; --i) {
|
|
// if (level->players[i]->owner == source) {
|
|
// level->players[i]->reallyRemoveIfPlayer = true;
|
|
// level->players.erase(level->players.begin() + i);
|
|
// break;
|
|
// }
|
|
//}
|
|
|
|
//if (!player) return;
|
|
|
|
//std::string message = packet->name.C_String();
|
|
//message += " joined the game";
|
|
//minecraft->gui.addMessage(message);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RemoveEntityPacket* packet)
|
|
{
|
|
if (!level) return;
|
|
|
|
Entity* entity = level->getEntity(packet->entityId);
|
|
LOGI("RemoveEntityPacket %p %p, %d\n", entity, minecraft->player, entity?(int)(entity->isPlayer()): -1);
|
|
if (!entity) return;
|
|
|
|
level->removeEntity(entity);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AddItemEntityPacket* packet)
|
|
{
|
|
if (!level) return;
|
|
|
|
ItemEntity* entity = new ItemEntity(level, packet->x, packet->y, packet->z, ItemInstance(packet->itemId, packet->itemCount, packet->auxValue));
|
|
entity->xd = packet->xa();
|
|
entity->yd = packet->ya();
|
|
entity->zd = packet->za();
|
|
//LOGI("item-entity @ client: %f, %f, %f\n", entity->xd, entity->yd, entity->zd);
|
|
|
|
mpcast(level)->putEntity(packet->id, entity);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, TakeItemEntityPacket* packet) {
|
|
if (!level) return;
|
|
|
|
Entity* e = level->getEntity(packet->itemId);
|
|
if (!e)
|
|
return;
|
|
|
|
ItemInstance item;
|
|
if (e->isItemEntity()) {
|
|
item = ((ItemEntity*) e)->item;
|
|
#ifndef STANDALONE_SERVER
|
|
if (Entity* to = level->getEntity(packet->playerId))
|
|
minecraft->particleEngine->add(new TakeAnimationParticle(level, (ItemEntity*)e, to, -0.5f));
|
|
#endif
|
|
}
|
|
else if (e->getEntityTypeId() == EntityTypes::IdArrow)
|
|
item = ItemInstance(Item::arrow);
|
|
|
|
if (item.isNull())
|
|
return;
|
|
|
|
// try take it and if we don't have space; re-throw it
|
|
if (minecraft->player->entityId == packet->playerId
|
|
&& !minecraft->player->inventory->add(&item)) {
|
|
DropItemPacket dropPacket(packet->playerId, item);
|
|
minecraft->raknetInstance->send(dropPacket);
|
|
}
|
|
level->playSound(e, "random.pop", 0.2f, 1.0f * 2.f);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MovePlayerPacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
//printf("MovePlayerPacket\n");
|
|
Entity* entity = level->getEntity(packet->entityId);
|
|
if (entity)
|
|
{
|
|
entity->lerpTo(packet->x, packet->y, packet->z, packet->yRot, packet->xRot, 3);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, MoveEntityPacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
//printf("MovePlayerPacket\n");
|
|
Entity* entity = level->getEntity(packet->entityId);
|
|
if (entity)
|
|
{
|
|
float xRot = packet->hasRot? packet->xRot : entity->xRot;
|
|
float yRot = packet->hasRot? packet->yRot : entity->yRot;
|
|
entity->lerpTo(packet->x, packet->y, packet->z, yRot, xRot, 3);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UpdateBlockPacket* packet)
|
|
{
|
|
if (!level) return;
|
|
|
|
//LOGI("UpdateBlockPacket @ %d, %d, %d\n", packet->x, packet->y, packet->z);
|
|
|
|
int x = packet->x, z = packet->z;
|
|
|
|
if (isChunkLoaded(x >> 4, z >> 4))
|
|
{
|
|
//LOGI("chunk is loaded - UPDATE @ %d, %d, %d -- %d, %d\n", x, packet->y, z, packet->blockId, packet->blockData);
|
|
|
|
int y = packet->y;
|
|
int tileId = Tile::transformToValidBlockId(packet->blockId, x, y, z);
|
|
level->setTileAndData(x, y, z, tileId, packet->blockData);
|
|
} else {
|
|
SBufferedBlockUpdate update;
|
|
update.blockId = packet->blockId;
|
|
update.blockData = packet->blockData;
|
|
update.setData = true;
|
|
update.x = packet->x;
|
|
update.y = packet->y;
|
|
update.z = packet->z;
|
|
bufferedBlockUpdates.push_back(update);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ExplodePacket* packet) {
|
|
if (!level) return;
|
|
Explosion explosion(level, NULL, packet->x, packet->y, packet->z, packet->r);
|
|
explosion.toBlow.insert(packet->toBlow.begin(), packet->toBlow.end());
|
|
explosion.finalizeExplosion();
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, LevelEventPacket* packet) {
|
|
if (!level) return;
|
|
if(packet->eventId == LevelEvent::ALL_PLAYERS_SLEEPING) {
|
|
minecraft->player->setAllPlayersSleeping();
|
|
}
|
|
else {
|
|
minecraft->level->levelEvent(NULL, packet->eventId, packet->x, packet->y, packet->z, packet->data);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, TileEventPacket* packet) {
|
|
if (!level) return;
|
|
minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->b0, packet->b1);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, EntityEventPacket* packet) {
|
|
if (!level) return;
|
|
|
|
Entity* e = level->getEntity(packet->entityId);
|
|
if (e) e->handleEntityEvent(packet->eventId);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChunkDataPacket* packet)
|
|
{
|
|
if (!level) {
|
|
LOGI("level @ handle ChunkDataPacket is 0\n");
|
|
return;
|
|
}
|
|
//LOGI("ChunkDataPacket\n");
|
|
|
|
LevelChunk* chunk = level->getChunkSource()->create(packet->x, packet->z);
|
|
if (!chunk || chunk->isEmpty())
|
|
{
|
|
LOGI("Failed to find write-able chunk\n");
|
|
return;
|
|
}
|
|
|
|
//unsigned char* blockIds = chunk->getBlockData();
|
|
DataLayer& blockData = chunk->data;
|
|
|
|
const int setSize = LEVEL_HEIGHT / 8;
|
|
const int setShift = 4; // power of LEVEL_HEIGHT / 8
|
|
|
|
bool recalcHeight = false;
|
|
|
|
int x0 = 16, x1 = 0, z0 = 16, z1 = 0, y0 = LEVEL_HEIGHT, y1 = 0;
|
|
int rx = packet->x << 4;
|
|
int rz = packet->z << 4;
|
|
|
|
unsigned char readBlockBuffer[setSize];
|
|
unsigned char readDataBuffer[setSize / 2];
|
|
|
|
for (int i = 0; i < CHUNK_COLUMNS; i++)
|
|
{
|
|
unsigned char updateBits = 0;
|
|
packet->chunkData.Read(updateBits);
|
|
|
|
if (updateBits > 0)
|
|
{
|
|
recalcHeight = true;
|
|
|
|
int colX = (i % CHUNK_WIDTH);
|
|
int colZ = (i / CHUNK_WIDTH);
|
|
int colDataPosition = colX << 11 | colZ << 7;
|
|
|
|
for (int set = 0; set < 8; set++)
|
|
{
|
|
if ((updateBits & (1 << set)) != 0)
|
|
{
|
|
packet->chunkData.Read((char*)readBlockBuffer, setSize);
|
|
packet->chunkData.Read((char*)readDataBuffer, setSize / 2);
|
|
|
|
for (int part = 0; part < setSize; part++)
|
|
{
|
|
//if (readBlockBuffer[part] == ((Tile*)Tile::grass)->id)
|
|
// readBlockBuffer[part] = 255;
|
|
int x = rx + colX;
|
|
int y = (set << setShift) + part;
|
|
int z = rz + colZ;
|
|
|
|
int tileId = Tile::transformToValidBlockId(readBlockBuffer[part], x, y, z);
|
|
level->setTileNoUpdate(x, y, z, tileId);
|
|
}
|
|
// ((part & 1) == 0) ? readDataBuffer[part >> 1] & 0xf : (readDataBuffer[part >> 1] & 0xf0) >> 4
|
|
|
|
//packet->chunkData.Read((char*)(&blockIds[colDataPosition + (set << setShift)]), setSize);
|
|
// block data is only 4 bits per block
|
|
//packet->chunkData.Read((char*)(&blockData.data[(colDataPosition + (set << setShift)) >> 1]), setSize >> 1);
|
|
|
|
memcpy(&blockData.data[(colDataPosition + (set << setShift)) >> 1], readDataBuffer, setSize >> 1);
|
|
}
|
|
|
|
if (((1 << set) << setShift) < y0)
|
|
{
|
|
y0 = ((1 << set) << setShift);
|
|
}
|
|
if (((1 << set) << setShift) + (LEVEL_HEIGHT / 8) - 1 > y1)
|
|
{
|
|
y1 = ((1 << set) << setShift) + (LEVEL_HEIGHT / 8) - 1;
|
|
}
|
|
}
|
|
if ((i % CHUNK_WIDTH) < x0)
|
|
{
|
|
x0 = (i % CHUNK_WIDTH);
|
|
}
|
|
if ((i % CHUNK_WIDTH) > x1)
|
|
{
|
|
x1 = (i % CHUNK_WIDTH);
|
|
}
|
|
if ((i / CHUNK_WIDTH) < z0)
|
|
{
|
|
z0 = (i / CHUNK_WIDTH);
|
|
}
|
|
if ((i / CHUNK_WIDTH) > z1)
|
|
{
|
|
z1 = (i / CHUNK_WIDTH);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (recalcHeight)
|
|
{
|
|
// chunk->recalcHeightmap();
|
|
// //chunk->recalcBlockLights();
|
|
level->setTilesDirty((packet->x << 4) + x0, y0, (packet->z << 4) + z0, (packet->x << 4) + x1, y1, (packet->z << 4) + z1);
|
|
// int rx = packet->x << 4;
|
|
// int rz = packet->z << 4;
|
|
// level->updateLight(LightLayer::Block, x0 + rx - 1, y0, z0 + rz - 1, x1 + rx + 1, y1, z1 + rz + 1);
|
|
// //for (int cx = x0; cx < x1; cx++)
|
|
// //{
|
|
// // for (int cz = z0; cz < z1; cz++)
|
|
// // {
|
|
// // for (int cy = y0; cy < y1; cy++)
|
|
// // {
|
|
// //level->updateLight(LightLayer::Sky, cx + rx, cy, cz + rz, cx + rx, cy, cz + rz);
|
|
// // level->updateLight(LightLayer::Block, cx + rx - 1, cy, cz + rz - 1, cx + rx + 1, cy, cz + rz + 1);
|
|
// // }
|
|
// // }
|
|
// //}
|
|
//
|
|
}
|
|
//chunk->terrainPopulated = true;
|
|
chunk->unsaved = false;
|
|
|
|
chunksLoaded[packet->x * CHUNK_CACHE_WIDTH + packet->z] = true;
|
|
|
|
if (areAllChunksLoaded())
|
|
{
|
|
ReadyPacket packet(ReadyPacket::READY_REQUESTEDCHUNKS);
|
|
raknetInstance->send(packet);
|
|
|
|
for (unsigned int i = 0; i < bufferedBlockUpdates.size(); i++)
|
|
{
|
|
const SBufferedBlockUpdate& update = bufferedBlockUpdates[i];
|
|
int tileId = Tile::transformToValidBlockId( update.blockId, update.x, update.y, update.z );
|
|
if (update.setData)
|
|
level->setTileAndData(update.x, update.y, update.z, tileId, update.blockData);
|
|
else
|
|
level->setTile(update.x, update.y, update.z, tileId);
|
|
}
|
|
bufferedBlockUpdates.clear();
|
|
}
|
|
else
|
|
{
|
|
requestNextChunk();
|
|
}
|
|
}
|
|
|
|
class _ChunkSorter
|
|
{
|
|
public:
|
|
_ChunkSorter(int x, int z)
|
|
: x(x),
|
|
y(z)
|
|
{
|
|
}
|
|
|
|
bool operator() (const IntPair& a, const IntPair& b) {
|
|
const int ax = a.x - x, ay = a.y - y;
|
|
const int bx = b.x - x, by = b.y - y;
|
|
return (ax*ax + ay*ay) < (bx*bx + by*by);
|
|
}
|
|
private:
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
|
|
void ClientSideNetworkHandler::arrangeRequestChunkOrder() {
|
|
clearChunksLoaded();
|
|
|
|
// Default sort is around center of the world
|
|
int cx = CHUNK_CACHE_WIDTH / 2;
|
|
int cz = CHUNK_CACHE_WIDTH / 2;
|
|
|
|
// If player exists, let's sort around him
|
|
Player* p = minecraft? minecraft->player : NULL;
|
|
if (p) {
|
|
cx = Mth::floor(p->x / (float)CHUNK_WIDTH);
|
|
cz = Mth::floor(p->z / (float)CHUNK_DEPTH);
|
|
}
|
|
|
|
_ChunkSorter sorter(cx, cz);
|
|
std::sort(requestNextChunkIndexList, requestNextChunkIndexList + NumRequestChunks, sorter);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::levelGenerated(Level* level)
|
|
{
|
|
this->level = level;
|
|
ReadyPacket packet(ReadyPacket::READY_CLIENTGENERATION);
|
|
raknetInstance->send(packet);
|
|
|
|
arrangeRequestChunkOrder();
|
|
requestNextChunk();
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerEquipmentPacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
Entity* entity = level->getEntity(packet->entityId);
|
|
if (!entity || !entity->isPlayer())
|
|
return;
|
|
|
|
Player* player = (Player*)entity;
|
|
// make sure it's not our local player
|
|
if (player->owner == rakPeer->GetMyGUID())
|
|
{
|
|
printf("Attempted to modify local player's inventory\n");
|
|
return;
|
|
}
|
|
// override the player's inventory
|
|
//int slot = player->inventory->getSlot(packet->itemId, packet->itemAuxValue);
|
|
//if (slot >= 0) {
|
|
// player->inventory->moveToSelectedSlot(slot, true);
|
|
//item->id = packet->itemId;
|
|
//item->setAuxValue(packet->itemAuxValue);
|
|
//item->count = 63;
|
|
int slot = Inventory::MAX_SELECTION_SIZE;
|
|
if (slot >= 0) {
|
|
if (packet->itemId == 0) {
|
|
player->inventory->clearSlot(slot);
|
|
} else {
|
|
ItemInstance newItem(packet->itemId, 63, packet->itemAuxValue);
|
|
player->inventory->replaceSlot(slot, &newItem);
|
|
}
|
|
player->inventory->moveToSelectedSlot(slot, true);
|
|
} else {
|
|
LOGW("Warning: Remote player doesn't have his thing, Odd!\n");
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, PlayerArmorEquipmentPacket* packet) {
|
|
if (!level)
|
|
return;
|
|
|
|
Entity* entity = level->getEntity(packet->entityId);
|
|
if (!entity || !entity->isPlayer())
|
|
return;
|
|
|
|
Player* player = (Player*)entity;
|
|
// make sure it's not our local player, since that should be UpdateArmorPacket
|
|
if (player->owner == rakPeer->GetMyGUID()) {
|
|
printf("Attempted to modify local player's armor visually\n");
|
|
return;
|
|
}
|
|
|
|
packet->fillIn(player);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, InteractPacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
Entity* src = level->getEntity(packet->sourceId);
|
|
Entity* entity = level->getEntity(packet->targetId);
|
|
if (src && entity && src->isPlayer())
|
|
{
|
|
Player* player = (Player*) src;
|
|
if (InteractPacket::Attack == packet->action)
|
|
minecraft->gameMode->attack(player, entity);
|
|
if (InteractPacket::Interact == packet->action)
|
|
minecraft->gameMode->interact(player, entity);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetEntityDataPacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
LOGI("SetEntityDataPacket\n");
|
|
|
|
Entity* e = level->getEntity(packet->id);
|
|
if (e) {
|
|
SynchedEntityData* data = e->getEntityData();
|
|
if (data)
|
|
data->assignValues(&packet->getUnpackedData());
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, SetEntityMotionPacket* packet )
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
if (Entity* e = level->getEntity(packet->id)) {
|
|
/*
|
|
e->xd = packet->xd;
|
|
e->yd = packet->yd;
|
|
e->zd = packet->zd;
|
|
*/
|
|
e->lerpMotion(packet->xd, packet->yd, packet->zd);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AnimatePacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
// Own player - Then don't play... :
|
|
if (minecraft->player->entityId == packet->entityId) {
|
|
if (packet->action == AnimatePacket::Swing) return;
|
|
}
|
|
|
|
Entity* entity = level->getEntity(packet->entityId);
|
|
if (!entity || !entity->isPlayer())
|
|
return;
|
|
|
|
Player* player = (Player*) entity;
|
|
|
|
switch (packet->action) {
|
|
case AnimatePacket::Swing:
|
|
player->swing();
|
|
break;
|
|
case AnimatePacket::WAKE_UP:
|
|
player->stopSleepInBed(false, false, false);
|
|
break;
|
|
default:
|
|
LOGW("Unknown Animate action: %d\n", packet->action);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, UseItemPacket* packet)
|
|
{
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetHealthPacket* packet)
|
|
{
|
|
if (!level || !minecraft->player)
|
|
return;
|
|
|
|
minecraft->player->hurtTo(packet->health);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SetSpawnPositionPacket* packet) {
|
|
if (!level || !minecraft || !minecraft->player) return;
|
|
if (!level->inRange(packet->x, packet->y, packet->z)) return;
|
|
|
|
minecraft->player->setRespawnPosition(Pos(packet->x, packet->y, packet->z));
|
|
level->getLevelData()->setSpawn(packet->x, packet->y, packet->z);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, HurtArmorPacket* packet) {
|
|
if (!level || !minecraft->player) {
|
|
return;
|
|
}
|
|
|
|
minecraft->player->hurtArmor(packet->dmg);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, RespawnPacket* packet)
|
|
{
|
|
if (level) {
|
|
//LOGI("RespawnPacket! %d\n", findPlayer(level, packet->entityId, NULL));
|
|
NetEventCallback::handle(level, source, packet );
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerOpenPacket* packet)
|
|
{
|
|
if (!level || !minecraft || !minecraft->player)
|
|
return;
|
|
|
|
if (packet->type == ContainerType::FURNACE) {
|
|
FurnaceTileEntity* te = new FurnaceTileEntity();
|
|
te->clientSideOnly = true;
|
|
minecraft->player->openFurnace(te);
|
|
if (minecraft->player->containerMenu)
|
|
minecraft->player->containerMenu->containerId = packet->containerId;
|
|
}
|
|
if (packet->type == ContainerType::CONTAINER) {
|
|
ChestTileEntity* te = new ChestTileEntity();
|
|
te->clientSideOnly = true;
|
|
minecraft->player->openContainer(te);
|
|
if (minecraft->player->containerMenu)
|
|
minecraft->player->containerMenu->containerId = packet->containerId;
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet)
|
|
{
|
|
if (minecraft && minecraft->player && minecraft->player->containerMenu)
|
|
minecraft->player->closeContainer();
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetContentPacket* packet)
|
|
{
|
|
if (!minecraft || !minecraft->player)
|
|
return;
|
|
|
|
if (packet->containerId == 0) {
|
|
for (unsigned int i = 0; i < packet->items.size(); ++i) {
|
|
minecraft->player->inventory->setItem(Inventory::MAX_SELECTION_SIZE + i, &packet->items[i]);
|
|
}
|
|
} else if (minecraft->player->containerMenu && minecraft->player->containerMenu->containerId == packet->containerId) {
|
|
for (unsigned int i = 0; i < packet->items.size(); ++i) {
|
|
minecraft->player->containerMenu->setSlot(i, &packet->items[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet)
|
|
{
|
|
//LOGI("ContainerSetSlot\n");
|
|
|
|
if (!minecraft->player
|
|
|| !minecraft->player->containerMenu
|
|
|| minecraft->player->containerMenu->containerId != packet->containerId)
|
|
return;
|
|
|
|
//minecraft->player->containerMenu->setSlot(packet->slot, packet->item.isNull()? NULL : &packet->item);
|
|
minecraft->player->containerMenu->setSlot(packet->slot, &packet->item);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ContainerSetDataPacket* packet)
|
|
{
|
|
//LOGI("ContainerSetData\n");
|
|
if (minecraft->player && minecraft->player->containerMenu && minecraft->player->containerMenu->containerId == packet->containerId) {
|
|
//LOGI("client: SetData2 %d, %d\n", packet->id, packet->value);
|
|
minecraft->player->containerMenu->setData(packet->id, packet->value);
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle( const RakNet::RakNetGUID& source, ChatPacket* packet )
|
|
{
|
|
#ifndef STANDALONE_SERVER
|
|
minecraft->gui.displayClientMessage(packet->message);
|
|
#endif
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet)
|
|
{
|
|
if (!level)
|
|
return;
|
|
|
|
TileEntity* te = level->getTileEntity(packet->x, packet->y, packet->z);
|
|
if (TileEntity::isType(te, TileEntityType::Sign)) {
|
|
SignTileEntity* ste = (SignTileEntity*) te;
|
|
if (ste->isEditable()) {
|
|
for (int i = 0; i < SignTileEntity::NUM_LINES; i++) {
|
|
ste->messages[i] = packet->lines[i];
|
|
}
|
|
//ste->setChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientSideNetworkHandler::handle(const RakNet::RakNetGUID& source, AdventureSettingsPacket* packet) {
|
|
if (!level)
|
|
return;
|
|
//assert(level != NULL && "level is NULL @ handle(AdventureSettingsPacket*)");
|
|
|
|
packet->fillIn(level->adventureSettings);
|
|
}
|
|
|
|
void ClientSideNetworkHandler::clearChunksLoaded()
|
|
{
|
|
// Init the chunk positions
|
|
for (int i = 0; i < NumRequestChunks; ++i) {
|
|
requestNextChunkIndexList[i].x = i/CHUNK_WIDTH;
|
|
requestNextChunkIndexList[i].y = i%CHUNK_WIDTH;
|
|
chunksLoaded[i] = false;
|
|
}
|
|
}
|