13 Commits

Author SHA1 Message Date
ModMaker101
a24318eedc Memory leak fix: Make chunks unload properly (#1406)
* Fix chunk unload and cleanup logic, fixes #1347

* Applying formatting to code I edited 😝
2026-03-24 23:25:18 -05:00
Sylvessa
993052409a Fix XUID logging (#1395)
* pass invalid_xuid to other players

* actually more simple fix
2026-03-24 05:04:07 -04:00
Ayush Thoren
ed9cbae3f7 Fix initial cursor position for in-game UI elements (#1120)
Signed-off-by: Ayush Thoren <ayushthoren@gmail.com>
2026-03-23 21:06:20 -05:00
Sylvessa
daed75b8a1 make handleParticleEvent actually parse the particle type instead of hardcoding hearts (#1399) 2026-03-23 20:11:37 -05:00
rtm516
9e715cb3bc Fix render order of F3 debug screen (#1239) 2026-03-23 18:01:40 -05:00
Sylvessa
127465b0eb add advanced tooltips, F3+H combo, and handle settings (#1389) 2026-03-23 17:54:46 -05:00
Connor Beard
77433dbd86 Fix: CMP0057 policy was not set (#1367) 2026-03-22 23:27:42 -05:00
Lord Cambion
9a6d126ae1 Stained Glass Survival Integration & Crafting UI Fix (#1195)
* Added Stained Glass

i found out that stained glass  was not accessible in survival, then i  saw they disabled it in the code

* Grouping glass correctly in crafting table

I removed the #if/endif from the ClothDyeRecipes.cpp and added a different one in StructureRecipies.cpp
also changed the Tile definition giving it the same
setBaseItemTypeAndMaterial of stained glass to group it correctly inside the crafting table UI.
also aincremented the Vertical Slot for crafting table to include many more craftings in the same group
2026-03-22 21:15:02 -05:00
Revela
39e46751bf Add clipboard paste support to UIControl_TextInput and UIScene_Keyboard (#1298)
Previously paste only worked in the chat screen. Wire Screen::getClipboard() into the two remaining text input paths so Ctrl+V works for sign editing, seed entry, server IP/port, and world name fields.
2026-03-22 21:09:10 -05:00
Alezito2008
ebab3ec40f Clicking outside container now drops items (#1306)
Java Edition KBM input parity
2026-03-22 21:05:04 -05:00
Sylvessa
c1ce97f7be allow displaying item lore (#1384) 2026-03-22 20:50:06 -05:00
Iruka Wolf
603c6ba7cb Fix missing trapped chest textures in Natural Texture Pack (#1381)
Co-authored-by: Iruka-Wolf <17684713+Iruka-Wolf@users.noreply.github.com>
2026-03-22 20:49:14 -05:00
Sylvessa
b6e25415ca Remove redundant buffer in UIScene_SettingsGraphicsMenu.cpp (#1348) (#1380) 2026-03-22 18:37:59 -04:00
28 changed files with 345 additions and 174 deletions

View File

@@ -4036,6 +4036,8 @@ void ClientConnection::handleSetPlayerTeamPacket(shared_ptr<SetPlayerTeamPacket>
void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> packet)
{
ePARTICLE_TYPE particleId = (ePARTICLE_TYPE)Integer::parseInt(packet->getName());
for (int i = 0; i < packet->getCount(); i++)
{
double xVarience = random->nextGaussian() * packet->getXDist();
@@ -4045,10 +4047,6 @@ void ClientConnection::handleParticleEvent(shared_ptr<LevelParticlesPacket> pack
double ya = random->nextGaussian() * packet->getMaxSpeed();
double za = random->nextGaussian() * packet->getMaxSpeed();
// TODO: determine particle ID from name
assert(0);
ePARTICLE_TYPE particleId = eParticleType_heart;
level->addParticle(particleId, packet->getX() + xVarience, packet->getY() + yVarience, packet->getZ() + zVarience, xa, ya, za);
}
}

View File

@@ -9,6 +9,7 @@
#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h"
#include "..\..\MultiplayerLocalPlayer.h"
#include "..\..\Minecraft.h"
#include "..\..\Options.h"
#ifdef __ORBIS__
#include <pad.h>
@@ -16,8 +17,6 @@
#ifdef _WINDOWS64
#include "..\..\Windows64\KeyboardMouseInput.h"
SavedInventoryCursorPos g_savedInventoryCursorPos = { 0.0f, 0.0f, false };
#endif
IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu()
@@ -1677,7 +1676,13 @@ vector<HtmlString> *IUIScene_AbstractContainerMenu::GetItemDescription(Slot *slo
{
if(slot == nullptr) return nullptr;
vector<HtmlString> *lines = slot->getItem()->getHoverText(nullptr, false);
bool advanced = false;
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
{
if (pMinecraft->options)
advanced = pMinecraft->options->advancedTooltips;
}
vector<HtmlString> *lines = slot->getItem()->getHoverText(nullptr, advanced);
// Add rarity to first line
if (lines->size() > 0)

View File

@@ -1,15 +1,5 @@
#pragma once
#ifdef _WINDOWS64
struct SavedInventoryCursorPos
{
float x;
float y;
bool hasSavedPos;
};
extern SavedInventoryCursorPos g_savedInventoryCursorPos;
#endif
// Uncomment to enable tap input detection to jump 1 slot. Doesn't work particularly well yet, and I feel the system does not need it.
// Would probably be required if we decide to slow down the pointer movement.
// 4J Stu - There was a request to be able to navigate the scenes with the dpad, so I have used much of the TAP_DETECTION

View File

@@ -20,9 +20,9 @@ protected:
eGroupTab_Right
};
static const int m_iMaxHSlotC = 12;
static const int m_iMaxHCraftingSlotC = 10;
static const int m_iMaxVSlotC = 17;
static const int m_iMaxHSlotC = 40;
static const int m_iMaxHCraftingSlotC = 40;
static const int m_iMaxVSlotC = 99;
static const int m_iMaxDisplayedVSlotC = 3;
static const int m_iIngredients3x3SlotC = 9;
static const int m_iIngredients2x2SlotC = 4;

View File

@@ -4,6 +4,7 @@
#include "..\..\..\Minecraft.World\net.minecraft.world.item.h"
#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h"
#include "..\..\Minecraft.h"
#include "..\..\Options.h"
#include "..\..\MultiPlayerLocalPlayer.h"
#include "..\..\ClientConnection.h"
#include "IUIScene_TradingMenu.h"
@@ -368,7 +369,13 @@ void IUIScene_TradingMenu::setTradeItem(int index, shared_ptr<ItemInstance> item
vector<HtmlString> *IUIScene_TradingMenu::GetItemDescription(shared_ptr<ItemInstance> item)
{
vector<HtmlString> *lines = item->getHoverText(nullptr, false);
bool advanced = false;
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
{
if (pMinecraft->options)
advanced = pMinecraft->options->advancedTooltips;
}
vector<HtmlString> *lines = item->getHoverText(nullptr, advanced);
// Add rarity to first line
if (lines->size() > 0)

View File

@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIControl_TextInput.h"
#include "..\..\Screen.h"
UIControl_TextInput::UIControl_TextInput()
{
@@ -211,6 +212,31 @@ UIControl_TextInput::EDirectEditResult UIControl_TextInput::tickDirectEdit()
}
}
// Paste from clipboard
if (g_KBMInput.IsKeyPressed('V') && g_KBMInput.IsKeyDown(VK_CONTROL))
{
wstring pasted = Screen::getClipboard();
wstring sanitized;
sanitized.reserve(pasted.length());
for (wchar_t pc : pasted)
{
if (pc >= 0x20) // Keep printable characters
{
if (m_iCharLimit > 0 && (m_editBuffer.length() + sanitized.length()) >= (size_t)m_iCharLimit)
break;
sanitized += pc;
}
}
if (!sanitized.empty())
{
m_editBuffer.insert(m_iCursorPos, sanitized);
m_iCursorPos += (int)sanitized.length();
changed = true;
}
}
// Arrow keys, Home, End, Delete for cursor movement
if (g_KBMInput.IsKeyPressed(VK_LEFT) && m_iCursorPos > 0)
{

View File

@@ -41,10 +41,6 @@ void UIScene_AbstractContainerMenu::handleDestroy()
app.DebugPrintf("UIScene_AbstractContainerMenu::handleDestroy\n");
#ifdef _WINDOWS64
g_savedInventoryCursorPos.x = m_pointerPos.x;
g_savedInventoryCursorPos.y = m_pointerPos.y;
g_savedInventoryCursorPos.hasSavedPos = true;
g_KBMInput.SetScreenCursorHidden(false);
g_KBMInput.SetCursorHiddenForUI(false);
#endif
@@ -173,16 +169,16 @@ void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex)
m_pointerPos = vPointerPos;
#ifdef _WINDOWS64
if (g_savedInventoryCursorPos.hasSavedPos)
if ((iPad == 0) && g_KBMInput.IsKBMActive())
{
m_pointerPos.x = g_savedInventoryCursorPos.x;
m_pointerPos.y = g_savedInventoryCursorPos.y;
if (m_pointerPos.x < m_fPointerMinX) m_pointerPos.x = m_fPointerMinX;
if (m_pointerPos.x > m_fPointerMaxX) m_pointerPos.x = m_fPointerMaxX;
if (m_pointerPos.y < m_fPointerMinY) m_pointerPos.y = m_fPointerMinY;
if (m_pointerPos.y > m_fPointerMaxY) m_pointerPos.y = m_fPointerMaxY;
m_pointerPos.x = ((m_fPanelMinX + m_fPanelMaxX) * 0.5f) - m_fPointerImageOffsetX;
m_pointerPos.y = ((m_fPanelMinY + m_fPanelMaxY) * 0.5f) - m_fPointerImageOffsetY;
}
if (m_pointerPos.x < m_fPointerMinX) m_pointerPos.x = m_fPointerMinX;
if (m_pointerPos.x > m_fPointerMaxX) m_pointerPos.x = m_fPointerMaxX;
if (m_pointerPos.y < m_fPointerMinY) m_pointerPos.y = m_fPointerMinY;
if (m_pointerPos.y > m_fPointerMaxY) m_pointerPos.y = m_fPointerMaxY;
#endif
IggyEvent mouseEvent;

View File

@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIScene_Keyboard.h"
#include "..\..\Screen.h"
#ifdef _WINDOWS64
// Global buffer that stores the text entered in the native keyboard scene.
@@ -224,6 +225,38 @@ void UIScene_Keyboard::tick()
}
}
// Paste from clipboard
if (g_KBMInput.IsKeyPressed('V') && g_KBMInput.IsKeyDown(VK_CONTROL))
{
wstring pasted = Screen::getClipboard();
wstring sanitized;
sanitized.reserve(pasted.length());
for (wchar_t pc : pasted)
{
if (pc >= 0x20) // Keep printable characters
{
if (static_cast<int>(m_win64TextBuffer.length() + sanitized.length()) >= m_win64MaxChars)
break;
sanitized += pc;
}
}
if (!sanitized.empty())
{
if (m_bPCMode)
{
m_win64TextBuffer.insert(m_iCursorPos, sanitized);
m_iCursorPos += (int)sanitized.length();
}
else
{
m_win64TextBuffer += sanitized;
}
changed = true;
}
}
if (m_bPCMode)
{
// Arrow keys, Home, End, Delete for cursor movement

View File

@@ -222,9 +222,8 @@ void UIScene_SettingsGraphicsMenu::handleSliderMove(F64 sliderId, F64 currentVal
const int fovValue = sliderValueToFov(value);
pMinecraft->gameRenderer->SetFovVal(static_cast<float>(fovValue));
app.SetGameSettings(m_iPad, eGameSetting_FOV, value);
WCHAR tempString[256];
swprintf(tempString, 256, L"FOV: %d", fovValue);
m_sliderFOV.setLabel(tempString);
swprintf(TempString, 256, L"FOV: %d", fovValue);
m_sliderFOV.setLabel(TempString);
}
break;

View File

@@ -1070,111 +1070,146 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
lines.push_back(ClientConstants::VERSION_STRING);
lines.push_back(ClientConstants::BRANCH_STRING);
}
if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr)
{
lines.push_back(minecraft->fpsString);
lines.push_back(L"E: " + std::to_wstring(minecraft->level->getAllEntities().size()));
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());
// 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;
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(L"");
lines.push_back(L""); // Spacer
// Players block pos
int xBlockPos = Mth::floor(minecraft->player->x);
int yBlockPos = Mth::floor(minecraft->player->y);
int zBlockPos = Mth::floor(minecraft->player->z);
// Chunk player is in
int xChunkPos = xBlockPos >> 4;
int yChunkPos = yBlockPos >> 4;
int zChunkPos = zBlockPos >> 4;
// Players offset within the chunk
int xChunkOffset = xBlockPos & 15;
int yChunkOffset = yBlockPos & 15;
int zChunkOffset = zBlockPos & 15;
WCHAR posString[44];
// 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);
lines.push_back(L"XYZ: " + std::wstring(posString));
lines.push_back(L"Block: " + std::to_wstring(xBlockPos) + L" " + std::to_wstring(yBlockPos) + L" " + std::to_wstring(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;
const wchar_t* cardinals[] = { L"south", L"west", L"north", L"east" };
lines.push_back(L"Facing: " + std::wstring(cardinals[direction]) + 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 skyLight = chunkAt->getBrightness(LightLayer::Sky, xChunkOffset, yChunkOffset, zChunkOffset);
int blockLight = chunkAt->getBrightness(LightLayer::Block, xChunkOffset, yChunkOffset, zChunkOffset);
int maxLight = fmax(skyLight, blockLight);
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")");
}
}
lines.push_back(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());
lines.push_back(minecraft->gatherStats2());
lines.push_back(minecraft->gatherStats3());
}
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
if (minecraft->options->renderDebug && minecraft->player != nullptr && minecraft->level != nullptr && minecraft->level->dimension->id == 0)
{
wstring wfeature[eTerrainFeature_Count];
wfeature[eTerrainFeature_Stronghold] = L"Stronghold: ";
wfeature[eTerrainFeature_Mineshaft] = L"Mineshaft: ";
wfeature[eTerrainFeature_Village] = L"Village: ";
wfeature[eTerrainFeature_Ravine] = L"Ravine: ";
// maxW in font units: physical width divided by font scale
float maxW = (static_cast<float>(g_rScreenWidth) - debugLeft - 8) / fontScale;
float maxWForContent = maxW - static_cast<float>(font->width(L"..."));
bool truncated[eTerrainFeature_Count] = {};
for (size_t i = 0; i < app.m_vTerrainFeatures.size(); i++)
#ifdef _DEBUG // Only show terrain features in debug builds not release
// No point trying to render this when not in the overworld
if (minecraft->level->dimension->id == 0)
{
FEATURE_DATA *pFeatureData = app.m_vTerrainFeatures[i];
int type = pFeatureData->eTerrainFeature;
if (type < eTerrainFeature_Stronghold || type > eTerrainFeature_Ravine) continue;
if (truncated[type]) continue;
wstring itemInfo = L"[" + std::to_wstring(pFeatureData->x * 16) + L", " + std::to_wstring(pFeatureData->z * 16) + L"] ";
if (font->width(wfeature[type] + itemInfo) <= maxWForContent)
wfeature[type] += itemInfo;
else
wstring wfeature[eTerrainFeature_Count];
wfeature[eTerrainFeature_Stronghold] = L"Stronghold: ";
wfeature[eTerrainFeature_Mineshaft] = L"Mineshaft: ";
wfeature[eTerrainFeature_Village] = L"Village: ";
wfeature[eTerrainFeature_Ravine] = L"Ravine: ";
// maxW in font units: physical width divided by font scale
float maxW = (static_cast<float>(g_rScreenWidth) - debugLeft - 8) / fontScale;
float maxWForContent = maxW - static_cast<float>(font->width(L"..."));
bool truncated[eTerrainFeature_Count] = {};
for (size_t i = 0; i < app.m_vTerrainFeatures.size(); i++)
{
wfeature[type] += L"...";
truncated[type] = true;
FEATURE_DATA *pFeatureData = app.m_vTerrainFeatures[i];
int type = pFeatureData->eTerrainFeature;
if (type < eTerrainFeature_Stronghold || type > eTerrainFeature_Ravine) continue;
if (truncated[type]) continue;
wstring itemInfo = L"[" + std::to_wstring(pFeatureData->x * 16) + L", " + std::to_wstring(pFeatureData->z * 16) + L"] ";
if (font->width(wfeature[type] + itemInfo) <= maxWForContent)
{
wfeature[type] += itemInfo;
}
else
{
wfeature[type] += L"...";
truncated[type] = true;
}
}
lines.push_back(L""); // Spacer
for (int i = eTerrainFeature_Stronghold; i <= static_cast<int>(eTerrainFeature_Ravine); i++)
{
lines.push_back(wfeature[i]);
}
lines.push_back(L""); // Spacer
}
lines.push_back(L"");
for (int i = eTerrainFeature_Stronghold; i <= static_cast<int>(eTerrainFeature_Ravine); i++)
lines.push_back(wfeature[i]);
lines.push_back(L"");
}
#endif
}
// Disable the depth test so the text shows on top of the paperdoll
glDisable(GL_DEPTH_TEST);
// Loop through the lines and draw them all on screen
int yPos = debugTop;
for (const auto &line : lines)
{
@@ -1182,6 +1217,9 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse)
yPos += 10;
}
// Restore the depth test
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);

View File

@@ -139,19 +139,26 @@ bool MultiPlayerChunkCache::reallyHasChunk(int x, int z)
return hasData[idx];
}
void MultiPlayerChunkCache::drop(int x, int z)
void MultiPlayerChunkCache::drop(const int x, const int z)
{
// 4J Stu - We do want to drop any entities in the chunks, especially for the case when a player is dead as they will
// not get the RemoveEntity packet if an entity is removed.
LevelChunk *chunk = getChunk(x, z);
if (!chunk->isEmpty())
const int ix = x + XZOFFSET;
const int iz = z + XZOFFSET;
if ((ix < 0) || (ix >= XZSIZE)) return;
if ((iz < 0) || (iz >= XZSIZE)) return;
const int idx = ix * XZSIZE + iz;
LevelChunk* chunk = cache[idx];
if (chunk != nullptr && !chunk->isEmpty())
{
// Added parameter here specifies that we don't want to delete tile entities, as they won't get recreated unless they've got update packets
// The tile entities are in general only created on the client by virtue of the chunk rebuild
// Unload chunk but keep tile entities
chunk->unload(false);
// 4J - We just want to clear out the entities in the chunk, but everything else should be valid
chunk->loaded = true;
const auto it = std::find(loadedChunkList.begin(), loadedChunkList.end(), chunk);
if (it != loadedChunkList.end()) loadedChunkList.erase(it);
cache[idx] = nullptr;
hasData[idx] = false;
chunk->loaded = false;
}
}

View File

@@ -170,6 +170,7 @@ void Options::init()
particles = 0;
fov = 0;
gamma = 0;
advancedTooltips = false;
}
Options::Options(Minecraft *minecraft, File workingDirectory)
@@ -451,8 +452,9 @@ void Options::load()
if (cmds[0] == L"fancyGraphics") fancyGraphics = cmds[1]==L"true";
if (cmds[0] == L"ao") ambientOcclusion = cmds[1]==L"true";
if (cmds[0] == L"clouds") renderClouds = cmds[1]==L"true";
if (cmds[0] == L"skin") skin = cmds[1];
if (cmds[0] == L"lastServer") lastMpIp = cmds[1];
if (cmds[0] == L"advancedTooltips") advancedTooltips = cmds[1]==L"false";
if (cmds[0] == L"skin") skin = cmds[1];
if (cmds[0] == L"lastServer") lastMpIp = cmds[1];
for (int i = 0; i < keyMappings_length; i++)
{
@@ -508,7 +510,8 @@ void Options::save()
dos.writeChars(L"fancyGraphics:" + wstring(fancyGraphics ? L"true" : L"false"));
dos.writeChars(ambientOcclusion ? L"ao:true" : L"ao:false");
dos.writeChars(renderClouds ? L"clouds:true" : L"clouds:false");
dos.writeChars(L"skin:" + skin);
dos.writeChars(advancedTooltips ? L"advancedTooltips:true" : L"advancedTooltips:false");
dos.writeChars(L"skin:" + skin);
dos.writeChars(L"lastServer:" + lastMpIp);
for (int i = 0; i < keyMappings_length; i++)

View File

@@ -110,6 +110,7 @@ public:
int particles; // 0 is all, 1 is decreased and 2 is minimal
float fov;
float gamma;
bool advancedTooltips;
void init(); // 4J added
Options(Minecraft *minecraft, File workingDirectory);

View File

@@ -792,6 +792,14 @@ void PlayerChunkMap::setRadius(int newRadius)
int xc = static_cast<int>(player->x) >> 4;
int zc = static_cast<int>(player->z) >> 4;
for (auto it = addRequests.begin(); it != addRequests.end(); )
{
if (it->player == player)
it = addRequests.erase(it);
else
++it;
}
for (int x = xc - newRadius; x <= xc + newRadius; x++)
for (int z = zc - newRadius; z <= zc + newRadius; z++)
{
@@ -801,9 +809,26 @@ void PlayerChunkMap::setRadius(int newRadius)
getChunkAndAddPlayer(x, z, player);
}
}
// Remove chunks that are outside the new radius
for (int x = xc - radius; x <= xc + radius; x++)
{
for (int z = zc - radius; z <= zc + radius; z++)
{
if (x < xc - newRadius || x > xc + newRadius || z < zc - newRadius || z > zc + newRadius)
{
getChunkAndRemovePlayer(x, z, player);
}
}
}
}
}
if (newRadius < radius)
{
level->cache->dropAll();
}
assert(radius <= MAX_VIEW_DISTANCE);
assert(radius >= MIN_VIEW_DISTANCE);
this->radius = newRadius;

View File

@@ -1690,7 +1690,16 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
}
// AP added for Vita so the range can be increased once the level starts
void PlayerList::setViewDistance(int newViewDistance)
void PlayerList::setViewDistance(const int newViewDistance)
{
viewDistance = newViewDistance;
for (size_t i = 0; i < server->levels.length; i++)
{
ServerLevel* level = server->levels[i];
if (level != nullptr)
{
level->getChunkMap()->setRadius(newViewDistance);
}
}
}

View File

@@ -80,54 +80,31 @@ vector<LevelChunk *> *ServerChunkCache::getLoadedChunkList()
return &m_loadedChunkList;
}
void ServerChunkCache::drop(int x, int z)
void ServerChunkCache::drop(const int x, const int z)
{
// 4J - we're not dropping things anymore now that we have a fixed sized cache
#ifdef _LARGE_WORLDS
const int ix = x + XZOFFSET;
const int iz = z + XZOFFSET;
if ((ix < 0) || (ix >= XZSIZE)) return;
if ((iz < 0) || (iz >= XZSIZE)) return;
const int idx = ix * XZSIZE + iz;
LevelChunk* chunk = cache[idx];
bool canDrop = false;
// if (level->dimension->mayRespawn())
// {
// Pos *spawnPos = level->getSharedSpawnPos();
// int xd = x * 16 + 8 - spawnPos->x;
// int zd = z * 16 + 8 - spawnPos->z;
// delete spawnPos;
// int r = 128;
// if (xd < -r || xd > r || zd < -r || zd > r)
// {
// canDrop = true;
//}
// }
// else
if (chunk != nullptr)
{
canDrop = true;
}
if(canDrop)
{
int ix = x + XZOFFSET;
int iz = z + XZOFFSET;
// Check we're in range of the stored level
if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return;
if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return;
int idx = ix * XZSIZE + iz;
LevelChunk *chunk = cache[idx];
const auto it = std::find(m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk);
if (it != m_loadedChunkList.end()) m_loadedChunkList.erase(it);
if(chunk)
{
m_toDrop.push_back(chunk);
}
cache[idx] = nullptr;
chunk->loaded = false;
}
#endif
}
void ServerChunkCache::dropAll()
{
#ifdef _LARGE_WORLDS
for (LevelChunk *chunk : m_loadedChunkList)
{
drop(chunk->x, chunk->z);
}
#endif
}
}
// 4J - this is the original (and virtual) interface to create
@@ -957,6 +934,10 @@ bool ServerChunkCache::tick()
m_unloadedCache[idx] = chunk;
cache[idx] = nullptr;
}
else
{
continue;
}
}
m_toDrop.pop_front();
}

