11 Commits

Author SHA1 Message Date
Ayush Thoren
bb8ffee0d3 Fix mouse being pulled to the bottom right (#1156)
Signed-off-by: Ayush Thoren <ayushthoren@gmail.com>
2026-03-12 01:44:32 -05:00
ModMaker101
44fc8a4db2 Fix quiet in-game audio after engine update #897 (#1171) 2026-03-12 01:43:12 -05:00
Marvelco
1036b7368e Fixed DLC map loading / saving, missing chunks (#1114)
* fixed all DLC maps

* fixed old saves have overlapping chunks with the new system
2026-03-10 22:04:19 -05:00
Loki
a195ac7172 Only single topic PRs, please - CONTRIBUTING.md 2026-03-10 09:37:52 -05:00
la
d7596aa28c Fix issue where visually the HUD shows you have 0 hearts when you dont (#1089)
* Fix for issue where player is able to stay alive with zero hearts in their healthbar.

* use static cast over c style cast
2026-03-09 22:30:01 -05:00
eh-K
c998346312 FIX: Bonus Chests spawn again when loading back in. #982 (#992)
* Fixed bug where Bonus Chests would spawn again when loading back into it.

Fixes #982

Added a check for if the world is new.

Meaning no more additional chests if the world is loaded up again.

* Replace NULL with nullptr for chest check
2026-03-09 22:07:38 -05:00
Marlian
91ae76f132 Fix tamed horses despawning when player moves away (#1057)
Co-authored-by: MCbabel <MCbabel@users.noreply.github.com>
2026-03-09 22:06:38 -05:00
Us3ful"-Dev
c90a6bf5ab Fixed Enderman, monster aggro in creative (#1051)
Fixed endermans by making a invulnerable check
2026-03-09 22:05:56 -05:00
Alezito2008
5f777a7f45 Fix: Prevent clicking disabled checkboxes (#1075) 2026-03-09 22:03:39 -05:00
Marlian
3bcf588fbe Fix crash when loading saved tutorial worlds (#1001)
writeRuleFile() was missing the schematic file count integer before the schematic entries. The reader in readRuleFile() expected this count, causing a stream misalignment that led to an assertion failure (Unrecognised schematic version) when reloading a saved tutorial world.

The fix writes the count on save and adds backward-compatible reading that detects old saves (without count) via a peek heuristic and falls back to count-less parsing.

Co-authored-by: MCbabel <MCbabel@users.noreply.github.com>
2026-03-09 22:02:39 -05:00
Loki Rautio
58c236ead5 Disable git LFS
It was never even enabled properly to begin with
2026-03-09 20:49:50 -05:00
17 changed files with 182 additions and 43 deletions

8
.gitattributes vendored
View File

@@ -1,8 +0,0 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.binka filter=lfs diff=lfs merge=lfs -text
*.arc filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.ico filter=lfs diff=lfs merge=lfs -text

View File

@@ -46,6 +46,13 @@ However, we would accept changes that...
- Having workable multi-platform compilation for ARM, Consoles, Linux
- Being a good base for further expansion and modding of LCE, such as backports and "modpacks".
# Scope of PRs
All Pull Requests should fully document the changes they include in their file changes. They should also be limited to one general topic and not touch all over the codebase unless its justifiable.
For example, we would not accept a PR that reworks UI, multiplayer code, and furnace ticking even if its a "fixup" PR as its too difficult to review a ton of code changes that are all irrelevant from each other. However, a PR focused on adding a bunch of commands or fixes several crashes that are otherwise irrelevant to each other would be accepted.
If your PR includes any undocumented changes it will be closed.
# Use of AI and LLMs
We currently do not accept any new code into the project that was written largely, entirely, or even noticably by an LLM. All contributions should be made by humans that understand the codebase.

View File

@@ -260,9 +260,9 @@ void SoundEngine::updateMiniAudio()
continue;
}
float finalVolume = s->info.volume * m_MasterEffectsVolume;
if (finalVolume > 1.0f)
finalVolume = 1.0f;
float finalVolume = s->info.volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
if (finalVolume > SFX_MAX_GAIN)
finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, s->info.pitch);
@@ -557,10 +557,13 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, floa
}
ma_sound_set_spatialization_enabled(&s->sound, MA_TRUE);
ma_sound_set_min_distance(&s->sound, SFX_3D_MIN_DISTANCE);
ma_sound_set_max_distance(&s->sound, SFX_3D_MAX_DISTANCE);
ma_sound_set_rolloff(&s->sound, SFX_3D_ROLLOFF);
float finalVolume = volume * m_MasterEffectsVolume;
if (finalVolume > 1.0f)
finalVolume = 1.0f;
float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
if (finalVolume > SFX_MAX_GAIN)
finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, pitch);

View File

@@ -6,6 +6,12 @@ using namespace std;
#include "miniaudio.h"
constexpr float SFX_3D_MIN_DISTANCE = 1.0f;
constexpr float SFX_3D_MAX_DISTANCE = 16.0f;
constexpr float SFX_3D_ROLLOFF = 0.5f;
constexpr float SFX_VOLUME_MULTIPLIER = 1.5f;
constexpr float SFX_MAX_GAIN = 1.5f;
enum eMUSICFILES
{
eStream_Overworld_Calm1 = 0,

View File

@@ -344,6 +344,7 @@ void GameRuleManager::writeRuleFile(DataOutputStream *dos)
// Write schematic files.
unordered_map<wstring, ConsoleSchematicFile *> *files;
files = getLevelGenerationOptions()->getUnfinishedSchematicFiles();
dos->writeInt((int)files->size());
for ( auto& it : *files )
{
const wstring& filename = it.first;
@@ -497,17 +498,36 @@ bool GameRuleManager::readRuleFile(LevelGenerationOptions *lgo, byte *dIn, UINT
}*/
// subfile
// Old saves didn't write a numFiles count before the schematic entries.
// Detect this: a real count is small, but a UTF filename prefix reads as a large int.
UINT numFiles = contentDis->readInt();
for (UINT i = 0; i < numFiles; i++)
if (lgo->isFromSave() && numFiles > 100)
{
wstring sFilename = contentDis->readUTF();
int length = contentDis->readInt();
byteArray ba( length );
contentDis->read(ba);
levelGenerator->loadSchematicFile(sFilename, ba.data, ba.length);
contentDis->skip(-4);
while (true)
{
int peek = contentDis->readInt();
if (peek <= 100) { contentDis->skip(-4); break; }
contentDis->skip(-4);
wstring sFilename = contentDis->readUTF();
int length = contentDis->readInt();
byteArray ba( length );
contentDis->read(ba);
levelGenerator->loadSchematicFile(sFilename, ba.data, ba.length);
}
}
else
{
for (UINT i = 0; i < numFiles; i++)
{
wstring sFilename = contentDis->readUTF();
int length = contentDis->readInt();
byteArray ba( length );
contentDis->read(ba);
levelGenerator->loadSchematicFile(sFilename, ba.data, ba.length);
}
}
LEVEL_GEN_ID lgoID = LEVEL_GEN_ID_NULL;

View File

@@ -455,6 +455,74 @@ unordered_map<wstring, ConsoleSchematicFile *> *LevelGenerationOptions::getUnfin
void LevelGenerationOptions::loadBaseSaveData()
{
#ifdef _WINDOWS64
int gameRulesCount = m_parentDLCPack ? m_parentDLCPack->getDLCItemsCount(DLCManager::e_DLCType_GameRulesHeader) : 0;
wstring baseSave = getBaseSavePath();
wstring packName = baseSave.substr(0, baseSave.find(L'.'));
for (int i = 0; i < gameRulesCount; ++i)
{
DLCGameRulesHeader* dlcFile = static_cast<DLCGameRulesHeader*>(m_parentDLCPack->getFile(DLCManager::e_DLCType_GameRulesHeader, i));
if (!dlcFile->getGrfPath().empty())
{
File grf(L"Windows64Media\\DLC\\" + packName + L"\\Data\\" + dlcFile->getGrfPath());
if (grf.exists())
{
wstring path = grf.getPath();
HANDLE fileHandle = CreateFileW(path.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (fileHandle != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize = grf.length();
DWORD bytesRead;
PBYTE pbData = new BYTE[dwFileSize];
BOOL bSuccess = ReadFile(fileHandle, pbData, dwFileSize, &bytesRead, nullptr);
CloseHandle(fileHandle);
if (bSuccess)
{
dlcFile->setGrfData(pbData, dwFileSize, m_stringTable);
app.m_gameRules.setLevelGenerationOptions(dlcFile->lgo);
}
delete[] pbData;
}
}
}
}
if (requiresBaseSave() && !getBaseSavePath().empty())
{
File save(L"Windows64Media\\DLC\\" + packName + L"\\Data\\" + baseSave);
if (save.exists())
{
wstring path = save.getPath();
HANDLE fileHandle = CreateFileW(path.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (fileHandle != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize = GetFileSize(fileHandle, nullptr);
DWORD bytesRead;
PBYTE pbData = new BYTE[dwFileSize];
BOOL bSuccess = ReadFile(fileHandle, pbData, dwFileSize, &bytesRead, nullptr);
CloseHandle(fileHandle);
if (bSuccess)
setBaseSaveData(pbData, dwFileSize);
else
delete[] pbData;
}
}
}
setLoadedData();
app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ReloadTexturePack);
#else
int mountIndex = -1;
if(m_parentDLCPack != nullptr) mountIndex = m_parentDLCPack->GetDLCMountIndex();
@@ -481,6 +549,7 @@ void LevelGenerationOptions::loadBaseSaveData()
setLoadedData();
app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ReloadTexturePack);
}
#endif
}
int LevelGenerationOptions::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicenceMask)

View File

@@ -942,13 +942,18 @@ int CGameNetworkManager::ServerThreadProc( void* lpParameter )
app.SetGameHostOption(eGameHostOption_All,param->settings);
// 4J Stu - If we are loading a DLC save that's separate from the texture pack, load
if( param->levelGen != nullptr && (param->texturePackId == 0 || param->levelGen->getRequiredTexturePackId() != param->texturePackId) )
if (param != nullptr && param->levelGen != nullptr && param->levelGen->isFromDLC())
{
while((Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()))
{
Sleep(1);
}
param->levelGen->loadBaseSaveData();
while (!param->levelGen->hasLoadedData())
{
Sleep(1);
}
}
}

View File

@@ -1298,10 +1298,8 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
}
}
vPointerPos.x = floor(vPointerPos.x);
vPointerPos.x += ( static_cast<int>(vPointerPos.x)%2);
vPointerPos.y = floor(vPointerPos.y);
vPointerPos.y += ( static_cast<int>(vPointerPos.y)%2);
vPointerPos.x = static_cast<float>(floor(vPointerPos.x + 0.5f));
vPointerPos.y = static_cast<float>(floor(vPointerPos.y + 0.5f));
m_pointerPos = vPointerPos;
adjustPointerForSafeZone();

