From 3a860d442fe2a02aee14a47e06d09fbeff1cd3f0 Mon Sep 17 00:00:00 2001 From: Fireblade <3+fireblade@noreply.neolegacy.dev> Date: Sun, 12 Jul 2026 23:17:19 -0400 Subject: [PATCH 1/3] feat: customizable panorama additions - customizable scroll speed + blur intensity via panorama.xml file - grid feature that brings back the look of the classic minecraft panorama uhhh yeah thats pretty much it lmao --- .../Common/UI/UIComponent_Panorama.cpp | 267 ++++++++++++++++-- .../Common/UI/UIComponent_Panorama.h | 15 + 2 files changed, 255 insertions(+), 27 deletions(-) diff --git a/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp b/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp index 4dffa43d..8b0f8d09 100644 --- a/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp +++ b/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp @@ -13,8 +13,149 @@ #include #include +#include "../../../Xbox/XML/ATGXmlParser.h" + static const wchar_t *PANORAMA_TEXTURE_RELPATH = L"Graphics/ControlType/Panorama/"; +// default settings +static const float kDefaultPanoramaScrollSpeed = 0.0001f / 16.6667f; +static const float kDefaultPanoramaBlurSigma = 0.5f; + +struct PanoramaConfig +{ + float scrollSpeed = kDefaultPanoramaScrollSpeed; + float blurSigma = kDefaultPanoramaBlurSigma; + + // grid mode (classic panorama) + bool gridEnabled = false; + float gridSizeX = 1.0f; + float gridSizeY = 1.0f; + + // nearest vs bilinear + bool gridScalingNearest = false; +}; + +// atg xml parser my beloved +class PanoramaXmlCallback : public ATG::ISAXCallback +{ +public: + PanoramaConfig config; + + HRESULT StartDocument() override { return S_OK; } + HRESULT EndDocument() override { return S_OK; } + + HRESULT ElementBegin(CONST WCHAR *strName, UINT NameLen, CONST ATG::XMLAttribute * /*pAttributes*/, UINT /*NumAttributes*/) override + { + wstring elementName(strName, NameLen); + if(elementName == L"grid") + { + // grid switch + config.gridEnabled = true; + } + m_elementStack.push_back(elementName); + m_currentText.clear(); + return S_OK; + } + + HRESULT ElementContent(CONST WCHAR *strData, UINT DataLen, BOOL More) override + { + m_currentText.append(strData, DataLen); + if(!More) + { + ApplyCurrentElement(); + m_currentText.clear(); + } + return S_OK; + } + + HRESULT ElementEnd(CONST WCHAR * /*strName*/, UINT /*NameLen*/) override + { + ApplyCurrentElement(); + m_currentText.clear(); + if(!m_elementStack.empty()) + { + m_elementStack.pop_back(); + } + return S_OK; + } + + HRESULT CDATABegin() override { return S_OK; } + HRESULT CDATAData(CONST WCHAR * /*strCDATA*/, UINT /*CDATALen*/, BOOL /*bMore*/) override { return S_OK; } + HRESULT CDATAEnd() override { return S_OK; } + + VOID Error(HRESULT /*hError*/, CONST CHAR * /*strMessage*/) override { } + +private: + void ApplyCurrentElement() + { + if(m_currentText.empty() || m_elementStack.empty()) return; + + const wstring &name = m_elementStack.back(); + const bool insideGrid = (m_elementStack.size() >= 2 && + m_elementStack[m_elementStack.size() - 2] == L"grid"); + + if(name == L"panoramaSpeed") + { + config.scrollSpeed = static_cast(wcstod(m_currentText.c_str(), nullptr)); + } + else if(name == L"panoramaBlur") + { + config.blurSigma = static_cast(wcstod(m_currentText.c_str(), nullptr)); + } + else if(insideGrid && name == L"sizeX") + { + config.gridSizeX = static_cast(wcstod(m_currentText.c_str(), nullptr)); + } + else if(insideGrid && name == L"sizeY") + { + config.gridSizeY = static_cast(wcstod(m_currentText.c_str(), nullptr)); + } + else if(insideGrid && name == L"scaling") + { + config.gridScalingNearest = (_wcsicmp(m_currentText.c_str(), L"nearest") == 0); + } + } + + vector m_elementStack; + wstring m_currentText; +}; + +// load custom panorama data (if any) and return its settings +static PanoramaConfig LoadPanoramaConfig(const wstring &root) +{ + PanoramaConfig config; + + wstring xmlPath = root + L"panorama.xml"; + if(GetFileAttributesW(xmlPath.c_str()) == INVALID_FILE_ATTRIBUTES) + { + return config; + } + + const char *nativePath = wstringtofilename(xmlPath); + + PanoramaXmlCallback callback; + ATG::XMLParser parser; + parser.RegisterSAXCallbackInterface(&callback); + + HRESULT hr = parser.ParseXMLFile(nativePath); + if(FAILED(hr)) + { + printf("Failed to parse panorama.xml at %ls (hr=0x%08X)\n", xmlPath.c_str(), hr); + return PanoramaConfig(); + } + + if(callback.config.gridSizeX <= 0.0f) callback.config.gridSizeX = 1.0f; + if(callback.config.gridSizeY <= 0.0f) callback.config.gridSizeY = 1.0f; + + printf("Loaded panorama.xml overrides from %ls (speed=%f, blur=%f, grid=%s %fx%f, scaling=%s)\n", + xmlPath.c_str(), callback.config.scrollSpeed, callback.config.blurSigma, + callback.config.gridEnabled ? "on" : "off", + callback.config.gridSizeX, callback.config.gridSizeY, + callback.config.gridScalingNearest ? "nearest" : "linear"); + + return callback.config; +} + static wstring GetPanoramaTexturePath() { const wchar_t *envOverride = _wgetenv(L"PANORAMA_TEXTURE_PATH"); @@ -89,7 +230,7 @@ static wstring GetPanoramaTexturePath() } // opengl time :v -static int LoadPanoramaQuadTexture(const wstring &fileName, int &outWidth, int &outHeight) +static int LoadPanoramaQuadTexture(const wstring &fileName, float blurSigma, int &outWidth, int &outHeight) { wstring filePath = GetPanoramaTexturePath() + fileName; const char *nativePath = wstringtofilename(filePath); @@ -127,10 +268,20 @@ static int LoadPanoramaQuadTexture(const wstring &fileName, int &outWidth, int & } // gaussian blur - const float sigma = 0.5f; + const float sigma = blurSigma; const int width = image.getWidth(); const int height = image.getHeight(); + if(sigma <= 0.0f) + { + outWidth = width; + outHeight = height; + + int idNoBlur = Minecraft::GetInstance()->textures->getTexture(&image, C4JRender::TEXTURE_FORMAT_RxGyBzAw, false); + printf("Loaded panorama background quad '%ls' (no blur) -> texture id %d\n", filePath.c_str(), idNoBlur); + return idNoBlur; + } + const int radius = static_cast(ceilf(3.0f * sigma)); const int ksize = radius * 2 + 1; vector kernel(ksize); @@ -217,10 +368,63 @@ static int LoadPanoramaQuadTexture(const wstring &fileName, int &outWidth, int & outHeight = image.getHeight(); int id = Minecraft::GetInstance()->textures->getTexture(&blurredImage, C4JRender::TEXTURE_FORMAT_RxGyBzAw, false); - printf("Loaded panorama background quad '%ls' -> texture id %d\n", filePath.c_str(), id); + printf("Loaded panorama background quad '%ls' (blur sigma=%f) -> texture id %d\n", filePath.c_str(), sigma, id); return id; } +// original behavior +static void DrawScrollingPanoramaQuads(S32 width, S32 height, float panoramaAspect, float panoramaScroll, Tesselator *t) +{ + float sourceWidth = static_cast(height) * panoramaAspect; + float scroll = panoramaScroll - floorf(panoramaScroll); + float scrollPx = scroll * sourceWidth; + float startX = -scrollPx; + const float overscanY = (static_cast(height) * 0.03f > 8.0f) ? (static_cast(height) * 0.03f) : 8.0f; + const float drawTop = -overscanY; + const float drawBottom = static_cast(height) + overscanY; + + t->begin(); + for(float x = startX - sourceWidth; x < static_cast(width) + sourceWidth; x += sourceWidth) + { + float left = x; + float right = x + sourceWidth; + t->vertexUV(left, drawBottom, 0.0f, 0.0f, 1.0f); + t->vertexUV(right, drawBottom, 0.0f, 1.0f, 1.0f); + t->vertexUV(right, drawTop, 0.0f, 1.0f, 0.0f); + t->vertexUV(left, drawTop, 0.0f, 0.0f, 0.0f); + } + t->end(); +} + +// grid mode (classic panorama) +static void DrawGridPanoramaQuads(S32 width, S32 height, float tileWidth, float tileHeight, float panoramaScroll, Tesselator *t) +{ + if(tileWidth <= 0.0f) tileWidth = static_cast(width); + if(tileHeight <= 0.0f) tileHeight = static_cast(height); + + const float scroll = panoramaScroll - floorf(panoramaScroll); + const float scrollPx = scroll * tileWidth; + const float startX = -scrollPx; + + t->begin(); + for(float y = -tileHeight; y < static_cast(height) + tileHeight; y += tileHeight) + { + const float top = y; + const float bottom = y + tileHeight; + for(float x = startX - tileWidth; x < static_cast(width) + tileWidth; x += tileWidth) + { + const float left = x; + const float right = x + tileWidth; + + t->vertexUV(left, bottom, 0.0f, 0.0f, 1.0f); + t->vertexUV(right, bottom, 0.0f, 1.0f, 1.0f); + t->vertexUV(right, top, 0.0f, 1.0f, 0.0f); + t->vertexUV(left, top, 0.0f, 0.0f, 0.0f); + } + } + t->end(); +} + UIComponent_Panorama::UIComponent_Panorama(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { m_bShowingDay = true; @@ -259,11 +463,10 @@ void UIComponent_Panorama::tick() if(m_lastScrollTickMs != 0) { const DWORD kMaxElapsedMs = 250; - const float kScrollPerMs = 0.0001f / 16.6667f; DWORD elapsedMs = nowMs - m_lastScrollTickMs; if(elapsedMs > kMaxElapsedMs) elapsedMs = kMaxElapsedMs; - m_panoramaScroll += kScrollPerMs * static_cast(elapsedMs); + m_panoramaScroll += m_panoramaScrollSpeed * static_cast(elapsedMs); } m_lastScrollTickMs = nowMs; @@ -290,19 +493,33 @@ void UIComponent_Panorama::EnsurePanoramaTexturesLoaded() m_panoramaTextureRoot = currentRoot; m_bPanoramaTexturesLoaded = false; + + // search for panorama.xml + PanoramaConfig config = LoadPanoramaConfig(currentRoot); + m_panoramaScrollSpeed = config.scrollSpeed; + m_panoramaBlurSigma = config.blurSigma; + m_panoramaGridEnabled = config.gridEnabled; + m_panoramaGridSizeX = config.gridSizeX; + m_panoramaGridSizeY = config.gridSizeY; + m_panoramaGridScalingNearest = config.gridScalingNearest; + int dayWidth = 0; int dayHeight = 0; - m_texPanoramaDay = LoadPanoramaQuadTexture(L"Panorama_S.png", dayWidth, dayHeight); + m_texPanoramaDay = LoadPanoramaQuadTexture(L"Panorama_S.png", m_panoramaBlurSigma, dayWidth, dayHeight); int nightWidth = 0; int nightHeight = 0; - m_texPanoramaNight = LoadPanoramaQuadTexture(L"Panorama_N.png", nightWidth, nightHeight); + m_texPanoramaNight = LoadPanoramaQuadTexture(L"Panorama_N.png", m_panoramaBlurSigma, nightWidth, nightHeight); if(dayWidth > 0 && dayHeight > 0) { m_panoramaAspect = static_cast(dayWidth) / static_cast(dayHeight); + m_panoramaNativeWidth = dayWidth; + m_panoramaNativeHeight = dayHeight; } else if(nightWidth > 0 && nightHeight > 0) { m_panoramaAspect = static_cast(nightWidth) / static_cast(nightHeight); + m_panoramaNativeWidth = nightWidth; + m_panoramaNativeHeight = nightHeight; } m_bPanoramaTexturesLoaded = (m_texPanoramaDay >= 0 || m_texPanoramaNight >= 0); } @@ -328,31 +545,27 @@ void UIComponent_Panorama::DrawPanoramaBackgroundQuad(S32 width, S32 height) Minecraft::GetInstance()->textures->bind(texId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - float sourceWidth = static_cast(height) * m_panoramaAspect; - float scroll = m_panoramaScroll - floorf(m_panoramaScroll); - float scrollPx = scroll * sourceWidth; - float startX = -scrollPx; - float startY = 0.0f; - const float overscanY = (static_cast(height) * 0.03f > 8.0f) ? (static_cast(height) * 0.03f) : 8.0f; - const float drawTop = -overscanY; - const float drawBottom = static_cast(height) + overscanY; + // nearest should only be used in grid mode, i dont necessarily see a purpose for it otherwise + const int panoramaFilter = (m_panoramaGridEnabled && m_panoramaGridScalingNearest) ? GL_NEAREST : GL_LINEAR; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, panoramaFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, panoramaFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); Tesselator *t = Tesselator::getInstance(); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - t->begin(); - for(float x = startX - sourceWidth; x < static_cast(width) + sourceWidth; x += sourceWidth) + + if(m_panoramaGridEnabled) { - float left = x; - float right = x + sourceWidth; - t->vertexUV(left, drawBottom, 0.0f, 0.0f, 1.0f); - t->vertexUV(right, drawBottom, 0.0f, 1.0f, 1.0f); - t->vertexUV(right, drawTop, 0.0f, 1.0f, 0.0f); - t->vertexUV(left, drawTop, 0.0f, 0.0f, 0.0f); + const float tileWidth = static_cast(m_panoramaNativeWidth) * m_panoramaGridSizeX; + const float tileHeight = static_cast(m_panoramaNativeHeight) * m_panoramaGridSizeY; + DrawGridPanoramaQuads(width, height, tileWidth, tileHeight, m_panoramaScroll, t); } - t->end(); + else + { + DrawScrollingPanoramaQuads(width, height, m_panoramaAspect, m_panoramaScroll, t); + } + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glDisable(GL_BLEND); diff --git a/Minecraft.Client/Common/UI/UIComponent_Panorama.h b/Minecraft.Client/Common/UI/UIComponent_Panorama.h index 3572c36a..d5465694 100644 --- a/Minecraft.Client/Common/UI/UIComponent_Panorama.h +++ b/Minecraft.Client/Common/UI/UIComponent_Panorama.h @@ -8,14 +8,29 @@ private: bool m_bShowingDay; void EnsurePanoramaTexturesLoaded(); void DrawPanoramaBackgroundQuad(S32 width, S32 height); + int m_texPanoramaDay = -1; int m_texPanoramaNight = -1; + bool m_bPanoramaTexturesLoaded = false; + wstring m_panoramaTextureRoot; float m_panoramaAspect = 1.0f; DWORD m_lastScrollTickMs = 0; float m_panoramaScroll = 0.0f; + float m_panoramaScrollSpeed = 0.0001f / 16.6667f; + float m_panoramaBlurSigma = 0.5f; + + bool m_panoramaGridEnabled = false; + float m_panoramaGridSizeX = 1.0f; + float m_panoramaGridSizeY = 1.0f; + + int m_panoramaNativeWidth = 0; + int m_panoramaNativeHeight = 0; + + bool m_panoramaGridScalingNearest = false; + public: UIComponent_Panorama(int iPad, void *initData, UILayer *parentLayer); From 99414c8906e62bf20023734b694437c971363212 Mon Sep 17 00:00:00 2001 From: Fireblade <3+fireblade@noreply.neolegacy.dev> Date: Mon, 13 Jul 2026 02:21:09 -0400 Subject: [PATCH 2/3] fix: village generation previously, world generation standards required all parts of the village (houses, pathes, etc) to be in a village-appropriate biome. now, villages have been made to inherit from the well's point-of-view; meaning that, as long as the well is in an acceptable biome, the rest of the village will continue to generate --- Minecraft.World/Level.cpp | 23 +++++++++++++++ Minecraft.World/Level.h | 1 + Minecraft.World/VillagePieces.cpp | 49 +++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index 1c6bc110..4563e0db 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -2206,6 +2206,29 @@ int Level::getTopSolidBlock(int x, int z) return -1; } +int Level::getTopSolidOrLiquidBlock(int x, int z) +{ + LevelChunk *levelChunk = getChunkAt(x, z); + + int y = levelChunk->getHighestSectionPosition() + 15; + + x &= 15; + z &= 15; + + while (y > 0) + { + int t = levelChunk->getTile(x, y, z); + if (t == 0 || Tile::tiles[t]->material == Material::leaves) + { + y--; + } + else + { + return y + 1; + } + } + return -1; +} int Level::getLightDepth(int x, int z) { diff --git a/Minecraft.World/Level.h b/Minecraft.World/Level.h index 4a6686fd..a71c02e8 100644 --- a/Minecraft.World/Level.h +++ b/Minecraft.World/Level.h @@ -331,6 +331,7 @@ public: Vec3 *getFogColor(float a); int getTopRainBlock(int x, int z); int getTopSolidBlock(int x, int z); + int getTopSolidOrLiquidBlock(int x, int z); bool biomeHasRain(int x, int z); // 4J added bool biomeHasSnow(int x, int z); // 4J added int getLightDepth(int x, int z); diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp index 641c8a68..4a73321f 100644 --- a/Minecraft.World/VillagePieces.cpp +++ b/Minecraft.World/VillagePieces.cpp @@ -211,6 +211,7 @@ StructurePiece *VillagePieces::generateAndAddPiece(StartPiece *startPiece, list< StructurePiece *newPiece = generatePieceFromSmallDoor(startPiece, pieces, random, footX, footY, footZ, direction, depth + 1); if (newPiece != nullptr) { + /* int x = (newPiece->boundingBox->x0 + newPiece->boundingBox->x1) / 2; int z = (newPiece->boundingBox->z0 + newPiece->boundingBox->z1) / 2; int xs = newPiece->boundingBox->x1 - newPiece->boundingBox->x0; @@ -218,11 +219,12 @@ StructurePiece *VillagePieces::generateAndAddPiece(StartPiece *startPiece, list< int r = xs > zs ? xs : zs; if (startPiece->getBiomeSource()->containsOnly(x, z, r / 2 + 4, VillageFeature::allowedBiomes)) { + */ pieces->push_back(newPiece); startPiece->pendingHouses.push_back(newPiece); return newPiece; - } - delete newPiece; +// } + // delete newPiece; } return nullptr; } @@ -242,6 +244,7 @@ StructurePiece *VillagePieces::generateAndAddRoadPiece(StartPiece *startPiece, l if (box != nullptr && box->y0 > LOWEST_Y_POSITION) { StructurePiece *newPiece = new StraightRoad(startPiece, depth, random, box, direction); + /* int x = (newPiece->boundingBox->x0 + newPiece->boundingBox->x1) / 2; int z = (newPiece->boundingBox->z0 + newPiece->boundingBox->z1) / 2; int xs = newPiece->boundingBox->x1 - newPiece->boundingBox->x0; @@ -249,12 +252,13 @@ StructurePiece *VillagePieces::generateAndAddRoadPiece(StartPiece *startPiece, l int r = xs > zs ? xs : zs; if (startPiece->getBiomeSource()->containsOnly(x, z, r / 2 + 4, VillageFeature::allowedBiomes)) { + */ pieces->push_back(newPiece); startPiece->pendingRoads.push_back(newPiece); return newPiece; - } +// } // 4J Stu - The dtor for newPiece will destroy box - delete newPiece; + // delete newPiece; } else if(box != nullptr) { @@ -486,6 +490,24 @@ void VillagePieces::VillagePiece::generateBox(Level *level, BoundingBox *chunkBB void VillagePieces::VillagePiece::fillColumnDown(Level *level, int block, int data, int x, int startY, int z, BoundingBox *chunkBB) { + // clear any and all plants + int worldX = getWorldX(x, z); + int worldZ = getWorldZ(x, z); + int worldY = getWorldY(startY); + + for (int y = worldY; y > 0; y--) + { + int tile = level->getTile(worldX, y, worldZ); + if (tile == Tile::tallgrass_Id || tile == Tile::double_plant_Id || tile == Tile::deadbush_Id) + { + level->setTileAndData(worldX, y, worldZ, 0, 0, Tile::UPDATE_CLIENTS); + } + else if (tile != 0) + { + break; + } + } + int bblock = biomeBlock(block, data); int bdata = biomeData(block, data); StructurePiece::fillColumnDown(level, bblock, bdata, x, startY, z, chunkBB); @@ -727,7 +749,7 @@ bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, Boun { if (chunkBB->isInside(x, 64, z)) { - int y = level->getTopSolidBlock(x, z) - 1; + int y = level->getTopSolidOrLiquidBlock(x, z) - 1; // was getTopSolidBlock int tileAtY = level->getTile(x, y, z); if (tileAtY == Tile::water_Id || tileAtY == Tile::flowing_water_Id) { @@ -741,7 +763,22 @@ bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, Boun } else { - level->setTileAndData(x, y, z, roadTile, 0, Tile::UPDATE_CLIENTS); + // tall grass fixes + int tileBelow = level->getTile(x, y - 1, z); + if (tileBelow == Tile::double_plant_Id) + { + level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y - 1, z, 0, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y - 2, z, roadTile, 0, Tile::UPDATE_CLIENTS); + } + else if (level->getTile(x, y, z) == Tile::tallgrass_Id) + { + level->setTileAndData(x, y, z, 0, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y - 1, z, roadTile, 0, Tile::UPDATE_CLIENTS); + } + else { + level->setTileAndData(x, y, z, roadTile, 0, Tile::UPDATE_CLIENTS); + } } } } From f5b395b4f0f6a864318b0c4b3d91ca90a34cd8b5 Mon Sep 17 00:00:00 2001 From: Fireblade <3+fireblade@noreply.neolegacy.dev> Date: Mon, 13 Jul 2026 02:39:22 -0400 Subject: [PATCH 3/3] fix: wood bridges --- Minecraft.World/VillagePieces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp index 4a73321f..05f02208 100644 --- a/Minecraft.World/VillagePieces.cpp +++ b/Minecraft.World/VillagePieces.cpp @@ -757,7 +757,7 @@ bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, Boun int fill = y - 1; while (fill >= 0 && (level->getTile(x, fill, z) == Tile::water_Id || level->getTile(x, fill, z) == Tile::flowing_water_Id)) { - level->setTileAndData(x, fill, z, Tile::planks_Id, 0, Tile::UPDATE_CLIENTS); + level->setTileAndData(x, y, z, Tile::planks_Id, 0, Tile::UPDATE_CLIENTS); fill--; } }