View File

@@ -653,11 +653,12 @@ shared_ptr<Packet> TrackedEntity::getAddEntityPacket()
PlayerUID xuid = INVALID_XUID;
PlayerUID OnlineXuid = INVALID_XUID;
if( player != nullptr )
{
xuid = player->getXuid();
OnlineXuid = player->getOnlineXuid();
}
// do not pass xuid/onlinxuid to cleints
//if( player != nullptr )
//{
// xuid = player->getXuid();
// OnlineXuid = player->getOnlineXuid();
//}
// 4J Added yHeadRotp param to fix #102563 - TU12: Content: Gameplay: When one of the Players is idle for a few minutes his head turns 180 degrees.
return std::make_shared<AddPlayerPacket>(player, xuid, OnlineXuid, xp, yp, zp, yRotp, xRotp, yHeadRotp);
}

View File

@@ -234,6 +234,13 @@ bool KeyboardMouseInput::IsKeyReleased(int vkCode) const
return false;
}
int KeyboardMouseInput::GetPressedKey() const
{
for (int i = 0; i < MAX_KEYS; ++i)
if (m_keyPressed[i]) return i;
return 0;
}
bool KeyboardMouseInput::IsMouseButtonDown(int button) const
{
if (button >= 0 && button < MAX_MOUSE_BUTTONS)

View File

@@ -56,6 +56,8 @@ public:
bool IsKeyPressed(int vkCode) const;
bool IsKeyReleased(int vkCode) const;
int GetPressedKey() const;
bool IsMouseButtonDown(int button) const;
bool IsMouseButtonPressed(int button) const;
bool IsMouseButtonReleased(int button) const;

View File

@@ -36,6 +36,7 @@
//#include "NetworkManager.h"
#include "..\..\Minecraft.Client\Tesselator.h"
#include "..\..\Minecraft.Client\Options.h"
#include "..\Gui.h"
#include "Sentient\SentientManager.h"
#include "..\..\Minecraft.World\IntCache.h"
#include "..\Textures.h"
@@ -107,6 +108,7 @@ int g_iScreenHeight = 1080;
// always matches the current window, even after a resize.
int g_rScreenWidth = 1920;
int g_rScreenHeight = 1080;
static bool f3ComboUsed = false;
float g_iAspectRatio = static_cast<float>(g_iScreenWidth) / g_iScreenHeight;
static bool g_bResizeReady = false;
@@ -1774,17 +1776,37 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
}
// F3 toggles onscreen debug info
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO))
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_INFO)) f3ComboUsed = false;
// f3 combo
if (g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_DEBUG_INFO))
{
if (const Minecraft* pMinecraft = Minecraft::GetInstance())
switch (g_KBMInput.GetPressedKey())
{
if (pMinecraft->options)
{
pMinecraft->options->renderDebug = !pMinecraft->options->renderDebug;
}
// advanced tooltips
case 'H':
if (pMinecraft->options && app.GetGameStarted())
{
pMinecraft->options->advancedTooltips = !pMinecraft->options->advancedTooltips;
pMinecraft->options->save();
const wstring msg = wstring(L"Advanced tooltips: ") + (pMinecraft->options->advancedTooltips ? L"shown" : L"hidden");
const int primaryPad = ProfileManager.GetPrimaryPad();
if (pMinecraft->gui) pMinecraft->gui->addMessage(msg, primaryPad);
f3ComboUsed = true;
}
break;
}
}
// no combo
if (g_KBMInput.IsKeyReleased(KeyboardMouseInput::KEY_DEBUG_INFO) && !f3ComboUsed)
if (pMinecraft->options)
pMinecraft->options->renderDebug = !pMinecraft->options->renderDebug;
#ifdef _DEBUG_MENUS_ENABLED
// F6 Open debug console
if (g_KBMInput.IsKeyPressed(KeyboardMouseInput::KEY_DEBUG_CONSOLE))

