From f261a03c141399fdf2de958a7431dc48d98dd04e Mon Sep 17 00:00:00 2001 From: peterbell10 Date: Sun, 28 May 2017 19:07:38 +0100 Subject: Double chest window fix (#3735) --- src/BlockEntities/ChestEntity.cpp | 165 +++++++++++++++++++++++++++----------- src/BlockEntities/ChestEntity.h | 17 +++- 2 files changed, 130 insertions(+), 52 deletions(-) diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index c3fb44a1b..3fcf1b386 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -13,21 +13,33 @@ cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type) : super(a_Type, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), - m_NumActivePlayers(0) + m_NumActivePlayers(0), + m_Neighbour(nullptr) { + int ChunkX = 0, ChunkZ = 0; + cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); + if ( + (m_World != nullptr) && + m_World->IsChunkValid(ChunkX, ChunkZ) + ) + { + ScanNeighbours(); + } } - cChestEntity::~cChestEntity() { - cWindow * Window = GetWindow(); - if (Window != nullptr) + if (m_Neighbour != nullptr) { - Window->OwnerDestroyed(); + // Neighbour may share a window with us, force the window shut + m_Neighbour->DestroyWindow(); + m_Neighbour->m_Neighbour = nullptr; } + + DestroyWindow(); } @@ -46,12 +58,42 @@ void cChestEntity::SendTo(cClientHandle & a_Client) bool cChestEntity::UsedBy(cPlayer * a_Player) { + if (IsBlocked()) + { + // Obstruction, don't open + return true; + } + + if (m_Neighbour == nullptr) + { + ScanNeighbours(); + } + + // The primary chest should be the one with lesser X or Z coord: + cChestEntity * PrimaryChest = this; + if (m_Neighbour != nullptr) + { + if (m_Neighbour->IsBlocked()) + { + // Obstruction, don't open + return true; + } + + if ( + (m_Neighbour->GetPosX() > GetPosX()) || + (m_Neighbour->GetPosZ() > GetPosZ()) + ) + { + PrimaryChest = m_Neighbour; + } + } + // If the window is not created, open it anew: - cWindow * Window = GetWindow(); + cWindow * Window = PrimaryChest->GetWindow(); if (Window == nullptr) { - OpenNewWindow(); - Window = GetWindow(); + PrimaryChest->OpenNewWindow(); + Window = PrimaryChest->GetWindow(); } // Open the window for the player: @@ -77,62 +119,87 @@ bool cChestEntity::UsedBy(cPlayer * a_Player) -void cChestEntity::OpenNewWindow(void) +void cChestEntity::ScanNeighbours() { - // TODO: cats are an obstruction - if ((GetPosY() < cChunkDef::Height - 1) && !cBlockInfo::IsTransparent(GetWorld()->GetBlock(GetPosX(), GetPosY() + 1, GetPosZ()))) - { - // Obstruction, don't open - return; - } - - // Callback for opening together with neighbor chest: - class cOpenDouble : + // Callback for finding neighbouring chest: + class cFindNeighbour : public cChestCallback { - cChestEntity * m_ThisChest; public: - cOpenDouble(cChestEntity * a_ThisChest) : - m_ThisChest(a_ThisChest) + cChestEntity * m_Neighbour; + + cFindNeighbour() : + m_Neighbour(nullptr) { } virtual bool Item(cChestEntity * a_Chest) override { - if ((a_Chest->GetPosY() < cChunkDef::Height - 1) && !cBlockInfo::IsTransparent(a_Chest->GetWorld()->GetBlock(a_Chest->GetPosX(), a_Chest->GetPosY() + 1, a_Chest->GetPosZ()))) - { - // Obstruction, don't open - return false; - } - - // The primary chest should eb the one with lesser X or Z coord: - cChestEntity * Primary = a_Chest; - cChestEntity * Secondary = m_ThisChest; - if ( - (Primary->GetPosX() > Secondary->GetPosX()) || - (Primary->GetPosZ() > Secondary->GetPosZ()) - ) - { - std::swap(Primary, Secondary); - } - m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary)); + m_Neighbour = a_Chest; return false; } - } ; + }; - // Scan neighbors for adjacent chests: - cOpenDouble OpenDbl(this); + // Scan horizontally adjacent blocks for any neighbouring chest: + cFindNeighbour FindNeighbour; if ( - m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) || - m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) || - m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ - 1, OpenDbl) || - m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ + 1, OpenDbl) + m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, FindNeighbour) || + m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, FindNeighbour) || + m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ - 1, FindNeighbour) || + m_World->DoWithChestAt(m_PosX, m_PosY, m_PosZ + 1, FindNeighbour) ) { - // The double-chest window has been opened in the callback - return; + m_Neighbour = FindNeighbour.m_Neighbour; + m_Neighbour->m_Neighbour = this; + // Force neighbour's window shut. Does Mojang server do this or should a double window open? + m_Neighbour->DestroyWindow(); + } +} + + + + + +void cChestEntity::OpenNewWindow(void) +{ + if (m_Neighbour != nullptr) + { + ASSERT( // This should be the primary chest + (m_Neighbour->GetPosX() < GetPosX()) || + (m_Neighbour->GetPosZ() < GetPosZ()) + ); + OpenWindow(new cChestWindow(this, m_Neighbour)); + } + else + { + // There is no chest neighbour, open a single-chest window: + OpenWindow(new cChestWindow(this)); + } +} + + + + + +void cChestEntity::DestroyWindow() +{ + cWindow * Window = GetWindow(); + if (Window != nullptr) + { + Window->OwnerDestroyed(); + CloseWindow(); } +} + + - // There is no chest neighbor, open a single-chest window: - OpenWindow(new cChestWindow(this)); + + +bool cChestEntity::IsBlocked() +{ + // TODO: cats are an obstruction + return ( + (GetPosY() >= cChunkDef::Height - 1) || + !cBlockInfo::IsTransparent(GetWorld()->GetBlock(GetPosX(), GetPosY() + 1, GetPosZ())) + ); } diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index 9a10cd9a5..dfd5ca0d0 100644 --- a/src/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h @@ -39,9 +39,17 @@ public: virtual void SendTo(cClientHandle & a_Client) override; virtual bool UsedBy(cPlayer * a_Player) override; - /** Opens a new chest window for this chest. - Scans for neighbors to open a double chest window, if appropriate. */ - void OpenNewWindow(void); + /** Search horizontally adjacent blocks for neighbouring chests and links them together. */ + void ScanNeighbours(); + + /** Opens a new chest window where this is the primary chest and any neighbour is the secondary. */ + void OpenNewWindow(); + + /** Forces any players to close the owned window. */ + void DestroyWindow(); + + /** Returns true if the chest should not be accessible by players. */ + bool IsBlocked(); /** Gets the number of players who currently have this chest open */ int GetNumberOfPlayers(void) const { return m_NumActivePlayers; } @@ -54,6 +62,9 @@ private: /** Number of players who currently have this chest open */ int m_NumActivePlayers; + /** Neighbouring chest that links to form a double chest */ + cChestEntity * m_Neighbour; + /** cItemGrid::cListener overrides: */ virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override { -- cgit v1.2.3