diff --git a/.gitattributes b/.gitattributes index 6e34e0e28..eaccbc0e8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ +<<<<<<< main *.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text @@ -7,3 +8,5 @@ *.bin filter=lfs diff=lfs merge=lfs -text *.ico filter=lfs diff=lfs merge=lfs -text * !text !filter !merge !diff +======= +>>>>>>> main diff --git a/Minecraft.Client/Common/GameRules/GameRuleManager.cpp b/Minecraft.Client/Common/GameRules/GameRuleManager.cpp index ff294a650..95434c08c 100644 --- a/Minecraft.Client/Common/GameRules/GameRuleManager.cpp +++ b/Minecraft.Client/Common/GameRules/GameRuleManager.cpp @@ -344,6 +344,7 @@ void GameRuleManager::writeRuleFile(DataOutputStream *dos) // Write schematic files. unordered_map *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; diff --git a/Minecraft.Client/Common/UI/IUIScene_HUD.cpp b/Minecraft.Client/Common/UI/IUIScene_HUD.cpp index fd9779665..d2754789c 100644 --- a/Minecraft.Client/Common/UI/IUIScene_HUD.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_HUD.cpp @@ -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(ceil(pMinecraft->localplayers[iPad]->getHealth())); + int oldHealth = static_cast(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); diff --git a/Minecraft.Client/Common/UI/UIScene.cpp b/Minecraft.Client/Common/UI/UIScene.cpp index 5630e9726..0088f43d4 100644 --- a/Minecraft.Client/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Common/UI/UIScene.cpp @@ -578,9 +578,12 @@ bool UIScene::handleMouseClick(F32 x, F32 y) if (bestCtrl->getControlType() == UIControl::eCheckBox) { UIControl_CheckBox *cb = static_cast(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 { diff --git a/Minecraft.World/BonusChestFeature.cpp b/Minecraft.World/BonusChestFeature.cpp index cd33e60ae..86dc97258 100644 --- a/Minecraft.World/BonusChestFeature.cpp +++ b/Minecraft.World/BonusChestFeature.cpp @@ -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; + + } } diff --git a/Minecraft.World/EnderMan.cpp b/Minecraft.World/EnderMan.cpp index e12318187..da469a3a2 100644 --- a/Minecraft.World/EnderMan.cpp +++ b/Minecraft.World/EnderMan.cpp @@ -410,9 +410,14 @@ bool EnderMan::hurt(DamageSource *source, float damage) if ( dynamic_cast(source) != nullptr && source->getEntity()->instanceof(eTYPE_PLAYER)) { - aggroedByPlayer = true; + if (!dynamic_pointer_cast(source->getEntity())->abilities.invulnerable) + { + aggroedByPlayer = true; + } + else setCreepy(false); } + if (dynamic_cast(source) != nullptr) { aggroedByPlayer = false; diff --git a/Minecraft.World/EntityHorse.cpp b/Minecraft.World/EntityHorse.cpp index 32c9eb2de..0bf5bc080 100644 --- a/Minecraft.World/EntityHorse.cpp +++ b/Minecraft.World/EntityHorse.cpp @@ -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::getClosestMommy(shared_ptr baby, double searchRadius) { diff --git a/Minecraft.World/EntityHorse.h b/Minecraft.World/EntityHorse.h index c2784491a..c91da172a 100644 --- a/Minecraft.World/EntityHorse.h +++ b/Minecraft.World/EntityHorse.h @@ -192,6 +192,7 @@ private: public: virtual void containerChanged(); virtual bool canSpawn(); + virtual bool removeWhenFarAway() override; protected: virtual shared_ptr getClosestMommy(shared_ptr baby, double searchRadius); diff --git a/Minecraft.World/MinecartTNT.cpp b/Minecraft.World/MinecartTNT.cpp index 6e72d66b7..18681aa46 100644 --- a/Minecraft.World/MinecartTNT.cpp +++ b/Minecraft.World/MinecartTNT.cpp @@ -64,28 +64,34 @@ void MinecartTNT::destroy(DamageSource *source) double speedSqr = xd * xd + zd * zd; - if (!source->isExplosion()) + if (!app.GetGameHostOption(eGameHostOption_TNT) || !source->isExplosion()) { - spawnAtLocation(std::make_shared(Tile::tnt, 1), 0); + spawnAtLocation( shared_ptr( new ItemInstance(Tile::tnt, 1) ), 0); } - if (source->isFire() || source->isExplosion() || speedSqr >= 0.01f) + if (app.GetGameHostOption(eGameHostOption_TNT)) { - explode(speedSqr); + if (source->isFire() || source->isExplosion() || speedSqr >= 0.01f) + { + explode(speedSqr); + } } } void MinecartTNT::explode(double speedSqr) { + if (!app.GetGameHostOption(eGameHostOption_TNT)) + { + remove(); + return; + } + if (!level->isClientSide) { double speed = sqrt(speedSqr); - if (speed > 5.0) speed = 5.0; - if (app.GetGameHostOption(eGameHostOption_TNT)) - { - level->explode(shared_from_this(), x, y, z, static_cast(4 + random->nextDouble() * 1.5f * speed), true); - remove(); - } + if (speed > 5) speed = 5; + level->explode(shared_from_this(), x, y, z, (float) (4 + random->nextDouble() * 1.5f * speed), true); + remove(); } } @@ -122,12 +128,15 @@ void MinecartTNT::handleEntityEvent(byte eventId) void MinecartTNT::primeFuse() { - fuse = 80; - - if (!level->isClientSide) + if (app.GetGameHostOption(eGameHostOption_TNT)) { - level->broadcastEntityEvent(shared_from_this(), EVENT_PRIME); - level->playEntitySound(shared_from_this(), eSoundType_RANDOM_FUSE, 1, 1.0f); + fuse = 80; + + if (!level->isClientSide) + { + level->broadcastEntityEvent(shared_from_this(), EVENT_PRIME); + level->playEntitySound(shared_from_this(), eSoundType_RANDOM_FUSE, 1, 1.0f); + } } } diff --git a/Minecraft.World/Monster.cpp b/Minecraft.World/Monster.cpp index 2de453153..ad19a36d4 100644 --- a/Minecraft.World/Monster.cpp +++ b/Minecraft.World/Monster.cpp @@ -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(sourceEntity)->abilities.invulnerable) + { + attackTarget = sourceEntity; + } + } + else attackTarget = sourceEntity; } return true; } diff --git a/README.md b/README.md index 122157ba8..0c476666e 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ ## Introduction -This project contains the source code of Minecraft Legacy Console Edition v1.6.0560.0 (TU19) from https://archive.org/details/minecraft-legacy-console-edition-source-code, with some fixes and improvements applied. +This project contains the source code of Minecraft Legacy Console Edition v1.6.0560.0 (TU19) with some fixes and improvements applied. ## Download -Windows users can download our [Nightly Build](https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly)! Simply download the `.zip` file and extract it to a folder where you'd like to keep the game. You can set your username in `username.txt` (you'll have to make this file) and add servers to connect to in `servers.txt` +Windows users can download our [Nightly Build](https://github.com/smartcmd/MinecraftConsoles/releases/tag/nightly)! Simply download the `.zip` file and extract it to a folder where you'd like to keep the game. You can set your username in `username.txt` (you'll have to make this file) ## Platform Support @@ -35,30 +35,11 @@ Basic LAN multiplayer is available on the Windows build - Other players on the same LAN can discover the session from the in-game Join Game menu - Game connections use TCP port `25565` by default - LAN discovery uses UDP port `25566` -- Add servers to your server list with `servers.txt` (temp solution) +- Add servers to your server list with the in-game Add Server button (temp) - Rename yourself without losing data by keeping your `uid.dat` Parts of this feature are based on code from [LCEMP](https://github.com/LCEMP/LCEMP) (thanks!) -### servers.txt - -To add a server to your game, create the `servers.txt` file in the same directory as you have `Minecraft.Client.exe`. Inside, follow this format: -``` -serverip.example.com -25565 -The name of your server in UI! -``` - -For example, here's a valid servers.txt -``` -1.1.1.1 -25565 -Cloudflare's Very Own LCE Server -127.0.0.1 -25565 -Localhost Test Crap -``` - ### Launch Arguments | Argument | Description | @@ -100,7 +81,7 @@ Minecraft.Client.exe -name Steve -fullscreen 2. Clone the repository. 3. Open the project by double-clicking `MinecraftConsoles.sln`. 4. Make sure `Minecraft.Client` is set as the Startup Project. -5. Set the build configuration to **Debug** (Release is also OK but has some bugs) and the target platform to **Windows64**, then build and run. +5. Set the build configuration to **Debug** (Release is also ok but missing some debug features) and the target platform to **Windows64**, then build and run. ### CMake (Windows x64)