View File

@@ -157,7 +157,7 @@ shared_ptr<ItemInstance> AbstractContainerMenu::clicked(int slotIndex, int butto
shared_ptr<ItemInstance> clickedEntity = nullptr;
shared_ptr<Inventory> inventory = player->inventory;
if (slotIndex < 0 || slotIndex >= (int)slots.size())
if ((slotIndex < 0 && slotIndex != SLOT_CLICKED_OUTSIDE) || slotIndex >= (int)slots.size())
return nullptr;
if (clickType == CLICK_QUICK_CRAFT)

View File

@@ -21,20 +21,20 @@ void ClothDyeRecipes::addRecipes(Recipes *r)
L'#', new ItemInstance(Tile::clayHardened),
L'X', new ItemInstance(Item::dye_powder, 1, i),L'D');
#if 0
r->addShapedRecipy(new ItemInstance(Tile::stained_glass, 8, ColoredTile::getItemAuxValueForTileData(i)), //
L"sssczczg",
L"###",
L"#X#",
L"###",
L'#', new ItemInstance(Tile::glass),
L'X', new ItemInstance(Item::dye_powder, 1, i), L'D');
r->addShapedRecipy(new ItemInstance(Tile::stained_glass_pane, 16, i), //
L"ssczg",
L"###",
L"###",
L'#', new ItemInstance(Tile::stained_glass, 1, i), L'D');
#endif
//#if 0
// r->addShapedRecipy(new ItemInstance(Tile::stained_glass, 8, ColoredTile::getItemAuxValueForTileData(i)), //
// L"sssczczg",
// L"###",
// L"#X#",
// L"###",
// L'#', new ItemInstance(Tile::glass),
// L'X', new ItemInstance(Item::dye_powder, 1, i), L'D');
// r->addShapedRecipy(new ItemInstance(Tile::stained_glass_pane, 16, i), //
// L"ssczg",
// L"###",
// L"###",
// L'#', new ItemInstance(Tile::stained_glass, 1, i), L'D');
//#endif
}
// some dye recipes