View File

@@ -195,8 +195,8 @@ void IUIScene_HUD::renderPlayerHealth()
// Update health
bool blink = pMinecraft->localplayers[iPad]->invulnerableTime / 3 % 2 == 1;
if (pMinecraft->localplayers[iPad]->invulnerableTime < 10) blink = false;
int currentHealth = pMinecraft->localplayers[iPad]->getHealth();
int oldHealth = pMinecraft->localplayers[iPad]->lastHealth;
int currentHealth = static_cast<int>(ceil(pMinecraft->localplayers[iPad]->getHealth()));
int oldHealth = static_cast<int>(ceil(pMinecraft->localplayers[iPad]->lastHealth));
bool bHasPoison = pMinecraft->localplayers[iPad]->hasEffect(MobEffect::poison);
bool bHasWither = pMinecraft->localplayers[iPad]->hasEffect(MobEffect::wither);
AttributeInstance *maxHealthAttribute = pMinecraft->localplayers[iPad]->getAttribute(SharedMonsterAttributes::MAX_HEALTH);

View File

@@ -578,9 +578,12 @@ bool UIScene::handleMouseClick(F32 x, F32 y)
if (bestCtrl->getControlType() == UIControl::eCheckBox)
{
UIControl_CheckBox *cb = static_cast<UIControl_CheckBox*>(bestCtrl);
bool newState = !cb->IsChecked();
cb->setChecked(newState);
handleCheckboxToggled((F64)bestId, newState);
if (cb->IsEnabled())
{
bool newState = !cb->IsChecked();
cb->setChecked(newState);
handleCheckboxToggled((F64)bestId, newState);
}
}
else
{

View File

@@ -937,7 +937,11 @@ bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring
storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(newFormatSave, File(L"."), name, true));
#else
storage = std::make_shared<McRegionLevelStorage>(new ConsoleSaveFileOriginal(L""), File(L"."), name, true);
ConsoleSaveFileOriginal* pSave = new ConsoleSaveFileOriginal(L"");
pSave->ConvertToLocalPlatform();
storage = std::make_shared<McRegionLevelStorage>(pSave, File(L"."), name, true);
#endif
}

