diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp index 6f0ad7366..0a63b8747 100644 --- a/Minecraft.Client/Chunk.cpp +++ b/Minecraft.Client/Chunk.cpp @@ -739,9 +739,9 @@ void Chunk::rebuild_SPU() { // 4J - get tile from those copied into our local array in earlier optimisation unsigned char tileId = pOutData->getTile(x,y,z); - if (tileId > 0) + if (tileId > 0 && tileId != 0xff) { - if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) + if (currentLayer == 0 && Tile::tiles[tileId] && Tile::tiles[tileId]->isEntityTile()) { shared_ptr et = region.getTileEntity(x, y, z); if (TileEntityRenderDispatcher::instance->hasRenderer(et)) @@ -754,6 +754,7 @@ void Chunk::rebuild_SPU() { Tile *tile = Tile::tiles[tileId]; + if (!tile) continue; int renderLayer = tile->getRenderLayer(); if (renderLayer != currentLayer) diff --git a/Minecraft.Client/ClientConstants.cpp b/Minecraft.Client/ClientConstants.cpp index 43da4360b..d1d80aa9e 100644 --- a/Minecraft.Client/ClientConstants.cpp +++ b/Minecraft.Client/ClientConstants.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" #include "ClientConstants.h" -const wstring ClientConstants::VERSION_STRING = wstring(L"Minecraft LCE ") + VER_FILEVERSION_STR_W;//+ SharedConstants::VERSION_STRING; \ No newline at end of file +const wstring ClientConstants::VERSION_STRING = wstring(L"Minecraft LCE ") + VER_FILEVERSION_STR_W;//+ SharedConstants::VERSION_STRING; +const wstring ClientConstants::BRANCH_STRING = VER_BRANCHVERSION_STR_W; diff --git a/Minecraft.Client/ClientConstants.h b/Minecraft.Client/ClientConstants.h index 82bba386f..1476e51bf 100644 --- a/Minecraft.Client/ClientConstants.h +++ b/Minecraft.Client/ClientConstants.h @@ -13,6 +13,7 @@ class ClientConstants // INTERNAL DEVELOPMENT SETTINGS public: static const wstring VERSION_STRING; + static const wstring BRANCH_STRING; static const bool DEADMAU5_CAMERA_CHEATS = false; }; \ No newline at end of file diff --git a/Minecraft.Client/Common/BuildVer.h b/Minecraft.Client/Common/BuildVer.h index babe75660..eaa77d26a 100644 --- a/Minecraft.Client/Common/BuildVer.h +++ b/Minecraft.Client/Common/BuildVer.h @@ -1,6 +1,7 @@ #pragma once #define VER_PRODUCTBUILD 560 -#define VER_PRODUCTVERSION_STR_W L"DEV (unknown)" +#define VER_PRODUCTVERSION_STR_W L"DEV (unknown version)" #define VER_FILEVERSION_STR_W VER_PRODUCTVERSION_STR_W +#define VER_BRANCHVERSION_STR_W L"UNKNOWN BRANCH" #define VER_NETWORK VER_PRODUCTBUILD diff --git a/Minecraft.Client/Common/PostProcesser.h b/Minecraft.Client/Common/PostProcesser.h index 35fa50809..966d58bc6 100644 --- a/Minecraft.Client/Common/PostProcesser.h +++ b/Minecraft.Client/Common/PostProcesser.h @@ -51,7 +51,10 @@ private: struct GammaCBData { float gamma; - float pad[3]; + float pad; + float uvOffsetX, uvOffsetY; + float uvScaleX, uvScaleY; + float pad2[2]; }; static const char* g_gammaVSCode; diff --git a/Minecraft.Client/Common/UI/UIComponent_Chat.cpp b/Minecraft.Client/Common/UI/UIComponent_Chat.cpp index 257199810..49c5d5d21 100644 --- a/Minecraft.Client/Common/UI/UIComponent_Chat.cpp +++ b/Minecraft.Client/Common/UI/UIComponent_Chat.cpp @@ -143,13 +143,17 @@ void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType vi F32 scale; ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart); - IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) ); - S32 contentOffX, contentOffY; - ComputeSplitContentOffset(viewport, m_movieWidth, m_movieHeight, scale, tileWidth, tileHeight, tileYStart, contentOffX, contentOffY); - xPos += contentOffX; - yPos += contentOffY; - ui.setupRenderPosition(xPos, yPos); + // For vertical split, scale down to fit the full SWF height when the + // window is shorter than the movie (same fix as HUD). + if(!needsYTile && m_movieHeight > 0) + { + F32 scaleH = (F32)tileHeight / (F32)m_movieHeight; + if(scaleH < scale) + scale = scaleH; + } + + IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) ); IggyPlayerDrawTilesStart ( getMovie() ); diff --git a/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp b/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp index 566f759d2..418546b70 100644 --- a/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp +++ b/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp @@ -247,13 +247,17 @@ void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportTyp F32 scale; ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart); - IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) ); - S32 contentOffX, contentOffY; - ComputeSplitContentOffset(viewport, m_movieWidth, m_movieHeight, scale, tileWidth, tileHeight, tileYStart, contentOffX, contentOffY); - xPos += contentOffX; - yPos += contentOffY; - ui.setupRenderPosition(xPos, yPos); + // For vertical split, scale down to fit the full SWF height when the + // window is shorter than the movie (same fix as HUD). + if(!needsYTile && m_movieHeight > 0) + { + F32 scaleH = (F32)tileHeight / (F32)m_movieHeight; + if(scaleH < scale) + scale = scaleH; + } + + IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) ); IggyPlayerDrawTilesStart ( getMovie() ); diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index 205a4f0bf..b12ea5e73 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -522,33 +522,9 @@ void UIController::tick() void UIController::loadSkins() { - wstring platformSkinPath = L""; - #ifdef __PS3__ - platformSkinPath = L"skinPS3.swf"; -#elif defined __PSVITA__ - platformSkinPath = L"skinVita.swf"; -#elif defined _WINDOWS64 - // Windows64/Durango/Orbis always load HD skin libraries unconditionally, - // which import "platformskinHD.swf" by name. The platform skin must - // therefore always be the HD variant regardless of screen resolution. - platformSkinPath = L"skinHDWin.swf"; -#elif defined _DURANGO - platformSkinPath = L"skinHDDurango.swf"; -#elif defined __ORBIS__ - platformSkinPath = L"skinHDOrbis.swf"; + m_iggyLibraries[eLibrary_Platform] = loadSkin(L"skinPS3.swf", L"platformskin.swf"); -#endif - // Every platform has one of these, so nothing shared. - // On HD platforms (Win64/Durango/Orbis) the skin libraries always import - // "platformskinHD.swf", so we must register under that name at any resolution. -#if defined(_WINDOWS64) || defined(_DURANGO) || defined(__ORBIS__) - m_iggyLibraries[eLibrary_Platform] = loadSkin(platformSkinPath, L"platformskinHD.swf"); -#else - m_iggyLibraries[eLibrary_Platform] = loadSkin(platformSkinPath, L"platformskin.swf"); -#endif - -#if defined(__PS3__) || defined(__PSVITA__) m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinGraphics.swf", L"skinGraphics.swf"); m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinGraphicsHud.swf", L"skinGraphicsHud.swf"); m_iggyLibraries[eLibrary_GraphicsInGame] = loadSkin(L"skinGraphicsInGame.swf", L"skinGraphicsInGame.swf"); @@ -559,13 +535,28 @@ void UIController::loadSkins() m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHud.swf", L"skinHud.swf"); m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinTooltips.swf", L"skinTooltips.swf"); m_iggyLibraries[eLibrary_Default] = loadSkin(L"skin.swf", L"skin.swf"); -#endif -#if ( defined(_WINDOWS64) || defined(_DURANGO) || defined(__ORBIS__) ) +#elif defined __PSVITA__ + m_iggyLibraries[eLibrary_Platform] = loadSkin(L"skinVita.swf", L"platformskin.swf"); -#if defined(_WINDOWS64) - // 4J Stu - Load the 720/480 skins so that we have something to fallback on during development -#ifndef _FINAL_BUILD + m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinGraphics.swf", L"skinGraphics.swf"); + m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinGraphicsHud.swf", L"skinGraphicsHud.swf"); + m_iggyLibraries[eLibrary_GraphicsInGame] = loadSkin(L"skinGraphicsInGame.swf", L"skinGraphicsInGame.swf"); + m_iggyLibraries[eLibrary_GraphicsTooltips] = loadSkin(L"skinGraphicsTooltips.swf", L"skinGraphicsTooltips.swf"); + m_iggyLibraries[eLibrary_GraphicsLabels] = loadSkin(L"skinGraphicsLabels.swf", L"skinGraphicsLabels.swf"); + m_iggyLibraries[eLibrary_Labels] = loadSkin(L"skinLabels.swf", L"skinLabels.swf"); + m_iggyLibraries[eLibrary_InGame] = loadSkin(L"skinInGame.swf", L"skinInGame.swf"); + m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHud.swf", L"skinHud.swf"); + m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinTooltips.swf", L"skinTooltips.swf"); + m_iggyLibraries[eLibrary_Default] = loadSkin(L"skin.swf", L"skin.swf"); + +#elif defined _WINDOWS64 + // HD platform skin — required by skinHD*.swf (1080p scene SWFs) + m_iggyLibraries[eLibrary_Platform] = loadSkin(L"skinHDWin.swf", L"platformskinHD.swf"); + // Non-HD platform skin — required by skin*.swf (720p/480p scene SWFs) + m_iggyLibraries[eLibraryFallback_Platform] = loadSkin(L"skinWin.swf", L"platformskin.swf"); + + // Non-HD skin set (720p/480p scenes import these) m_iggyLibraries[eLibraryFallback_GraphicsDefault] = loadSkin(L"skinGraphics.swf", L"skinGraphics.swf"); m_iggyLibraries[eLibraryFallback_GraphicsHUD] = loadSkin(L"skinGraphicsHud.swf", L"skinGraphicsHud.swf"); m_iggyLibraries[eLibraryFallback_GraphicsInGame] = loadSkin(L"skinGraphicsInGame.swf", L"skinGraphicsInGame.swf"); @@ -576,8 +567,21 @@ void UIController::loadSkins() m_iggyLibraries[eLibraryFallback_HUD] = loadSkin(L"skinHud.swf", L"skinHud.swf"); m_iggyLibraries[eLibraryFallback_Tooltips] = loadSkin(L"skinTooltips.swf", L"skinTooltips.swf"); m_iggyLibraries[eLibraryFallback_Default] = loadSkin(L"skin.swf", L"skin.swf"); -#endif -#endif + + // HD skin set (1080p scenes import these) + m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinHDGraphics.swf", L"skinHDGraphics.swf"); + m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinHDGraphicsHud.swf", L"skinHDGraphicsHud.swf"); + m_iggyLibraries[eLibrary_GraphicsInGame] = loadSkin(L"skinHDGraphicsInGame.swf", L"skinHDGraphicsInGame.swf"); + m_iggyLibraries[eLibrary_GraphicsTooltips] = loadSkin(L"skinHDGraphicsTooltips.swf", L"skinHDGraphicsTooltips.swf"); + m_iggyLibraries[eLibrary_GraphicsLabels] = loadSkin(L"skinHDGraphicsLabels.swf", L"skinHDGraphicsLabels.swf"); + m_iggyLibraries[eLibrary_Labels] = loadSkin(L"skinHDLabels.swf", L"skinHDLabels.swf"); + m_iggyLibraries[eLibrary_InGame] = loadSkin(L"skinHDInGame.swf", L"skinHDInGame.swf"); + m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHDHud.swf", L"skinHDHud.swf"); + m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinHDTooltips.swf", L"skinHDTooltips.swf"); + m_iggyLibraries[eLibrary_Default] = loadSkin(L"skinHD.swf", L"skinHD.swf"); + +#elif defined _DURANGO + m_iggyLibraries[eLibrary_Platform] = loadSkin(L"skinHDDurango.swf", L"platformskinHD.swf"); m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinHDGraphics.swf", L"skinHDGraphics.swf"); m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinHDGraphicsHud.swf", L"skinHDGraphicsHud.swf"); @@ -589,7 +593,21 @@ void UIController::loadSkins() m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHDHud.swf", L"skinHDHud.swf"); m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinHDTooltips.swf", L"skinHDTooltips.swf"); m_iggyLibraries[eLibrary_Default] = loadSkin(L"skinHD.swf", L"skinHD.swf"); -#endif // HD platforms + +#elif defined __ORBIS__ + m_iggyLibraries[eLibrary_Platform] = loadSkin(L"skinHDOrbis.swf", L"platformskinHD.swf"); + + m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinHDGraphics.swf", L"skinHDGraphics.swf"); + m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinHDGraphicsHud.swf", L"skinHDGraphicsHud.swf"); + m_iggyLibraries[eLibrary_GraphicsInGame] = loadSkin(L"skinHDGraphicsInGame.swf", L"skinHDGraphicsInGame.swf"); + m_iggyLibraries[eLibrary_GraphicsTooltips] = loadSkin(L"skinHDGraphicsTooltips.swf", L"skinHDGraphicsTooltips.swf"); + m_iggyLibraries[eLibrary_GraphicsLabels] = loadSkin(L"skinHDGraphicsLabels.swf", L"skinHDGraphicsLabels.swf"); + m_iggyLibraries[eLibrary_Labels] = loadSkin(L"skinHDLabels.swf", L"skinHDLabels.swf"); + m_iggyLibraries[eLibrary_InGame] = loadSkin(L"skinHDInGame.swf", L"skinHDInGame.swf"); + m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHDHud.swf", L"skinHDHud.swf"); + m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinHDTooltips.swf", L"skinHDTooltips.swf"); + m_iggyLibraries[eLibrary_Default] = loadSkin(L"skinHD.swf", L"skinHD.swf"); +#endif } IggyLibrary UIController::loadSkin(const wstring &skinPath, const wstring &skinName) @@ -1810,14 +1828,24 @@ GDrawTexture * RADLINK UIController::TextureSubstitutionCreateCallback ( void * // 4J Stu - All our flash controls that allow replacing textures use a special 64x64 symbol // Force this size here so that our images don't get scaled wildly - #if (defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 ) + #if (defined __ORBIS__ || defined _DURANGO ) *width = 96; *height = 96; #else *width = 64; *height = 64; - #endif + + #if defined _WINDOWS64 + // Only set the size to 96x96 for 1080p on Windows + UIScene *scene = uiController->GetTopScene(0); + if (scene->getSceneResolution() == UIScene::eSceneResolution_1080) + { + *width = 96; + *height = 96; + } + #endif + *destroy_callback_data = (void *)id; app.DebugPrintf("Found substitution texture %ls (%d) - %dx%d\n", static_cast(texture_name), id, image.getWidth(), image.getHeight()); diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 29c278129..08a5ba088 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -101,9 +101,8 @@ private: eLibrary_Tooltips, eLibrary_Default, -#if ( defined(_WINDOWS64) ) - // 4J Stu - Load the 720/480 skins so that we have something to fallback on during development -#ifndef _FINAL_BUILD +#if defined(_WINDOWS64) + // Non-HD skin libraries needed by 720p/480p scene SWFs. eLibraryFallback_Platform, eLibraryFallback_GraphicsDefault, eLibraryFallback_GraphicsHUD, @@ -115,7 +114,6 @@ private: eLibraryFallback_HUD, eLibraryFallback_Tooltips, eLibraryFallback_Default, -#endif #endif eLibrary_Count, diff --git a/Minecraft.Client/Common/UI/UIScene.cpp b/Minecraft.Client/Common/UI/UIScene.cpp index 6918bc5bd..5630e9726 100644 --- a/Minecraft.Client/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Common/UI/UIScene.cpp @@ -286,8 +286,16 @@ void UIScene::loadMovie() moviePath.append(L"Vita.swf"); m_loadedResolution = eSceneResolution_Vita; #elif defined _WINDOWS64 - moviePath.append(L"1080.swf"); - m_loadedResolution = eSceneResolution_1080; + if(ui.getScreenHeight() > 720.0f) + { + moviePath.append(L"1080.swf"); + m_loadedResolution = eSceneResolution_1080; + } + else + { + moviePath.append(L"720.swf"); + m_loadedResolution = eSceneResolution_720; + } #else moviePath.append(L"1080.swf"); m_loadedResolution = eSceneResolution_1080; diff --git a/Minecraft.Client/GameRenderer.cpp b/Minecraft.Client/GameRenderer.cpp index 169f1280b..73b39529c 100644 --- a/Minecraft.Client/GameRenderer.cpp +++ b/Minecraft.Client/GameRenderer.cpp @@ -954,91 +954,70 @@ float GameRenderer::ComputeGammaFromSlider(float slider0to100) void GameRenderer::CachePlayerGammas() { - for (int j = 0; j < XUSER_MAX_COUNT && j < NUM_LIGHT_TEXTURES; ++j) - { - std::shared_ptr player = Minecraft::GetInstance()->localplayers[j]; - if (!player) - { - m_cachedGammaPerPlayer[j] = 1.0f; - continue; - } + const float slider = app.GetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_Gamma); + const float gamma = ComputeGammaFromSlider(slider); - const float slider = app.GetGameSettings(j, eGameSetting_Gamma); // 0..100 - m_cachedGammaPerPlayer[j] = ComputeGammaFromSlider(slider); - } + for (int j = 0; j < XUSER_MAX_COUNT && j < NUM_LIGHT_TEXTURES; ++j) + m_cachedGammaPerPlayer[j] = gamma; } bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport) const { - // Use the actual backbuffer dimensions so viewports adapt to window resize. extern int g_rScreenWidth; extern int g_rScreenHeight; - int active = 0; - int indexMap[NUM_LIGHT_TEXTURES] = {-1, -1, -1, -1}; - for (int i = 0; i < XUSER_MAX_COUNT && i < NUM_LIGHT_TEXTURES; ++i) - { - if (Minecraft::GetInstance()->localplayers[i]) - indexMap[active++] = i; - } - - if (active <= 1) - { - outViewport.TopLeftX = 0.0f; - outViewport.TopLeftY = 0.0f; - outViewport.Width = static_cast(g_rScreenWidth); - outViewport.Height = static_cast(g_rScreenHeight); - outViewport.MinDepth = 0.0f; - outViewport.MaxDepth = 1.0f; - return true; - } - - int k = -1; - for (int ord = 0; ord < active; ++ord) - if (indexMap[ord] == j) - { - k = ord; - break; - } - if (k < 0) + std::shared_ptr player = Minecraft::GetInstance()->localplayers[j]; + if (!player) return false; - const float width = static_cast(g_rScreenWidth); - const float height = static_cast(g_rScreenHeight); + const float w = static_cast(g_rScreenWidth); + const float h = static_cast(g_rScreenHeight); + const float halfW = w * 0.5f; + const float halfH = h * 0.5f; - if (active == 2) + outViewport.MinDepth = 0.0f; + outViewport.MaxDepth = 1.0f; + + switch (static_cast(player->m_iScreenSection)) { - const float halfH = height * 0.5f; - outViewport.TopLeftX = 0.0f; - outViewport.Width = width; - outViewport.MinDepth = 0.0f; - outViewport.MaxDepth = 1.0f; - if (k == 0) - { - outViewport.TopLeftY = 0.0f; - outViewport.Height = halfH; - } - else - { - outViewport.TopLeftY = halfH; - outViewport.Height = halfH; - } - return true; - } - else - { - const float halfW = width * 0.5f; - const float halfH = height * 0.5f; - const int row = (k >= 2) ? 1 : 0; - const int col = (k % 2); - outViewport.TopLeftX = col ? halfW : 0.0f; - outViewport.TopLeftY = row ? halfH : 0.0f; - outViewport.Width = halfW; - outViewport.Height = halfH; - outViewport.MinDepth = 0.0f; - outViewport.MaxDepth = 1.0f; - return true; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + outViewport.TopLeftX = 0; outViewport.TopLeftY = 0; + outViewport.Width = w; outViewport.Height = halfH; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + outViewport.TopLeftX = 0; outViewport.TopLeftY = halfH; + outViewport.Width = w; outViewport.Height = halfH; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + outViewport.TopLeftX = 0; outViewport.TopLeftY = 0; + outViewport.Width = halfW; outViewport.Height = h; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + outViewport.TopLeftX = halfW; outViewport.TopLeftY = 0; + outViewport.Width = halfW; outViewport.Height = h; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + outViewport.TopLeftX = 0; outViewport.TopLeftY = 0; + outViewport.Width = halfW; outViewport.Height = halfH; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + outViewport.TopLeftX = halfW; outViewport.TopLeftY = 0; + outViewport.Width = halfW; outViewport.Height = halfH; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + outViewport.TopLeftX = 0; outViewport.TopLeftY = halfH; + outViewport.Width = halfW; outViewport.Height = halfH; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + outViewport.TopLeftX = halfW; outViewport.TopLeftY = halfH; + outViewport.Width = halfW; outViewport.Height = halfH; + break; + default: + outViewport.TopLeftX = 0; outViewport.TopLeftY = 0; + outViewport.Width = w; outViewport.Height = h; + break; } + return true; } uint32_t GameRenderer::BuildPlayerViewports(D3D11_VIEWPORT *outViewports, float *outGammas, UINT maxCount) const diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp index 3d5bd7ef4..8654304bc 100644 --- a/Minecraft.Client/Gui.cpp +++ b/Minecraft.Client/Gui.cpp @@ -851,7 +851,10 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) #ifndef _FINAL_BUILD MemSect(31); - if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr) + + // temporarily render overlay at all times so version is more obvious in bug reports + // we can turn this off once things stabilize + if (true)// minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr) { const int debugLeft = 1; const int debugTop = 1; @@ -870,117 +873,120 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) vector lines; lines.push_back(ClientConstants::VERSION_STRING); - lines.push_back(minecraft->fpsString); - lines.push_back(L"E: " + std::to_wstring(minecraft->level->getAllEntities().size())); // Could maybe use entity::shouldRender to work out how many are rendered but thats like expensive - // TODO Add server information with packet counts - once multiplayer is more stable - int renderDistance = app.GetGameSettings(iPad, eGameSetting_RenderDistance); - // Calculate the chunk sections using 16 * (2n + 1)^2 - lines.push_back(L"C: " + std::to_wstring(16 * (2 * renderDistance + 1) * (2 * renderDistance + 1)) + L" D: " + std::to_wstring(renderDistance)); - lines.push_back(minecraft->gatherStats4()); // Chunk Cache - - // Dimension - wstring dimension = L"unknown"; - switch (minecraft->player->dimension) + lines.push_back(ClientConstants::BRANCH_STRING); + if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr) { - case -1: - dimension = L"minecraft:the_nether"; - break; - case 0: - dimension = L"minecraft:overworld"; - break; - case 1: - dimension = L"minecraft:the_end"; - break; - } - lines.push_back(dimension); + lines.push_back(minecraft->fpsString); + lines.push_back(L"E: " + std::to_wstring(minecraft->level->getAllEntities().size())); // Could maybe use entity::shouldRender to work out how many are rendered but thats like expensive + // TODO Add server information with packet counts - once multiplayer is more stable + int renderDistance = app.GetGameSettings(iPad, eGameSetting_RenderDistance); + // Calculate the chunk sections using 16 * (2n + 1)^2 + lines.push_back(L"C: " + std::to_wstring(16 * (2 * renderDistance + 1) * (2 * renderDistance + 1)) + L" D: " + std::to_wstring(renderDistance)); + lines.push_back(minecraft->gatherStats4()); // Chunk Cache - lines.push_back(L""); // Spacer + // Dimension + wstring dimension = L"unknown"; + switch (minecraft->player->dimension) + { + case -1: + dimension = L"minecraft:the_nether"; + break; + case 0: + dimension = L"minecraft:overworld"; + break; + case 1: + dimension = L"minecraft:the_end"; + break; + } + lines.push_back(dimension); - // Players block pos - int xBlockPos = Mth::floor(minecraft->player->x); - int yBlockPos = Mth::floor(minecraft->player->y); - int zBlockPos = Mth::floor(minecraft->player->z); + lines.push_back(L""); // Spacer - // Chunk player is in - int xChunkPos = xBlockPos >> 4; - int yChunkPos = yBlockPos >> 4; - int zChunkPos = zBlockPos >> 4; + // Players block pos + int xBlockPos = Mth::floor(minecraft->player->x); + int yBlockPos = Mth::floor(minecraft->player->y); + int zBlockPos = Mth::floor(minecraft->player->z); - // Players offset within the chunk - int xChunkOffset = xBlockPos & 15; - int yChunkOffset = yBlockPos & 15; - int zChunkOffset = zBlockPos & 15; + // Chunk player is in + int xChunkPos = xBlockPos >> 4; + int yChunkPos = yBlockPos >> 4; + int zChunkPos = zBlockPos >> 4; - // Format the position like java with limited decumal places - WCHAR posString[44]; // Allows upto 7 digit positions (+-9_999_999) - swprintf(posString, 44, L"%.3f / %.5f / %.3f", minecraft->player->x, minecraft->player->y, minecraft->player->z); + // Players offset within the chunk + int xChunkOffset = xBlockPos & 15; + int yChunkOffset = yBlockPos & 15; + int zChunkOffset = zBlockPos & 15; - lines.push_back(L"XYZ: " + std::wstring(posString)); - lines.push_back(L"Block: " + std::to_wstring(static_cast(xBlockPos)) + L" " + std::to_wstring(static_cast(yBlockPos)) + L" " + std::to_wstring(static_cast(zBlockPos))); - lines.push_back(L"Chunk: " + std::to_wstring(xChunkOffset) + L" " + std::to_wstring(yChunkOffset) + L" " + std::to_wstring(zChunkOffset) + L" in " + std::to_wstring(xChunkPos) + L" " + std::to_wstring(yChunkPos) + L" " + std::to_wstring(zChunkPos)); + // Format the position like java with limited decumal places + WCHAR posString[44]; // Allows upto 7 digit positions (+-9_999_999) + swprintf(posString, 44, L"%.3f / %.5f / %.3f", minecraft->player->x, minecraft->player->y, minecraft->player->z); - // Wrap the yRot to 360 then adjust to (-180 to 180) range to match java - float yRotDisplay = fmod(minecraft->player->yRot, 360.0f); - if (yRotDisplay > 180.0f) - { - yRotDisplay -= 360.0f; + lines.push_back(L"XYZ: " + std::wstring(posString)); + lines.push_back(L"Block: " + std::to_wstring(static_cast(xBlockPos)) + L" " + std::to_wstring(static_cast(yBlockPos)) + L" " + std::to_wstring(static_cast(zBlockPos))); + lines.push_back(L"Chunk: " + std::to_wstring(xChunkOffset) + L" " + std::to_wstring(yChunkOffset) + L" " + std::to_wstring(zChunkOffset) + L" in " + std::to_wstring(xChunkPos) + L" " + std::to_wstring(yChunkPos) + L" " + std::to_wstring(zChunkPos)); + + // Wrap the yRot to 360 then adjust to (-180 to 180) range to match java + float yRotDisplay = fmod(minecraft->player->yRot, 360.0f); + if (yRotDisplay > 180.0f) + { + yRotDisplay -= 360.0f; + } + if (yRotDisplay < -180.0f) + { + yRotDisplay += 360.0f; + } + // Generate the angle string in the format "yRot / xRot" with one decimal place, similar to java edition + WCHAR angleString[16]; + swprintf(angleString, 16, L"%.1f / %.1f", yRotDisplay, minecraft->player->xRot); + + // Work out the named direction + int direction = Mth::floor(minecraft->player->yRot * 4.0f / 360.0f + 0.5) & 0x3; + wstring cardinalDirection; + switch (direction) + { + case 0: + cardinalDirection = L"south"; + break; + case 1: + cardinalDirection = L"west"; + break; + case 2: + cardinalDirection = L"north"; + break; + case 3: + cardinalDirection = L"east"; + break; + } + + lines.push_back(L"Facing: " + cardinalDirection + L" (" + angleString + L")"); + + // We have to limit y to 256 as we don't get any information past that + if (minecraft->level != NULL && minecraft->level->hasChunkAt(xBlockPos, fmod(yBlockPos, 256), zBlockPos)) + { + LevelChunk *chunkAt = minecraft->level->getChunkAt(xBlockPos, zBlockPos); + if (chunkAt != NULL) + { + int skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset); + int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset); + int maxLight = fmax(skyLight, blockLight); + lines.push_back(L"Light: " + std::to_wstring(maxLight) + L" (" + std::to_wstring(skyLight) + L" sky, " + std::to_wstring(blockLight) + L" block)"); + + lines.push_back(L"CH S: " + std::to_wstring(chunkAt->getHeightmap(xChunkOffset, zChunkOffset))); + + Biome *biome = chunkAt->getBiome(xChunkOffset, zChunkOffset, minecraft->level->getBiomeSource()); + lines.push_back(L"Biome: " + biome->m_name + L" (" + std::to_wstring(biome->id) + L")"); + + lines.push_back(L"Difficulty: " + std::to_wstring(minecraft->level->difficulty) + L" (Day " + std::to_wstring(minecraft->level->getGameTime() / Level::TICKS_PER_DAY) + L")"); + } + } + + // This is all LCE only stuff, it was never on java + lines.push_back(L""); // Spacer + lines.push_back(L"Seed: " + std::to_wstring(minecraft->level->getLevelData()->getSeed())); + lines.push_back(minecraft->gatherStats1()); // Time to autosave + lines.push_back(minecraft->gatherStats2()); // Empty currently - CPlatformNetworkManagerStub::GatherStats() + lines.push_back(minecraft->gatherStats3()); // RTT } - if (yRotDisplay < -180.0f) - { - yRotDisplay += 360.0f; - } - // Generate the angle string in the format "yRot / xRot" with one decimal place, similar to java edition - WCHAR angleString[16]; - swprintf(angleString, 16, L"%.1f / %.1f", yRotDisplay, minecraft->player->xRot); - - // Work out the named direction - int direction = Mth::floor(minecraft->player->yRot * 4.0f / 360.0f + 0.5) & 0x3; - wstring cardinalDirection; - switch (direction) - { - case 0: - cardinalDirection = L"south"; - break; - case 1: - cardinalDirection = L"west"; - break; - case 2: - cardinalDirection = L"north"; - break; - case 3: - cardinalDirection = L"east"; - break; - } - - lines.push_back(L"Facing: " + cardinalDirection + L" (" + angleString + L")"); - - // We have to limit y to 256 as we don't get any information past that - if (minecraft->level != NULL && minecraft->level->hasChunkAt(xBlockPos, fmod(yBlockPos, 256), zBlockPos)) - { - LevelChunk *chunkAt = minecraft->level->getChunkAt(xBlockPos, zBlockPos); - if (chunkAt != NULL) - { - int skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset); - int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset); - int maxLight = fmax(skyLight, blockLight); - lines.push_back(L"Light: " + std::to_wstring(maxLight) + L" (" + std::to_wstring(skyLight) + L" sky, " + std::to_wstring(blockLight) + L" block)"); - - lines.push_back(L"CH S: " + std::to_wstring(chunkAt->getHeightmap(xChunkOffset, zChunkOffset))); - - Biome *biome = chunkAt->getBiome(xChunkOffset, zChunkOffset, minecraft->level->getBiomeSource()); - lines.push_back(L"Biome: " + biome->m_name + L" (" + std::to_wstring(biome->id) + L")"); - - lines.push_back(L"Difficulty: " + std::to_wstring(minecraft->level->difficulty) + L" (Day " + std::to_wstring(minecraft->level->getGameTime() / Level::TICKS_PER_DAY) + L")"); - } - } - - - // This is all LCE only stuff, it was never on java - lines.push_back(L""); // Spacer - lines.push_back(L"Seed: " + std::to_wstring(minecraft->level->getLevelData()->getSeed())); - lines.push_back(minecraft->gatherStats1()); // Time to autosave - lines.push_back(minecraft->gatherStats2()); // Empty currently - CPlatformNetworkManagerStub::GatherStats() - lines.push_back(minecraft->gatherStats3()); // RTT #ifdef _DEBUG // Only show terrain features in debug builds not release // TERRAIN FEATURES diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp index 0a0f3c44b..6d5497f02 100644 --- a/Minecraft.Client/PendingConnection.cpp +++ b/Minecraft.Client/PendingConnection.cpp @@ -190,34 +190,11 @@ void PendingConnection::handleLogin(shared_ptr packet) } else if (duplicateXuid) { - // The old player is still in PlayerList (disconnect hasn't been - // processed yet). Force-close the stale connection so the - // reconnecting client isn't rejected. - app.DebugPrintf("RECONNECT: Duplicate xuid for name: %ls, forcing old connection closed\n", name.c_str()); - shared_ptr stalePlayer = server->getPlayers()->getPlayer(loginXuid); - if (stalePlayer == nullptr && packet->m_onlineXuid != INVALID_XUID) - stalePlayer = server->getPlayers()->getPlayer(packet->m_onlineXuid); - - if (stalePlayer != nullptr && stalePlayer->connection != nullptr) - { - BYTE oldSmallId = 0; - if (stalePlayer->connection->connection != nullptr && stalePlayer->connection->connection->getSocket() != nullptr) - oldSmallId = stalePlayer->connection->connection->getSocket()->getSmallId(); - app.DebugPrintf("RECONNECT: Force-disconnecting old player smallId=%d\n", oldSmallId); - stalePlayer->connection->disconnect(DisconnectPacket::eDisconnect_Closed); - - // Queue the old SmallId for recycling so it's not permanently leaked. - // PlayerList::tick() will call PushFreeSmallId/ClearSocketForSmallId. - if (oldSmallId != 0) - server->getPlayers()->queueSmallIdForRecycle(oldSmallId); - - app.DebugPrintf("RECONNECT: Old player force-disconnect complete\n"); - } - - // Accept the login now that the old entry is removed. - app.DebugPrintf("RECONNECT: Calling handleAcceptedLogin for new connection\n"); - handleAcceptedLogin(packet); - app.DebugPrintf("RECONNECT: handleAcceptedLogin complete\n"); + // Reject the incoming connection — a player with this UID is already + // on the server. Allowing duplicates causes invisible players and + // other undefined behaviour. + app.DebugPrintf("LOGIN: Rejecting duplicate xuid for name: %ls\n", name.c_str()); + disconnect(DisconnectPacket::eDisconnect_Banned); } #ifdef _WINDOWS64 else if (g_bRejectDuplicateNames) diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 8e056cd81..d9915cf61 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -1588,13 +1588,13 @@ void PlayerConnection::handleCraftItem(shared_ptr packet) if(iRecipe == -1) return; + int recipeCount = (int)Recipes::getInstance()->getRecipies()->size(); + if(iRecipe < 0 || iRecipe >= recipeCount) + return; + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); shared_ptr pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); - size_t recipeCount = Recipes::getInstance()->getRecipies()->size(); - if (iRecipe < 0 || iRecipe >= (int)recipeCount) - return; - if(app.DebugSettingsOn() && (player->GetDebugOptions()&(1L<onCraftedBy(player->level, dynamic_pointer_cast( player->shared_from_this() ), pTempItemInst->count ); diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp index d322d9b6d..a2596911d 100644 --- a/Minecraft.Client/ServerLevel.cpp +++ b/Minecraft.Client/ServerLevel.cpp @@ -1077,7 +1077,12 @@ void ServerLevel::entityRemoved(shared_ptr e) shared_ptr ServerLevel::getEntity(int id) { - return entitiesById[id]; + auto it = entitiesById.find(id); + if (it != entitiesById.end()) + { + return it->second; + } + return nullptr; } bool ServerLevel::addGlobalEntity(shared_ptr e) diff --git a/Minecraft.Client/Windows64/Iggy/gdraw/gdraw_d3d1x_shared.inl b/Minecraft.Client/Windows64/Iggy/gdraw/gdraw_d3d1x_shared.inl index 0722308ac..62e97e0fe 100644 --- a/Minecraft.Client/Windows64/Iggy/gdraw/gdraw_d3d1x_shared.inl +++ b/Minecraft.Client/Windows64/Iggy/gdraw/gdraw_d3d1x_shared.inl @@ -998,11 +998,7 @@ static void RADLINK gdraw_SetViewSizeAndWorldScale(S32 w, S32 h, F32 scalex, F32 { static S32 s_lastW = 0, s_lastH = 0; static F32 s_lastSx = 0, s_lastSy = 0; - if (w != s_lastW || h != s_lastH || scalex != s_lastSx || scaley != s_lastSy) { - app.DebugPrintf("[GDRAW] SetViewSize: fw=%d fh=%d scale=%.6f,%.6f frametex=%dx%d vx=%d vy=%d\n", - w, h, scalex, scaley, gdraw->frametex_width, gdraw->frametex_height, gdraw->vx, gdraw->vy); - s_lastW = w; s_lastH = h; s_lastSx = scalex; s_lastSy = scaley; - } + memset(gdraw->frame, 0, sizeof(gdraw->frame)); gdraw->cur = gdraw->frame; gdraw->fw = w; diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index e82118cd0..28d295049 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -404,7 +404,7 @@ bool WinsockNetLayer::JoinGame(const char* ip, int port) bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void* data, int dataSize) { - if (sock == INVALID_SOCKET || dataSize <= 0) return false; + if (sock == INVALID_SOCKET || dataSize <= 0 || dataSize > WIN64_NET_MAX_PACKET_SIZE) return false; // TODO: s_sendLock is a single global lock for ALL sockets. If one client's // send() blocks (TCP window full, slow WiFi), every other write thread stalls diff --git a/Minecraft.Client/Windows64/PostProcesser.cpp b/Minecraft.Client/Windows64/PostProcesser.cpp index 4c55d3d98..aabb1f679 100644 --- a/Minecraft.Client/Windows64/PostProcesser.cpp +++ b/Minecraft.Client/Windows64/PostProcesser.cpp @@ -21,13 +21,17 @@ const char* PostProcesser::g_gammaPSCode = "cbuffer GammaCB : register(b0)\n" "{\n" " float gamma;\n" - " float3 pad;\n" + " float _pad;\n" + " float2 uvOffset;\n" + " float2 uvScale;\n" + " float2 _pad2;\n" "};\n" "Texture2D sceneTex : register(t0);\n" "SamplerState sceneSampler : register(s0);\n" "float4 main(float4 pos : SV_Position, float2 uv : TEXCOORD0) : SV_Target\n" "{\n" - " float4 color = sceneTex.Sample(sceneSampler, uv);\n" + " float2 texUV = uvOffset + uv * uvScale;\n" + " float4 color = sceneTex.Sample(sceneSampler, texUV);\n" "\n" " color.rgb = max(color.rgb, 0.0);\n" "\n" @@ -158,7 +162,7 @@ void PostProcesser::Init() cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - GammaCBData initData = {1.0f, {0, 0, 0}}; + GammaCBData initData = {1.0f, 0, 0.0f, 0.0f, 1.0f, 1.0f, {0, 0}}; D3D11_SUBRESOURCE_DATA srData; srData.pSysMem = &initData; srData.SysMemPitch = 0; @@ -237,6 +241,10 @@ void PostProcesser::Apply() const { GammaCBData *cb = static_cast(mapped.pData); cb->gamma = m_gamma; + cb->uvOffsetX = 0.0f; + cb->uvOffsetY = 0.0f; + cb->uvScaleX = 1.0f; + cb->uvScaleY = 1.0f; ctx->Unmap(m_pGammaCB, 0); } @@ -333,27 +341,6 @@ void PostProcesser::ApplyFromCopied() const ID3D11DeviceContext* ctx = g_pImmediateContext; - D3D11_MAPPED_SUBRESOURCE mapped; - const D3D11_MAP mapType = m_wineMode ? D3D11_MAP_WRITE_NO_OVERWRITE : D3D11_MAP_WRITE_DISCARD; - const HRESULT hr = ctx->Map(m_pGammaCB, 0, mapType, 0, &mapped); - if (SUCCEEDED(hr)) - { - const auto cb = static_cast(mapped.pData); - cb->gamma = m_gamma; - ctx->Unmap(m_pGammaCB, 0); - } - - ID3D11RenderTargetView* oldRTV = nullptr; - ID3D11DepthStencilView* oldDSV = nullptr; - ctx->OMGetRenderTargets(1, &oldRTV, &oldDSV); - - UINT numViewports = 1; - D3D11_VIEWPORT oldViewport = {}; - ctx->RSGetViewports(&numViewports, &oldViewport); - - ID3D11RenderTargetView* bbRTV = g_pRenderTargetView; - ctx->OMSetRenderTargets(1, &bbRTV, nullptr); - D3D11_VIEWPORT vp; if (m_useCustomViewport) { @@ -369,6 +356,33 @@ void PostProcesser::ApplyFromCopied() const vp.TopLeftY = 0; } + D3D11_MAPPED_SUBRESOURCE mapped; + const D3D11_MAP mapType = m_wineMode ? D3D11_MAP_WRITE_NO_OVERWRITE : D3D11_MAP_WRITE_DISCARD; + const HRESULT hr = ctx->Map(m_pGammaCB, 0, mapType, 0, &mapped); + if (SUCCEEDED(hr)) + { + const auto cb = static_cast(mapped.pData); + cb->gamma = m_gamma; + const float texW = static_cast(m_gammaTexWidth); + const float texH = static_cast(m_gammaTexHeight); + cb->uvOffsetX = vp.TopLeftX / texW; + cb->uvOffsetY = vp.TopLeftY / texH; + cb->uvScaleX = vp.Width / texW; + cb->uvScaleY = vp.Height / texH; + ctx->Unmap(m_pGammaCB, 0); + } + + ID3D11RenderTargetView* oldRTV = nullptr; + ID3D11DepthStencilView* oldDSV = nullptr; + ctx->OMGetRenderTargets(1, &oldRTV, &oldDSV); + + UINT numViewports = 1; + D3D11_VIEWPORT oldViewport = {}; + ctx->RSGetViewports(&numViewports, &oldViewport); + + ID3D11RenderTargetView* bbRTV = g_pRenderTargetView; + ctx->OMSetRenderTargets(1, &bbRTV, nullptr); + ctx->RSSetViewports(1, &vp); ctx->IASetInputLayout(nullptr); diff --git a/Minecraft.Client/prebuild.ps1 b/Minecraft.Client/prebuild.ps1 index fa2c487c5..0acbf023b 100644 --- a/Minecraft.Client/prebuild.ps1 +++ b/Minecraft.Client/prebuild.ps1 @@ -1,5 +1,18 @@ $sha = (git rev-parse --short=7 HEAD) -$ref = (git symbolic-ref --short HEAD) + +if ($env:GITHUB_REPOSITORY) { + $ref = "$env:GITHUB_REPOSITORY/$(git symbolic-ref --short HEAD)" +} else { + $remoteUrl = (git remote get-url origin) + # handle github urls only, can't predict other origins behavior + if ($remoteUrl -match '(?:github\.com[:/])([^/:]+/[^/]+?)(?:\.git)?$') { + $ref = "$($matches[1])/$(git symbolic-ref --short HEAD)" + }else{ + # fallback to just symbolic ref in case remote isnt what we expect + $ref = "UNKNOWN/$(git symbolic-ref --short HEAD)" + } +} + $build = 560 # Note: Build/network has to stay static for now, as without it builds wont be able to play together. We can change it later when we have a better versioning scheme in place. $suffix = "" @@ -18,7 +31,8 @@ if (git status --porcelain) { #pragma once #define VER_PRODUCTBUILD $build -#define VER_PRODUCTVERSION_STR_W L"$sha$suffix ($ref)" +#define VER_PRODUCTVERSION_STR_W L"$sha$suffix" #define VER_FILEVERSION_STR_W VER_PRODUCTVERSION_STR_W +#define VER_BRANCHVERSION_STR_W L"$ref" #define VER_NETWORK VER_PRODUCTBUILD "@ | Set-Content "Common/BuildVer.h" diff --git a/Minecraft.World/AbstractContainerMenu.cpp b/Minecraft.World/AbstractContainerMenu.cpp index 10d8afdcc..c98fc22c5 100644 --- a/Minecraft.World/AbstractContainerMenu.cpp +++ b/Minecraft.World/AbstractContainerMenu.cpp @@ -157,6 +157,9 @@ shared_ptr AbstractContainerMenu::clicked(int slotIndex, int butto shared_ptr clickedEntity = nullptr; shared_ptr inventory = player->inventory; + if (slotIndex < 0 || slotIndex >= (int)slots.size()) + return nullptr; + if (clickType == CLICK_QUICK_CRAFT) { int expectedStatus = quickcraftStatus; @@ -558,12 +561,13 @@ bool AbstractContainerMenu::isPauseScreen() void AbstractContainerMenu::setItem(unsigned int slot, shared_ptr item) { + if (slot >= slots.size()) return; getSlot(slot)->set(item); } void AbstractContainerMenu::setAll(ItemInstanceArray *items) { - for (unsigned int i = 0; i < items->length; i++) + for (unsigned int i = 0; i < items->length && i < slots.size(); i++) { getSlot(i)->set( (*items)[i] ); } diff --git a/Minecraft.World/AwardStatPacket.cpp b/Minecraft.World/AwardStatPacket.cpp index 07888541f..b2950a0ef 100644 --- a/Minecraft.World/AwardStatPacket.cpp +++ b/Minecraft.World/AwardStatPacket.cpp @@ -47,7 +47,7 @@ void AwardStatPacket::read(DataInputStream *dis) //throws IOException // Read parameter blob. int length = dis->readInt(); - if(length > 0) + if (length > 0 && length <= 65536) { m_paramData = byteArray(length); dis->readFully(m_paramData); diff --git a/Minecraft.World/BlockRegionUpdatePacket.cpp b/Minecraft.World/BlockRegionUpdatePacket.cpp index 730ce3edc..b91cf4f82 100644 --- a/Minecraft.World/BlockRegionUpdatePacket.cpp +++ b/Minecraft.World/BlockRegionUpdatePacket.cpp @@ -105,6 +105,12 @@ void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException levelIdx = ( size >> 30 ) & 3; size &= 0x3fffffff; + const int MAX_COMPRESSED_CHUNK_SIZE = 5 * 1024 * 1024; + if (size < 0 || size > MAX_COMPRESSED_CHUNK_SIZE) + { + size = 0; + } + if(size == 0) { buffer = byteArray(); diff --git a/Minecraft.World/Boat.cpp b/Minecraft.World/Boat.cpp index a470936ab..60d0c807e 100644 --- a/Minecraft.World/Boat.cpp +++ b/Minecraft.World/Boat.cpp @@ -27,7 +27,7 @@ void Boat::_init() blocksBuilding = true; setSize(1.5f, 0.6f); - heightOffset = bbHeight / 2.0f; + heightOffset = (bbHeight / 2.0f) + 0.2f; // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that // the derived version of the function is called @@ -194,7 +194,7 @@ void Boat::tick() yo = y; zo = z; - + // Check how much of the boat is in water int steps = 5; double waterPercentage = 0; for (int i = 0; i < steps; i++) @@ -208,33 +208,14 @@ void Boat::tick() } } + // Create particles double lastSpeed = sqrt(xd * xd + zd * zd); - if (lastSpeed > MAX_COLLISION_SPEED) + if (lastSpeed > MAX_COLLISION_SPEED && waterPercentage > 0) { - double xa = cos(yRot * PI / 180); - double za = sin(yRot * PI / 180); - - for (int i = 0; i < 1 + lastSpeed * 60; i++) - { - - double side = (random->nextFloat() * 2 - 1); - - double side2 = (random->nextInt(2) * 2 - 1) * 0.7; - if (random->nextBoolean()) - { - double xx = x - xa * side * 0.8 + za * side2; - double zz = z - za * side * 0.8 - xa * side2; - level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd); - } - else - { - double xx = x + xa + za * side * 0.7; - double zz = z + za - xa * side * 0.7; - level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd); - } - } + createSplash(lastSpeed); } + // Interpolation if (level->isClientSide && doLerp) { if (lSteps > 0) @@ -254,7 +235,6 @@ void Boat::tick() } else { -#if 1 // Original //double xt = x + xd; //double yt = y + yd; @@ -273,51 +253,25 @@ void Boat::tick() xd *= 0.99f; yd *= 0.95f; zd *= 0.99f; -#else - // 4J Stu - Fix for #8280 - Gameplay : Boats behave erratically when exited next to land. - // The client shouldn't change the position of the boat - double xt = x;// + xd; - double yt = y + yd; - double zt = z;// + zd; - this->setPos(xt, yt, zt); - - // 4J Stu - Fix for #9579 - GAMEPLAY: Boats with a player in them slowly sink under the water over time, and with no player in them they float into the sky. - // Just make the boats bob up and down rather than any other client-side movement when not receiving packets from server - if (waterPercentage > 0) - { - double bob = waterPercentage * 2 - 1; - yd += 0.04f * bob; - } - else - { - if (yd < 0) yd /= 2; - yd += 0.007f; - } - //if (onGround) - //{ - xd *= 0.5f; - yd *= 0.5f; - zd *= 0.5f; - //} - //xd *= 0.99f; - //yd *= 0.95f; - //zd *= 0.99f; -#endif } return; } + // Bob in water if (waterPercentage > 0) { double bob = waterPercentage * 2 - 1; yd += 0.04f * bob; } - else + + // Reimplement gravity again (??) + int tileUnder = level->getTile(Mth::floor(x), Mth::floor(y-0.15), Mth::floor(z)); + if (tileUnder == 0 && !onGround) { - yd = 0; + yd -= 0.04f; } - + // Rider controls if ( rider.lock() != nullptr && rider.lock()->instanceof(eTYPE_LIVINGENTITY) ) { shared_ptr livingRider = dynamic_pointer_cast(rider.lock()); @@ -354,6 +308,7 @@ void Boat::tick() if (acceleration < MIN_ACCELERATION) acceleration = MIN_ACCELERATION; } + // Slow on ground if (onGround) { xd *= 0.5f; @@ -362,6 +317,7 @@ void Boat::tick() } move(xd, yd, zd); + // Break boat on high speed collision if ((horizontalCollision && lastSpeed > 0.20)) { if (!level->isClientSide && !removed) @@ -401,6 +357,7 @@ void Boat::tick() yRot += (float) rotDiff; setRot(yRot, xRot); + // Server code after this if(level->isClientSide) return; vector > *entities = level->getEntities(shared_from_this(), bb->grow(0.2f, 0, 0.2f)); @@ -444,13 +401,38 @@ void Boat::tick() } } +void Boat::createSplash(double particleStrengh) { + double xa = cos(yRot * PI / 180); + double za = sin(yRot * PI / 180); + + for (int i = 0; i < 1 + particleStrengh * 60; i++) + { + double side = (random->nextFloat() * 2 - 1); + + double side2 = (random->nextInt(2) * 2 - 1) * 0.7; + if (random->nextBoolean()) + { + double xx = x - xa * side * 0.8 + za * side2; + double zz = z - za * side * 0.8 - xa * side2; + level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd); + } + else + { + double xx = x + xa + za * side * 0.7; + double zz = z + za - xa * side * 0.7; + level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd); + } + } +} + void Boat::positionRider() { if (rider.lock() == nullptr) return; double xa = cos(yRot * PI / 180) * 0.4; double za = sin(yRot * PI / 180) * 0.4; - rider.lock()->setPos(x + xa, y + getRideHeight() + rider.lock()->getRidingHeight(), z + za); + // We minus 0.4 to ensure that the player is not hovering above the boat. + rider.lock()->setPos(x + xa, y + getRideHeight() + rider.lock()->getRidingHeight()-0.4, z + za); } @@ -523,3 +505,4 @@ void Boat::setDoLerp(bool doLerp) { this->doLerp = doLerp; } + diff --git a/Minecraft.World/ByteArrayInputStream.cpp b/Minecraft.World/ByteArrayInputStream.cpp index d79eff362..29f6fc262 100644 --- a/Minecraft.World/ByteArrayInputStream.cpp +++ b/Minecraft.World/ByteArrayInputStream.cpp @@ -2,117 +2,141 @@ #include "InputOutputStream.h" -//Creates ByteArrayInputStream that uses buf as its buffer array. The initial value of pos is offset and -//the initial value of count is the minimum of offset+length and buf.length. The buffer array is not copied. -//The buffer's mark is set to the specified offset. -//Parameters: -//buf - the input buffer. -//offset - the offset in the buffer of the first byte to read. -//length - the maximum number of bytes to read from the buffer. +// Creates ByteArrayInputStream that uses buf as its buffer array. The initial value of pos is offset and +// the initial value of count is the minimum of offset+length and buf.length. The buffer array is not copied. +// The buffer's mark is set to the specified offset. +// Parameters: +// buf - the input buffer. +// offset - the offset in the buffer of the first byte to read. +// length - the maximum number of bytes to read from the buffer. ByteArrayInputStream::ByteArrayInputStream(byteArray buf, unsigned int offset, unsigned int length) - : pos( offset ), count( min( offset+length, buf.length ) ), mark( offset ) + : pos(offset), mark(offset) { - this->buf = buf; + if (offset > buf.length) + { + count = buf.length; + } + else if (length > buf.length - offset) + { + count = buf.length; + } + else + { + count = offset + length; + } + this->buf = buf; } -//Creates a ByteArrayInputStream so that it uses buf as its buffer array. The buffer array is not copied. -//The initial value of pos is 0 and the initial value of count is the length of buf. -//Parameters: -//buf - the input buffer. +// Creates a ByteArrayInputStream so that it uses buf as its buffer array. The buffer array is not copied. +// The initial value of pos is 0 and the initial value of count is the length of buf. +// Parameters: +// buf - the input buffer. ByteArrayInputStream::ByteArrayInputStream(byteArray buf) - : pos( 0 ), count( buf.length ), mark( 0 ) + : pos(0), count(buf.length), mark(0) { - this->buf = buf; + this->buf = buf; } -//Reads the next byte of data from this input stream. The value byte is returned as an int in the range 0 to 255. -//If no byte is available because the end of the stream has been reached, the value -1 is returned. -//This read method cannot block. -//Returns: -//the next byte of data, or -1 if the end of the stream has been reached. +// Reads the next byte of data from this input stream. The value byte is returned as an int in the range 0 to 255. +// If no byte is available because the end of the stream has been reached, the value -1 is returned. +// This read method cannot block. +// Returns: +// the next byte of data, or -1 if the end of the stream has been reached. int ByteArrayInputStream::read() { - if( pos >= count ) - return -1; - else - return buf[pos++]; + if (pos >= count) + { + return -1; + } + else + { + return buf[pos++]; + } } -//Reads some number of bytes from the input stream and stores them into the buffer array b. -//The number of bytes actually read is returned as an integer. This method blocks until input data is available, -//end of file is detected, or an exception is thrown. -//If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. -//If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, -//at least one byte is read and stored into b. +// Reads some number of bytes from the input stream and stores them into the buffer array b. +// The number of bytes actually read is returned as an integer. This method blocks until input data is available, +// end of file is detected, or an exception is thrown. +// If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. +// If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, +// at least one byte is read and stored into b. // -//The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, -//at most, equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], -//leaving elements b[k] through b[b.length-1] unaffected. +// The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, +// at most, equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], +// leaving elements b[k] through b[b.length-1] unaffected. // -//The read(b) method for class InputStream has the same effect as: +// The read(b) method for class InputStream has the same effect as: // -// read(b, 0, b.length) -//Parameters: -//b - the buffer into which the data is read. -//Returns: -//the total number of bytes read into the buffer, or -1 is there is no more data because the end of the stream has been reached. +// read(b, 0, b.length) +// Parameters: +// b - the buffer into which the data is read. +// Returns: +// the total number of bytes read into the buffer, or -1 is there is no more data because the end of the stream has been reached. int ByteArrayInputStream::read(byteArray b) { - return read( b, 0, b.length ); + return read(b, 0, b.length); } -//Reads up to len bytes of data into an array of bytes from this input stream. If pos equals count, -//then -1 is returned to indicate end of file. Otherwise, the number k of bytes read is equal to the smaller of len and count-pos. -//If k is positive, then bytes buf[pos] through buf[pos+k-1] are copied into b[off] through b[off+k-1] in the manner -//performed by System.arraycopy. The value k is added into pos and k is returned. -//This read method cannot block. -//Parameters: -//b - the buffer into which the data is read. -//off - the start offset in the destination array b -//len - the maximum number of bytes read. -//Returns: -//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached. +// Reads up to len bytes of data into an array of bytes from this input stream. If pos equals count, +// then -1 is returned to indicate end of file. Otherwise, the number k of bytes read is equal to the smaller of len and count-pos. +// If k is positive, then bytes buf[pos] through buf[pos+k-1] are copied into b[off] through b[off+k-1] in the manner +// performed by System.arraycopy. The value k is added into pos and k is returned. +// This read method cannot block. +// Parameters: +// b - the buffer into which the data is read. +// off - the start offset in the destination array b +// len - the maximum number of bytes read. +// Returns: +// the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached. int ByteArrayInputStream::read(byteArray b, unsigned int offset, unsigned int length) { - if( pos == count ) - return -1; + if (pos == count) + { + return -1; + } - int k = min( length, count-pos ); - XMemCpy( &b[offset], &buf[pos], k ); - //std::copy( buf->data+pos, buf->data+pos+k, b->data + offset ); // Or this instead? + int k = min(length, count - pos); + XMemCpy(&b[offset], &buf[pos], k); + // std::copy( buf->data+pos, buf->data+pos+k, b->data + offset ); // Or this instead? - pos += k; + pos += k; - return k; + return k; } -//Closing a ByteArrayInputStream has no effect. -//The methods in this class can be called after the stream has been closed without generating an IOException. +// Closing a ByteArrayInputStream has no effect. +// The methods in this class can be called after the stream has been closed without generating an IOException. void ByteArrayInputStream::close() { - return; + return; } -//Skips n bytes of input from this input stream. Fewer bytes might be skipped if the end of the input stream is reached. The actual number k of bytes to be skipped is equal to the smaller of n and count-pos. The value k is added into pos and k is returned. -//Overrides: -//skip in class InputStream -//Parameters: -//n - the number of bytes to be skipped. -//Returns: -//the actual number of bytes skipped. -int64_t ByteArrayInputStream::skip(int64_t n) +// Skips n bytes of input from this input stream. Fewer bytes might be skipped if the end of the input stream is reached. The actual number k of bytes to be skipped is equal to the smaller of n and count-pos. The value k is added into pos and k is returned. +// Overrides: +// skip in class InputStream +// Parameters: +// n - the number of bytes to be skipped. +// Returns: +// the actual number of bytes skipped. +__int64 ByteArrayInputStream::skip(__int64 n) { - int newPos = pos + n; + int newPos = pos + n; - if(newPos > count) newPos = count; + if (newPos > count) + { + newPos = count; + } - int k = newPos - pos; - pos = newPos; + int k = newPos - pos; + pos = newPos; - return k; + return k; } ByteArrayInputStream::~ByteArrayInputStream() { - if(buf.data != nullptr) delete [] buf.data; -} \ No newline at end of file + if (buf.data != NULL) + { + delete[] buf.data; + } +} diff --git a/Minecraft.World/ByteArrayOutputStream.cpp b/Minecraft.World/ByteArrayOutputStream.cpp index a9f36e04a..9173941fc 100644 --- a/Minecraft.World/ByteArrayOutputStream.cpp +++ b/Minecraft.World/ByteArrayOutputStream.cpp @@ -5,76 +5,95 @@ // Creates a new byte array output stream. The buffer capacity is initially 32 bytes, though its size increases if necessary. ByteArrayOutputStream::ByteArrayOutputStream() { - count = 0; - buf = byteArray( 32 ); + count = 0; + buf = byteArray(32); } -//Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes. -//Parameters: -//size - the initial size. +// Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes. +// Parameters: +// size - the initial size. ByteArrayOutputStream::ByteArrayOutputStream(unsigned int size) { - count = 0; - buf = byteArray( size ); + count = 0; + buf = byteArray(size); } ByteArrayOutputStream::~ByteArrayOutputStream() { - if (buf.data != nullptr) - delete[] buf.data; + if (buf.data != NULL) + { + delete[] buf.data; + } } -//Writes the specified byte to this byte array output stream. -//Parameters: -//b - the byte to be written. +// Writes the specified byte to this byte array output stream. +// Parameters: +// b - the byte to be written. void ByteArrayOutputStream::write(unsigned int b) { - // If we will fill the buffer we need to make it bigger - if( count + 1 >= buf.length ) - buf.resize( buf.length * 2 ); + // If we will fill the buffer we need to make it bigger + if (count + 1 >= buf.length) + { + buf.resize(buf.length * 2); + } - buf[count] = static_cast(b); - count++; + buf[count] = (byte)b; + count++; } // Writes b.length bytes from the specified byte array to this output stream. -//The general contract for write(b) is that it should have exactly the same effect as the call write(b, 0, b.length). +// The general contract for write(b) is that it should have exactly the same effect as the call write(b, 0, b.length). void ByteArrayOutputStream::write(byteArray b) { - write(b, 0, b.length); + write(b, 0, b.length); } -//Writes len bytes from the specified byte array starting at offset off to this byte array output stream. -//Parameters: -//b - the data. -//off - the start offset in the data. -//len - the number of bytes to write. +// Writes len bytes from the specified byte array starting at offset off to this byte array output stream. +// Parameters: +// b - the data. +// off - the start offset in the data. +// len - the number of bytes to write. void ByteArrayOutputStream::write(byteArray b, unsigned int offset, unsigned int length) { - assert( b.length >= offset + length ); - // If we will fill the buffer we need to make it bigger - if( count + length >= buf.length ) - buf.resize( max( count + length + 1, buf.length * 2 ) ); + if (offset > b.length || length > b.length - offset) + { + return; + } - XMemCpy( &buf[count], &b[offset], length ); - //std::copy( b->data+offset, b->data+offset+length, buf->data + count ); // Or this instead? + if (length > 0xFFFFFFFF - count) + { + return; + } - count += length; + // If we will fill the buffer we need to make it bigger + if (count + length >= buf.length) + { + unsigned int newSize = (std::max)(count + length + 1, buf.length * 2); + if (newSize <= buf.length) + { + return; + } + buf.resize(newSize); + } + + XMemCpy(&buf[count], &b[offset], length); + + count += length; } -//Closing a ByteArrayOutputStream has no effect. -//The methods in this class can be called after the stream has been closed without generating an IOException. +// Closing a ByteArrayOutputStream has no effect. +// The methods in this class can be called after the stream has been closed without generating an IOException. void ByteArrayOutputStream::close() { } -//Creates a newly allocated byte array. Its size is the current size of this output stream and the valid contents of the buffer have been copied into it. -//Returns: -//the current contents of this output stream, as a byte array. +// Creates a newly allocated byte array. Its size is the current size of this output stream and the valid contents of the buffer have been copied into it. +// Returns: +// the current contents of this output stream, as a byte array. byteArray ByteArrayOutputStream::toByteArray() { - byteArray out(count); - memcpy(out.data,buf.data,count); - return out; -} \ No newline at end of file + byteArray out(count); + memcpy(out.data, buf.data, count); + return out; +} diff --git a/Minecraft.World/ByteArrayTag.h b/Minecraft.World/ByteArrayTag.h index 1d3e38755..4d53c63ea 100644 --- a/Minecraft.World/ByteArrayTag.h +++ b/Minecraft.World/ByteArrayTag.h @@ -21,6 +21,7 @@ public: void load(DataInput *dis, int tagDepth) { int length = dis->readInt(); + if (length < 0 || length > 2 * 1024 * 1024) length = 0; if ( data.data ) delete[] data.data; data = byteArray(length); diff --git a/Minecraft.World/ComplexItemDataPacket.cpp b/Minecraft.World/ComplexItemDataPacket.cpp index 38c39cb65..98eeb9dee 100644 --- a/Minecraft.World/ComplexItemDataPacket.cpp +++ b/Minecraft.World/ComplexItemDataPacket.cpp @@ -32,7 +32,12 @@ void ComplexItemDataPacket::read(DataInputStream *dis) //throws IOException itemType = dis->readShort(); itemId = dis->readShort(); - data = charArray(dis->readUnsignedShort() & 0xffff); + int dataLength = dis->readShort() & 0xffff; + if (dataLength > 32767) + { + dataLength = 0; + } + data = charArray(dataLength); dis->readFully(data); } diff --git a/Minecraft.World/CompoundTag.h b/Minecraft.World/CompoundTag.h index 5eaa2a05d..8f4d045df 100644 --- a/Minecraft.World/CompoundTag.h +++ b/Minecraft.World/CompoundTag.h @@ -42,10 +42,16 @@ public: } tags.clear(); Tag *tag; - while ((tag = Tag::readNamedTag(dis))->getId() != Tag::TAG_End) - { - tags[tag->getName()] = tag; - } + int tagCount = 0; + const int MAX_COMPOUND_TAGS = 10000; + while ((tag = Tag::readNamedTag(dis))->getId() != Tag::TAG_End) + { + tags[tag->getName()] = tag; + if (++tagCount >= MAX_COMPOUND_TAGS) + { + break; + } + } delete tag; } diff --git a/Minecraft.World/Connection.cpp b/Minecraft.World/Connection.cpp index 7f10dc4a7..d44502667 100644 --- a/Minecraft.World/Connection.cpp +++ b/Minecraft.World/Connection.cpp @@ -108,8 +108,8 @@ Connection::Connection(Socket *socket, const wstring& id, PacketListener *packet const char *szId = wstringtofilename(id); char readThreadName[256]; char writeThreadName[256]; - sprintf(readThreadName,"%s read\n",szId); - sprintf(writeThreadName,"%s write\n",szId); + sprintf_s(readThreadName, sizeof(readThreadName), "%.240s read\n", szId); + sprintf_s(writeThreadName, sizeof(writeThreadName), "%.240s write\n", szId); readThread = new C4JThread(runRead, static_cast(this), readThreadName, READ_STACK_SIZE); writeThread = new C4JThread(runWrite, this, writeThreadName, WRITE_STACK_SIZE); diff --git a/Minecraft.World/ContainerSetContentPacket.cpp b/Minecraft.World/ContainerSetContentPacket.cpp index ae76e0f05..28ce64e9c 100644 --- a/Minecraft.World/ContainerSetContentPacket.cpp +++ b/Minecraft.World/ContainerSetContentPacket.cpp @@ -32,6 +32,9 @@ void ContainerSetContentPacket::read(DataInputStream *dis) //throws IOException { containerId = dis->readByte(); int count = dis->readShort(); + + if (count < 0 || count > 256) count = 0; + items = ItemInstanceArray(count); for (int i = 0; i < count; i++) { diff --git a/Minecraft.World/ContainerSetSlotPacket.cpp b/Minecraft.World/ContainerSetSlotPacket.cpp index 6a0382442..de8d26804 100644 --- a/Minecraft.World/ContainerSetSlotPacket.cpp +++ b/Minecraft.World/ContainerSetSlotPacket.cpp @@ -35,7 +35,7 @@ void ContainerSetSlotPacket::read(DataInputStream *dis) //throws IOException // 4J Stu - TU-1 hotfix // Fix for #13142 - Holding down the A button on the furnace ingredient slot causes the UI to display incorrect item counts BYTE byteId = dis->readByte(); - containerId = *(char *)&byteId; + containerId = (char)(signed char)byteId; slot = dis->readShort(); item = readItem(dis); } diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp index 46fde5aaf..e86f01de0 100644 --- a/Minecraft.World/CustomPayloadPacket.cpp +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -43,7 +43,7 @@ void CustomPayloadPacket::read(DataInputStream *dis) identifier = readUtf(dis, 20); length = dis->readShort(); - if (length > 0 && length < Short::MAX_VALUE) + if (length > 0 && length <= Short::MAX_VALUE) { if(data.data != nullptr) { diff --git a/Minecraft.World/DataInputStream.cpp b/Minecraft.World/DataInputStream.cpp index 4e4f5cd1b..0cd21a7e9 100644 --- a/Minecraft.World/DataInputStream.cpp +++ b/Minecraft.World/DataInputStream.cpp @@ -303,6 +303,10 @@ wstring DataInputStream::readUTF() int b = stream->read(); unsigned short UTFLength = static_cast(((a & 0xff) << 8) | (b & 0xff)); + const unsigned short MAX_UTF_LENGTH = 32767; + if (UTFLength > MAX_UTF_LENGTH) + return outputString; + //// 4J Stu - I decided while writing DataOutputStream that we didn't need to bother using the UTF8 format //// used in the java libs, and just write in/out as wchar_t all the time diff --git a/Minecraft.World/ExplodePacket.cpp b/Minecraft.World/ExplodePacket.cpp index 07626161f..88bf00c69 100644 --- a/Minecraft.World/ExplodePacket.cpp +++ b/Minecraft.World/ExplodePacket.cpp @@ -52,6 +52,8 @@ void ExplodePacket::read(DataInputStream *dis) //throws IOException r = dis->readFloat(); int count = dis->readInt(); + if (count < 0 || count > 32768) count = 0; + int xp = static_cast(x); int yp = static_cast(y); int zp = static_cast(z); diff --git a/Minecraft.World/GameCommandPacket.cpp b/Minecraft.World/GameCommandPacket.cpp index 17c5316af..ada5b04cd 100644 --- a/Minecraft.World/GameCommandPacket.cpp +++ b/Minecraft.World/GameCommandPacket.cpp @@ -40,7 +40,7 @@ void GameCommandPacket::read(DataInputStream *dis) command = static_cast(dis->readInt()); length = dis->readShort(); - if (length > 0 && length < Short::MAX_VALUE) + if (length > 0 && length <= Short::MAX_VALUE) { if(data.data != nullptr) { diff --git a/Minecraft.World/IntArrayTag.h b/Minecraft.World/IntArrayTag.h index 61ca59d09..64a4d004c 100644 --- a/Minecraft.World/IntArrayTag.h +++ b/Minecraft.World/IntArrayTag.h @@ -35,6 +35,8 @@ public: void load(DataInput *dis, int tagDepth) { int length = dis->readInt(); + if (length < 0 || length > 65536) + length = 0; if ( data.data ) delete[] data.data; data = intArray(length); diff --git a/Minecraft.World/ListTag.h b/Minecraft.World/ListTag.h index 2a0fdebc3..f6e1c6a0a 100644 --- a/Minecraft.World/ListTag.h +++ b/Minecraft.World/ListTag.h @@ -26,21 +26,16 @@ public: void load(DataInput *dis, int tagDepth) { - if (tagDepth > MAX_DEPTH) - { -#ifndef _CONTENT_PACKAGE - printf("Tried to read NBT tag with too high complexity, depth > %d", MAX_DEPTH); - __debugbreak(); -#endif - return; - } type = dis->readByte(); int size = dis->readInt(); + if (size < 0 || size > MAX_DEPTH) + size = 0; list.clear(); for (int i = 0; i < size; i++) { Tag *tag = Tag::newTag(type, L""); + if (tag == nullptr) break; tag->load(dis, tagDepth); list.push_back(tag); } diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp index 129024b77..05bf932dc 100644 --- a/Minecraft.World/Packet.cpp +++ b/Minecraft.World/Packet.cpp @@ -267,8 +267,12 @@ void Packet::updatePacketStatsPIX() shared_ptr Packet::getPacket(int id) { - // 4J: Removed try/catch - return idToCreateMap[id](); + auto it = idToCreateMap.find(id); + if (it == idToCreateMap.end()) + { + return nullptr; + } + return it->second(); } void Packet::writeBytes(DataOutputStream *dataoutputstream, byteArray bytes) @@ -334,30 +338,11 @@ shared_ptr Packet::readPacket(DataInputStream *dis, bool isServer) // th if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end())) { - app.DebugPrintf("*** BAD PACKET ID %d (0x%02X) isServer=%d totalPacketsRead=%d\n", id, id, isServer ? 1 : 0, s_packetCount); - app.DebugPrintf("*** Last %d good packet IDs (oldest first): ", 8); - for (int dbg = 0; dbg < 8; dbg++) - { - int idx = (s_lastIdPos + dbg) % 8; - app.DebugPrintf("%d ", s_lastIds[idx]); - } - app.DebugPrintf("\n"); - // Dump the next 32 bytes from the stream to see what follows - app.DebugPrintf("*** Next bytes in stream: "); - for (int dbg = 0; dbg < 32; dbg++) - { - int b = dis->read(); - if (b == -1) { app.DebugPrintf("[EOS] "); break; } - app.DebugPrintf("%02X ", b); - } - app.DebugPrintf("\n"); - __debugbreak(); - assert(false); - // throw new IOException(wstring(L"Bad packet id ") + std::to_wstring(id)); + return nullptr; } packet = getPacket(id); - if (packet == nullptr) assert(false);//throw new IOException(wstring(L"Bad packet id ") + std::to_wstring(id)); + if (packet == nullptr) return nullptr;//throw new IOException(wstring(L"Bad packet id ") + std::to_wstring(id)); s_lastIds[s_lastIdPos] = id; s_lastIdPos = (s_lastIdPos + 1) % 8; @@ -418,11 +403,9 @@ wstring Packet::readUtf(DataInputStream *dis, int maxLength) // throws IOExcepti { short stringLength = dis->readShort(); - if (stringLength > maxLength) + if (stringLength > maxLength || stringLength <= 0) { - wstringstream stream; - stream << L"Received string length longer than maximum allowed (" << stringLength << " > " << maxLength << ")"; - assert(false); + return L""; // throw new IOException( stream.str() ); } if (stringLength < 0) @@ -531,7 +514,7 @@ shared_ptr Packet::readItem(DataInputStream *dis) { shared_ptr item = nullptr; int id = dis->readShort(); - if (id >= 0) + if (id >= 0 && id < 32000) // todo: should turn Item::ITEM_NUM_COUNT into a global define { int count = dis->readByte(); int damage = dis->readShort(); @@ -569,9 +552,16 @@ void Packet::writeItem(shared_ptr item, DataOutputStream *dos) CompoundTag *Packet::readNbt(DataInputStream *dis) { int size = dis->readShort(); - if (size < 0) return nullptr; + if (size <= 0) return nullptr; + + const int MAX_NBT_SIZE = 32767; + if (size > MAX_NBT_SIZE) return nullptr; byteArray buff(size); - dis->readFully(buff); + if (!dis->readFully(buff)) + { + delete [] buff.data; + return nullptr; + } CompoundTag *result = (CompoundTag *) NbtIo::decompress(buff); delete [] buff.data; return result; diff --git a/Minecraft.World/PreLoginPacket.cpp b/Minecraft.World/PreLoginPacket.cpp index aa6832cad..ddcfe1973 100644 --- a/Minecraft.World/PreLoginPacket.cpp +++ b/Minecraft.World/PreLoginPacket.cpp @@ -62,6 +62,7 @@ void PreLoginPacket::read(DataInputStream *dis) //throws IOException m_friendsOnlyBits = dis->readByte(); m_ugcPlayersVersion = dis->readInt(); m_dwPlayerCount = dis->readByte(); + if( m_dwPlayerCount > MINECRAFT_NET_MAX_PLAYERS ) m_dwPlayerCount = MINECRAFT_NET_MAX_PLAYERS; if( m_dwPlayerCount > 0 ) { m_playerXuids = new PlayerUID[m_dwPlayerCount]; @@ -74,6 +75,7 @@ void PreLoginPacket::read(DataInputStream *dis) //throws IOException { m_szUniqueSaveName[i]=dis->readByte(); } + // m_szUniqueSaveName[m_iSaveNameLen - 1] = 0; // LCEMP does this but I have no idea why, TODO: why? m_serverSettings = dis->readInt(); m_hostIndex = dis->readByte(); diff --git a/Minecraft.World/RemoveEntitiesPacket.cpp b/Minecraft.World/RemoveEntitiesPacket.cpp index 609cfa776..4c5e33131 100644 --- a/Minecraft.World/RemoveEntitiesPacket.cpp +++ b/Minecraft.World/RemoveEntitiesPacket.cpp @@ -21,7 +21,9 @@ RemoveEntitiesPacket::~RemoveEntitiesPacket() void RemoveEntitiesPacket::read(DataInputStream *dis) //throws IOException { - ids = intArray(dis->readByte()); + int count = dis->readByte(); + if(count < 0) count = 0; + ids = intArray(count); for(unsigned int i = 0; i < ids.length; ++i) { ids[i] = dis->readInt(); diff --git a/Minecraft.World/Socket.cpp b/Minecraft.World/Socket.cpp index 2d5b257d0..41ac3702f 100644 --- a/Minecraft.World/Socket.cpp +++ b/Minecraft.World/Socket.cpp @@ -141,6 +141,11 @@ void Socket::pushDataToQueue(const BYTE * pbData, DWORD dwDataSize, bool fromHos // dwDataSize, queueIdx, dwDataSize > 0 ? pbData[0] : 0, networkPlayerSmallId); EnterCriticalSection(&m_queueLockNetwork[queueIdx]); + if (m_queueNetwork[queueIdx].size() + dwDataSize > 2 * 1024 * 1024) + { + LeaveCriticalSection(&m_queueLockNetwork[queueIdx]); + return; + } for( unsigned int i = 0; i < dwDataSize; i++ ) { m_queueNetwork[queueIdx].push(*pbData++); diff --git a/Minecraft.World/SynchedEntityData.cpp b/Minecraft.World/SynchedEntityData.cpp index 69d419b0d..67b9597bb 100644 --- a/Minecraft.World/SynchedEntityData.cpp +++ b/Minecraft.World/SynchedEntityData.cpp @@ -342,7 +342,10 @@ vector > *SynchedEntityData::unpack(Data int currentHeader = input->readByte(); - while (currentHeader != EOF_MARKER) + int itemCount = 0; + const int MAX_ENTITY_DATA_ITEMS = 256; + + while (currentHeader != EOF_MARKER && itemCount < MAX_ENTITY_DATA_ITEMS) { if (result == nullptr) @@ -397,6 +400,7 @@ vector > *SynchedEntityData::unpack(Data break; } result->push_back(item); + itemCount++; currentHeader = input->readByte(); } diff --git a/Minecraft.World/Tag.cpp b/Minecraft.World/Tag.cpp index b594bb0e2..eafdc9e87 100644 --- a/Minecraft.World/Tag.cpp +++ b/Minecraft.World/Tag.cpp @@ -84,27 +84,56 @@ Tag *Tag::readNamedTag(DataInput *dis) Tag *Tag::readNamedTag(DataInput *dis, int tagDepth) { + static __declspec(thread) int depth = 0; + static __declspec(thread) int totalTagCount = 0; + + if (depth == 0) + { + totalTagCount = 0; + } + + depth++; + + if (depth > 256) + { + depth--; + return new EndTag(); + } + + totalTagCount++; + const int MAX_TOTAL_TAGS = 32768; + if (totalTagCount > MAX_TOTAL_TAGS) + { + depth--; + return new EndTag(); + } + byte type = dis->readByte(); - if (type == 0) return new EndTag(); + if (type == 0) { + depth--; + return new EndTag(); + } // 4J Stu - readByte can return -1, so if it's that then also mark as the end tag if(type == 255) { - app.DebugPrintf("readNamedTag read a type of 255\n"); -#ifndef _CONTENT_PACKAGE - __debugbreak(); -#endif + depth--; return new EndTag(); } wstring name = dis->readUTF();//new String(bytes, "UTF-8"); Tag *tag = newTag(type, name); + if (tag == nullptr) { + depth--; + return new EndTag(); + } // short length = dis.readShort(); // byte[] bytes = new byte[length]; // dis.readFully(bytes); tag->load(dis, tagDepth); + depth--; return tag; } diff --git a/Minecraft.World/TextureAndGeometryPacket.cpp b/Minecraft.World/TextureAndGeometryPacket.cpp index bf5eccdbb..1c920ee7a 100644 --- a/Minecraft.World/TextureAndGeometryPacket.cpp +++ b/Minecraft.World/TextureAndGeometryPacket.cpp @@ -121,7 +121,20 @@ void TextureAndGeometryPacket::read(DataInputStream *dis) //throws IOException { textureName = dis->readUTF(); dwSkinID = static_cast(dis->readInt()); - dwTextureBytes = static_cast(dis->readShort()); + + short rawTextureBytes = dis->readShort(); + if (rawTextureBytes <= 0) + { + dwTextureBytes = 0; + } + else + { + dwTextureBytes = (DWORD)(unsigned short)rawTextureBytes; + if (dwTextureBytes > 65536) + { + dwTextureBytes = 0; + } + } if(dwTextureBytes>0) { @@ -134,7 +147,19 @@ void TextureAndGeometryPacket::read(DataInputStream *dis) //throws IOException } uiAnimOverrideBitmask = dis->readInt(); - dwBoxC = static_cast(dis->readShort()); + short rawBoxC = dis->readShort(); + if (rawBoxC <= 0) + { + dwBoxC = 0; + } + else + { + dwBoxC = (DWORD)(unsigned short)rawBoxC; + if (dwBoxC > 256) + { + dwBoxC = 0; // sane limit for skin boxes + } + } if(dwBoxC>0) { diff --git a/Minecraft.World/TexturePacket.cpp b/Minecraft.World/TexturePacket.cpp index eadcb3edb..94c195a05 100644 --- a/Minecraft.World/TexturePacket.cpp +++ b/Minecraft.World/TexturePacket.cpp @@ -37,17 +37,26 @@ void TexturePacket::handle(PacketListener *listener) void TexturePacket::read(DataInputStream *dis) //throws IOException { textureName = dis->readUTF(); - dwBytes = static_cast(dis->readShort()); + short rawBytes = dis->readShort(); + if (rawBytes <= 0) + { + dwBytes = 0; + return; + } + dwBytes = (DWORD)(unsigned short)rawBytes; + if (dwBytes > 65536) + { + dwBytes = 0; + return; + } + + this->pbData= new BYTE [dwBytes]; - if(dwBytes>0) + for(DWORD i=0;ipbData= new BYTE [dwBytes]; - - for(DWORD i=0;ipbData[i] = dis->readByte(); - } + this->pbData[i] = dis->readByte(); } + } void TexturePacket::write(DataOutputStream *dos) //throws IOException diff --git a/Minecraft.World/ThreadName.cpp b/Minecraft.World/ThreadName.cpp index f41beb61d..1f63aaf00 100644 --- a/Minecraft.World/ThreadName.cpp +++ b/Minecraft.World/ThreadName.cpp @@ -22,9 +22,9 @@ void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName ) #if ( defined _WINDOWS64 | defined _DURANGO ) __try { - RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR *)&info ); - } - __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) + RaiseException(0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR *)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) { } #endif diff --git a/Minecraft.World/UpdateGameRuleProgressPacket.cpp b/Minecraft.World/UpdateGameRuleProgressPacket.cpp index d88560114..5bb1b3012 100644 --- a/Minecraft.World/UpdateGameRuleProgressPacket.cpp +++ b/Minecraft.World/UpdateGameRuleProgressPacket.cpp @@ -18,7 +18,7 @@ UpdateGameRuleProgressPacket::UpdateGameRuleProgressPacket(ConsoleGameRules::EGa m_auxValue = auxValue; m_dataTag = dataTag; - if(dataLength > 0) + if (dataLength > 0 && dataLength <= 65536) { m_data = byteArray(dataLength); memcpy(m_data.data,data,dataLength); diff --git a/Minecraft.World/compression.cpp b/Minecraft.World/compression.cpp index 8c2e51c08..4122922b9 100644 --- a/Minecraft.World/compression.cpp +++ b/Minecraft.World/compression.cpp @@ -237,9 +237,19 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz unsigned char *dynamicRleBuf = nullptr; HRESULT decompressResult; - if(*pDestSize > rleSize) + unsigned int safeRleSize = max(rleSize, *pDestSize); + + const unsigned int MAX_RLE_ALLOC = 16 * 1024 * 1024; // 16 MB + if (safeRleSize > MAX_RLE_ALLOC) + { + LeaveCriticalSection(&rleDecompressLock); + *pDestSize = 0; + return E_FAIL; + } + + if (safeRleSize > staticRleSize) { - rleSize = *pDestSize; + rleSize = safeRleSize; dynamicRleBuf = new unsigned char[rleSize]; decompressResult = Decompress(dynamicRleBuf, &rleSize, pSource, SrcSize); pucIn = (unsigned char *)dynamicRleBuf; @@ -263,7 +273,7 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz //unsigned char *pucIn = (unsigned char *)rleDecompressBuf; const unsigned char *pucEnd = pucIn + rleSize; unsigned char *pucOut = static_cast(pDestination); - const unsigned char *pucOutEnd = pucOut + *pDestSize; + unsigned char *pucOutEnd = pucOut + *pDestSize; while( pucIn != pucEnd ) { @@ -275,7 +285,11 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz if( count < 3 ) { count++; - if( pucOut + count > pucOutEnd ) break; + if (pucOut + count > pucOutEnd) + { + pucOut = pucOutEnd; + break; + } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = 255; @@ -286,7 +300,11 @@ HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSiz count++; if( pucIn >= pucEnd ) break; const unsigned char data = *pucIn++; - if( pucOut + count > pucOutEnd ) break; + if (pucOut + count > pucOutEnd) + { + pucOut = pucOutEnd; + break; + } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = data; @@ -317,7 +335,7 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, unsigned char *pucIn = static_cast(pSource); const unsigned char *pucEnd = pucIn + SrcSize; unsigned char *pucOut = static_cast(pDestination); - const unsigned char *pucOutEnd = pucOut + *pDestSize; + unsigned char *pucOutEnd = pucOut + *pDestSize; while( pucIn != pucEnd ) { @@ -329,7 +347,11 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, if( count < 3 ) { count++; - if( pucOut + count > pucOutEnd ) break; + if (pucOut + count > pucOutEnd) + { + pucOut = pucOutEnd; + break; + } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = 255; @@ -340,7 +362,11 @@ HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, count++; if( pucIn >= pucEnd ) break; const unsigned char data = *pucIn++; - if( pucOut + count > pucOutEnd ) break; + if (pucOut + count > pucOutEnd) + { + pucOut = pucOutEnd; + break; + } for( unsigned int i = 0; i < count; i++ ) { *pucOut++ = data;