View File

@@ -562,34 +562,27 @@ vector<HtmlString> *ItemInstance::getHoverText(shared_ptr<Player> player, bool a
title.italics = true;
}
// 4J: This is for showing aux values, not useful in console version
/*
if (advanced)
{
wstring suffix = L"";
if (title.length() > 0)
if (title.text.length() > 0)
{
title += L" (";
title.text += L" (";
suffix = L")";
}
wchar_t buf[64];
if (isStackedByData())
{
title += String.format("#%04d/%d%s", id, auxValue, suffix);
}
swprintf_s(buf, 64, L"#%04d/%d%s", id, auxValue, suffix.c_str());
else
{
title += String.format("#%04d%s", id, suffix);
}
swprintf_s(buf, 64, L"#%04d%s", id, suffix.c_str());
title.text += buf;
}
else if (!hasCustomHoverName() && id == Item::map_Id)
*/
/*if (!hasCustomHoverName() && id == Item::map_Id)
{
title.text += L" #" + std::to_wstring(auxValue);
}*/
}
lines->push_back(title);
@@ -615,7 +608,7 @@ vector<HtmlString> *ItemInstance::getHoverText(shared_ptr<Player> player, bool a
if (tag->contains(L"display"))
{
//CompoundTag *display = tag->getCompound(L"display");
CompoundTag *display = tag->getCompound(L"display");
//if (display->contains(L"color"))
//{
@@ -631,8 +624,7 @@ vector<HtmlString> *ItemInstance::getHoverText(shared_ptr<Player> player, bool a
// }
//}
// 4J: Lore isn't in use in game
/*if (display->contains(L"Lore"))
if (display->contains(L"Lore"))
{
ListTag<StringTag> *lore = (ListTag<StringTag> *) display->getList(L"Lore");
if (lore->size() > 0)
@@ -643,7 +635,7 @@ vector<HtmlString> *ItemInstance::getHoverText(shared_ptr<Player> player, bool a
lines->push_back(lore->get(i)->data);
}
}
}*/
}
}
}
@@ -674,7 +666,7 @@ vector<HtmlString> *ItemInstance::getHoverText(shared_ptr<Player> player, bool a
{
if (isDamaged())
{
wstring damageStr = L"Durability: LOCALISE " + std::to_wstring((getMaxDamage()) - getDamageValue()) + L" / " + std::to_wstring(getMaxDamage());
wstring damageStr = L"Durability: " + std::to_wstring((getMaxDamage()) - getDamageValue()) + L" / " + std::to_wstring(getMaxDamage());
lines->push_back(HtmlString(damageStr));
}
}

View File

@@ -299,6 +299,7 @@ Recipes::Recipes()
pClothDyeRecipes->addRecipes(this);
addShapedRecipy(new ItemInstance(Tile::snow, 1), //
L"sscig",
L"##", //

View File

@@ -115,6 +115,28 @@ void StructureRecipies::addRecipes(Recipes *r)
L'#', Tile::glass,
L'D');
// Stained Glass block + pane per color
for (int i = 0; i < 16; i++)
{
r->addShapedRecipy(new ItemInstance(Tile::stained_glass, 8, ColoredTile::getItemAuxValueForTileData(i)),
L"sssczczg",
L"###",
L"#X#",
L"###",
L'#', new ItemInstance(Tile::glass),
L'X', new ItemInstance(Item::dye_powder, 1, i),
L'D');
r->addShapedRecipy(new ItemInstance(Tile::stained_glass_pane, 16, ColoredTile::getItemAuxValueForTileData(i)),
L"ssczg",
L"###",
L"###",
L'#', new ItemInstance(Tile::stained_glass, 1, ColoredTile::getItemAuxValueForTileData(i)),
L'D');
}
r->addShapedRecipy(new ItemInstance(Tile::netherBrick, 1), //
L"sscig",

View File

@@ -371,7 +371,12 @@ void Tile::staticCtor()
Tile::ironFence = (new ThinFenceTile(101, L"iron_bars", L"iron_bars", Material::metal, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_fence, Item::eMaterial_iron)->setDestroyTime(5.0f)->setExplodeable(10)->setSoundType(SOUND_METAL)->setDescriptionId(IDS_TILE_IRON_FENCE)->setUseDescriptionId(IDS_DESC_IRON_FENCE);
Tile::thinGlass = (new ThinFenceTile(102, L"glass", L"glass_pane_top", Material::glass, false)) ->setDestroyTime(0.3f)->setSoundType(SOUND_GLASS)->setDescriptionId(IDS_TILE_THIN_GLASS)->setUseDescriptionId(IDS_DESC_THIN_GLASS);
Tile::thinGlass = (new ThinFenceTile(102, L"glass", L"glass_pane_top", Material::glass, false))
->setBaseItemTypeAndMaterial(Item::eBaseItemType_glass, Item::eMaterial_glass)
->setDestroyTime(0.3f)
->setSoundType(SOUND_GLASS)
->setDescriptionId(IDS_TILE_THIN_GLASS)
->setUseDescriptionId(IDS_DESC_THIN_GLASS);
Tile::melon = (new MelonTile(103)) ->setDestroyTime(1.0f)->setSoundType(SOUND_WOOD)->setIconName(L"melon")->setDescriptionId(IDS_TILE_MELON)->setUseDescriptionId(IDS_DESC_MELON_BLOCK);
Tile::pumpkinStem = (new StemTile(104, Tile::pumpkin)) ->setDestroyTime(0.0f)->setSoundType(SOUND_WOOD)->setIconName(L"pumpkin_stem")->setDescriptionId(IDS_TILE_PUMPKIN_STEM)->sendTileData();
Tile::melonStem = (new StemTile(105, Tile::melon)) ->setDestroyTime(0.0f)->setSoundType(SOUND_WOOD)->setIconName(L"melon_stem")->setDescriptionId(IDS_TILE_MELON_STEM)->sendTileData();

View File

@@ -3,6 +3,7 @@
# Required:
# COPY_SOURCE pipe-separated list of source file paths
# COPY_DEST destination directory
cmake_minimum_required(VERSION 3.24)
if(NOT COPY_SOURCE OR NOT COPY_DEST)
message(FATAL_ERROR "COPY_SOURCE and COPY_DEST must be set.")