View File

@@ -24,18 +24,21 @@ bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z)
bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z, bool force)
{
if( !force )
//Will only spawn a bonus chest if the world is new and has never been saved.
if (level->isNew)
{
int t = 0;
while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 1)
if( !force )
{
int t = 0;
while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 1)
y--;
if (y < 1)
{
return false;
if (y < 1)
{
return false;
}
y++;
}
y++;
}
for (int i = 0; i < 4; i++)
{
@@ -85,4 +88,6 @@ bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z,
}
return false;
}
}

View File

@@ -410,9 +410,14 @@ bool EnderMan::hurt(DamageSource *source, float damage)
if ( dynamic_cast<EntityDamageSource *>(source) != nullptr && source->getEntity()->instanceof(eTYPE_PLAYER))
{
aggroedByPlayer = true;
if (!dynamic_pointer_cast<Player>(source->getEntity())->abilities.invulnerable)
{
aggroedByPlayer = true;
}
else setCreepy(false);
}
if (dynamic_cast<IndirectEntityDamageSource *>(source) != nullptr)
{
aggroedByPlayer = false;

View File

@@ -515,6 +515,15 @@ bool EntityHorse::canSpawn()
return Animal::canSpawn();
}
bool EntityHorse::removeWhenFarAway()
{
if (isTamed()) return false;
if (isSaddled()) return false;
if (isLeashed()) return false;
if (getArmorType() > 0) return false;
return Animal::removeWhenFarAway();
}
shared_ptr<EntityHorse> EntityHorse::getClosestMommy(shared_ptr<Entity> baby, double searchRadius)
{

View File

@@ -192,6 +192,7 @@ private:
public:
virtual void containerChanged();
virtual bool canSpawn();
virtual bool removeWhenFarAway() override;
protected:
virtual shared_ptr<EntityHorse> getClosestMommy(shared_ptr<Entity> baby, double searchRadius);

View File

@@ -60,7 +60,14 @@ bool Monster::hurt(DamageSource *source, float dmg)
if (sourceEntity != shared_from_this())
{
attackTarget = sourceEntity;
if (sourceEntity->instanceof(eTYPE_PLAYER))
{
if (!dynamic_pointer_cast<Player>(sourceEntity)->abilities.invulnerable)
{
attackTarget = sourceEntity;
}
}
else attackTarget = sourceEntity;
}
return true;
}

View File

@@ -12,6 +12,11 @@ bool RegionFileCache::useSplitSaves(ESavePlatform platform)
case SAVE_FILE_PLATFORM_XBONE:
case SAVE_FILE_PLATFORM_PS4:
return true;
case SAVE_FILE_PLATFORM_WIN64:
{
LevelGenerationOptions* lgo = app.getLevelGenerationOptions();
return (lgo != nullptr && lgo->isFromDLC());
}
default:
return false;
};