mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Merge branch 'NSDeathman-Personal' of https://github.com/NSDeathman/MinecraftConsoles into NSDeathman-Personal
This commit is contained in:
commit
fb87883b54
8
.gitattributes
vendored
8
.gitattributes
vendored
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ void ConsoleSoundEngine::SetIsPlayingStreamingGameMusic(bool bVal)
|
|||
{
|
||||
m_bIsPlayingStreamingGameMusic=bVal;
|
||||
}
|
||||
bool ConsoleSoundEngine::GetIsPlayingMenuMusic()
|
||||
{
|
||||
return m_bIsPlayingMenuMusic;
|
||||
}
|
||||
bool ConsoleSoundEngine::GetIsPlayingEndMusic()
|
||||
{
|
||||
return m_bIsPlayingEndMusic;
|
||||
|
|
@ -26,6 +30,10 @@ bool ConsoleSoundEngine::GetIsPlayingNetherMusic()
|
|||
{
|
||||
return m_bIsPlayingNetherMusic;
|
||||
}
|
||||
void ConsoleSoundEngine::SetIsPlayingMenuMusic(bool bVal)
|
||||
{
|
||||
m_bIsPlayingMenuMusic = bVal;
|
||||
}
|
||||
void ConsoleSoundEngine::SetIsPlayingEndMusic(bool bVal)
|
||||
{
|
||||
m_bIsPlayingEndMusic=bVal;
|
||||
|
|
|
|||
|
|
@ -61,9 +61,11 @@ public:
|
|||
virtual bool GetIsPlayingStreamingGameMusic() ;
|
||||
virtual void SetIsPlayingStreamingCDMusic(bool bVal) ;
|
||||
virtual void SetIsPlayingStreamingGameMusic(bool bVal) ;
|
||||
virtual bool GetIsPlayingMenuMusic();
|
||||
virtual bool GetIsPlayingEndMusic() ;
|
||||
virtual bool GetIsPlayingNetherMusic() ;
|
||||
virtual void SetIsPlayingEndMusic(bool bVal) ;
|
||||
virtual void SetIsPlayingMenuMusic(bool bVal);
|
||||
virtual void SetIsPlayingNetherMusic(bool bVal) ;
|
||||
static const WCHAR *wchSoundNames[eSoundType_MAX];
|
||||
static const WCHAR *wchUISoundNames[eSFX_MAX];
|
||||
|
|
@ -94,6 +96,7 @@ private:
|
|||
|
||||
bool m_bIsPlayingStreamingCDMusic;
|
||||
bool m_bIsPlayingStreamingGameMusic;
|
||||
bool m_bIsPlayingMenuMusic;
|
||||
bool m_bIsPlayingEndMusic;
|
||||
bool m_bIsPlayingNetherMusic;
|
||||
};
|
||||
97
Minecraft.Client/Common/Audio/MusicTrackManager.cpp
Normal file
97
Minecraft.Client/Common/Audio/MusicTrackManager.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include "stdafx.h"
|
||||
#include "MusicTrackManager.h"
|
||||
#include "../Minecraft.World/Random.h"
|
||||
|
||||
MusicTrackManager::MusicTrackManager(Random* rng)
|
||||
: m_random(rng)
|
||||
{
|
||||
// The domains will be filled later via setDomainRange().
|
||||
}
|
||||
|
||||
MusicTrackManager::~MusicTrackManager()
|
||||
{
|
||||
// unordered_map will automatically destroy its DomainInfo objects,
|
||||
// which in turn delete[] the heard arrays.
|
||||
}
|
||||
|
||||
void MusicTrackManager::setDomainRange(Domain domain, int minIdx, int maxIdx)
|
||||
{
|
||||
assert(minIdx <= maxIdx);
|
||||
// Use emplace to construct the DomainInfo in-place.
|
||||
// If the domain already exists, this will replace it (C++17).
|
||||
m_domains.erase(domain); // Remove old entry if any
|
||||
m_domains.emplace(domain, DomainInfo(minIdx, maxIdx));
|
||||
}
|
||||
|
||||
int MusicTrackManager::selectTrack(Domain domain)
|
||||
{
|
||||
// Special case: if domain is None, return -1 (no track).
|
||||
if (domain == Domain::None)
|
||||
return -1;
|
||||
|
||||
DomainInfo& info = getInfo(domain);
|
||||
|
||||
// If range contains only one track, just return that track.
|
||||
if (info.trackCount == 1)
|
||||
return info.minIdx;
|
||||
|
||||
// Check whether all tracks have been heard.
|
||||
bool allHeard = true;
|
||||
for (int i = 0; i < info.trackCount; ++i)
|
||||
{
|
||||
if (!info.heard[i])
|
||||
{
|
||||
allHeard = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all tracks have been heard, reset the heard flags.
|
||||
if (allHeard)
|
||||
{
|
||||
std::memset(info.heard, 0, sizeof(bool) * info.trackCount);
|
||||
}
|
||||
|
||||
// Try up to (trackCount/2 + 1) times to pick a track that hasn't been heard.
|
||||
// This biases toward unplayed tracks but doesn't guarantee it if the random
|
||||
// keeps hitting played ones. It's a compromise between fairness and performance.
|
||||
const int maxAttempts = info.trackCount / 2 + 1;
|
||||
for (int attempt = 0; attempt < maxAttempts; ++attempt)
|
||||
{
|
||||
int idx = m_random->nextInt(info.trackCount); // 0 .. trackCount-1
|
||||
if (!info.heard[idx])
|
||||
{
|
||||
info.heard[idx] = true;
|
||||
return info.minIdx + idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: if we couldn't find an unplayed track (should be rare),
|
||||
// just pick any random track and mark it as heard.
|
||||
int fallbackIdx = m_random->nextInt(info.trackCount);
|
||||
info.heard[fallbackIdx] = true;
|
||||
return info.minIdx + fallbackIdx;
|
||||
}
|
||||
|
||||
void MusicTrackManager::resetDomain(Domain domain)
|
||||
{
|
||||
DomainInfo& info = getInfo(domain);
|
||||
std::memset(info.heard, 0, sizeof(bool) * info.trackCount);
|
||||
}
|
||||
|
||||
// ---------- private helpers ----------
|
||||
|
||||
MusicTrackManager::DomainInfo& MusicTrackManager::getInfo(Domain domain)
|
||||
{
|
||||
auto it = m_domains.find(domain);
|
||||
assert(it != m_domains.end() && "Domain not initialized. Call setDomainRange first.");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const MusicTrackManager::DomainInfo& MusicTrackManager::getInfo(Domain domain) const
|
||||
{
|
||||
auto it = m_domains.find(domain);
|
||||
assert(it != m_domains.end() && "Domain not initialized.");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
117
Minecraft.Client/Common/Audio/MusicTrackManager.h
Normal file
117
Minecraft.Client/Common/Audio/MusicTrackManager.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <cstring> // for memset
|
||||
#include <cassert>
|
||||
|
||||
// Forward declaration of the random number generator used by SoundEngine.
|
||||
// Replace with your actual random class if different.
|
||||
class Random;
|
||||
|
||||
/**
|
||||
* Manages selection of music tracks across different gameplay domains
|
||||
* (Menu, Overworld Survival, Overworld Creative, Nether, End).
|
||||
* Each domain maintains its own range of track indices and a history
|
||||
* of recently played tracks to avoid immediate repetition.
|
||||
*/
|
||||
class MusicTrackManager
|
||||
{
|
||||
public:
|
||||
// Enumeration of all music domains. The values match the original
|
||||
// SoundEngine constants for easy conversion.
|
||||
enum class Domain
|
||||
{
|
||||
Menu = -1,
|
||||
OverworldSurvival = 0,
|
||||
OverworldCreative = 1,
|
||||
Nether = 2,
|
||||
End = 3,
|
||||
None = 4 // Special value for when no background music should play (e.g., during a music disc)
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param rng Pointer to a random number generator (must remain valid).
|
||||
*/
|
||||
explicit MusicTrackManager(Random* rng);
|
||||
|
||||
/**
|
||||
* Destructor – frees all dynamically allocated heard-track arrays.
|
||||
*/
|
||||
~MusicTrackManager();
|
||||
|
||||
// Prevent copying (to avoid double deletion of internal arrays).
|
||||
MusicTrackManager(const MusicTrackManager&) = delete;
|
||||
MusicTrackManager& operator=(const MusicTrackManager&) = delete;
|
||||
|
||||
/**
|
||||
* Sets the inclusive index range for a given domain.
|
||||
* @param domain The music domain.
|
||||
* @param minIdx First valid track index.
|
||||
* @param maxIdx Last valid track index (must be >= minIdx).
|
||||
*/
|
||||
void setDomainRange(Domain domain, int minIdx, int maxIdx);
|
||||
|
||||
/**
|
||||
* Selects a track index for the specified domain.
|
||||
* Tries to return a track that has not been played recently;
|
||||
* if all tracks have been played, resets the history and picks one randomly.
|
||||
* For domains with only one track, that track is always returned.
|
||||
* @param domain The domain for which to choose a track.
|
||||
* @return A valid track index within the domain's range.
|
||||
*/
|
||||
int selectTrack(Domain domain);
|
||||
|
||||
/**
|
||||
* Resets the heard history for a domain, marking all tracks as "not heard".
|
||||
* Useful when switching domains or after a major game state change.
|
||||
* @param domain The domain to reset.
|
||||
*/
|
||||
void resetDomain(Domain domain);
|
||||
|
||||
int getDomainMin(Domain d) const { return getInfo(d).minIdx; }
|
||||
int getDomainMax(Domain d) const { return getInfo(d).maxIdx; }
|
||||
|
||||
private:
|
||||
// Internal structure storing range and heard-array for a domain.
|
||||
struct DomainInfo
|
||||
{
|
||||
int minIdx; // First track index (inclusive)
|
||||
int maxIdx; // Last track index (inclusive)
|
||||
int trackCount; // Number of tracks in this domain
|
||||
bool* heard; // Dynamic array: heard[i] == true if track (minIdx + i) has been played recently
|
||||
|
||||
DomainInfo(int minVal = 0, int maxVal = 0)
|
||||
: minIdx(minVal), maxIdx(maxVal), trackCount(maxVal - minVal + 1)
|
||||
{
|
||||
heard = new bool[trackCount](); // value-initialized to false
|
||||
}
|
||||
|
||||
// Move constructor (optional, but needed if we want to store in unordered_map with emplace)
|
||||
DomainInfo(DomainInfo&& other) noexcept
|
||||
: minIdx(other.minIdx), maxIdx(other.maxIdx), trackCount(other.trackCount), heard(other.heard)
|
||||
{
|
||||
other.heard = nullptr; // prevent double deletion
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~DomainInfo()
|
||||
{
|
||||
delete[] heard;
|
||||
}
|
||||
|
||||
// No copy
|
||||
DomainInfo(const DomainInfo&) = delete;
|
||||
DomainInfo& operator=(const DomainInfo&) = delete;
|
||||
};
|
||||
|
||||
// Map from Domain to its info.
|
||||
std::unordered_map<Domain, DomainInfo> m_domains;
|
||||
|
||||
// Pointer to the random number generator.
|
||||
Random* m_random;
|
||||
|
||||
// Helper to get the info for a domain (assumes domain exists).
|
||||
DomainInfo& getInfo(Domain domain);
|
||||
const DomainInfo& getInfo(Domain domain) const;
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@ class Mob;
|
|||
class Options;
|
||||
using namespace std;
|
||||
#include "..\..\Minecraft.World\SoundTypes.h"
|
||||
#include "MusicTrackManager.h"
|
||||
|
||||
#include "miniaudio.h"
|
||||
|
||||
|
|
@ -17,6 +18,10 @@ enum eMUSICFILES
|
|||
eStream_Overworld_hal4,
|
||||
eStream_Overworld_nuance1,
|
||||
eStream_Overworld_nuance2,
|
||||
eStream_Overworld_piano1,
|
||||
eStream_Overworld_piano2,
|
||||
eStream_Overworld_piano3, // <-- make piano3 the last overworld one
|
||||
|
||||
#ifndef _XBOX
|
||||
// Add the new music tracks
|
||||
eStream_Overworld_Creative1,
|
||||
|
|
@ -25,14 +30,12 @@ enum eMUSICFILES
|
|||
eStream_Overworld_Creative4,
|
||||
eStream_Overworld_Creative5,
|
||||
eStream_Overworld_Creative6,
|
||||
|
||||
eStream_Overworld_Menu1,
|
||||
eStream_Overworld_Menu2,
|
||||
eStream_Overworld_Menu3,
|
||||
eStream_Overworld_Menu4,
|
||||
#endif
|
||||
eStream_Overworld_piano1,
|
||||
eStream_Overworld_piano2,
|
||||
eStream_Overworld_piano3, // <-- make piano3 the last overworld one
|
||||
// Nether
|
||||
eStream_Nether1,
|
||||
eStream_Nether2,
|
||||
|
|
@ -106,6 +109,7 @@ extern std::vector<MiniAudioSound*> m_activeSounds;
|
|||
class SoundEngine : public ConsoleSoundEngine
|
||||
{
|
||||
static const int MAX_SAME_SOUNDS_PLAYING = 8; // 4J added
|
||||
|
||||
public:
|
||||
SoundEngine();
|
||||
void destroy() override;
|
||||
|
|
@ -125,10 +129,11 @@ public:
|
|||
void addMusic(const wstring& name, File *file) override;
|
||||
void addStreaming(const wstring& name, File *file) override;
|
||||
char *ConvertSoundPathToName(const wstring& name, bool bConvertSpaces=false) override;
|
||||
bool isStreamingWavebankReady(); // 4J Added
|
||||
int getMusicID(int iDomain);
|
||||
bool isStreamingWavebankReady();
|
||||
MusicTrackManager::Domain determineCurrentMusicDomain() const;
|
||||
int getTrackForDomain(MusicTrackManager::Domain domain);
|
||||
int getMusicID(const wstring& name);
|
||||
void SetStreamingSounds(int iOverworldMin, int iOverWorldMax, int iNetherMin, int iNetherMax, int iEndMin, int iEndMax, int iCD1);
|
||||
void SetStreamingSounds(int iMenuMin, int iMenuMax, int iOverworldSurvivalMin, int iOverWorldSurvivalMax, int iOverworldCreativeMin, int iOverWorldCreativeMax, int iNetherMin, int iNetherMax, int iEndMin, int iEndMax, int iCD1);
|
||||
void updateMiniAudio();
|
||||
void playMusicUpdate();
|
||||
|
||||
|
|
@ -141,8 +146,10 @@ private:
|
|||
int initAudioHardware(int iMinSpeakers) override
|
||||
{ return iMinSpeakers;}
|
||||
#endif
|
||||
|
||||
int GetRandomishTrack(int iStart,int iEnd);
|
||||
|
||||
Random* random;
|
||||
MusicTrackManager m_musicTrackManager;
|
||||
MusicTrackManager::Domain m_currentMusicDomain;
|
||||
|
||||
ma_engine m_engine;
|
||||
ma_engine_config m_engineConfig;
|
||||
|
|
@ -157,8 +164,6 @@ private:
|
|||
AUDIO_LISTENER m_ListenerA[MAX_LOCAL_PLAYERS];
|
||||
int m_validListenerCount;
|
||||
|
||||
|
||||
Random *random;
|
||||
int m_musicID;
|
||||
int m_iMusicDelay;
|
||||
int m_StreamState;
|
||||
|
|
@ -174,12 +179,7 @@ private:
|
|||
char m_szStreamName[255];
|
||||
int CurrentSoundsPlaying[eSoundType_MAX+eSFX_MAX];
|
||||
|
||||
// streaming music files - will be different for mash-up packs
|
||||
int m_iStream_Overworld_Min,m_iStream_Overworld_Max;
|
||||
int m_iStream_Nether_Min,m_iStream_Nether_Max;
|
||||
int m_iStream_End_Min,m_iStream_End_Max;
|
||||
int m_iStream_CD_1;
|
||||
bool *m_bHeardTrackA;
|
||||
|
||||
#ifdef __ORBIS__
|
||||
int32_t m_hBGMAudio;
|
||||
|
|
|
|||
|
|
@ -3754,10 +3754,12 @@ void CMinecraftApp::HandleXuiActions(void)
|
|||
// need to stop the streaming audio - by playing streaming audio from the default texture pack now
|
||||
// reset the streaming sounds back to the normal ones
|
||||
#ifndef _XBOX
|
||||
pMinecraft->soundEngine->SetStreamingSounds(eStream_Overworld_Calm1,eStream_Overworld_piano3,
|
||||
eStream_Nether1,eStream_Nether4,
|
||||
eStream_end_dragon,eStream_end_end,
|
||||
eStream_CD_1);
|
||||
pMinecraft->soundEngine->SetStreamingSounds(eStream_Overworld_Menu1, eStream_Overworld_Menu4,
|
||||
eStream_Overworld_Calm1, eStream_Overworld_piano3,
|
||||
eStream_Overworld_Creative1, eStream_Overworld_Creative6,
|
||||
eStream_Nether1, eStream_Nether4,
|
||||
eStream_end_dragon, eStream_end_end,
|
||||
eStream_CD_1);
|
||||
#endif
|
||||
pMinecraft->soundEngine->playStreaming(L"", 0, 0, 0, 1, 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -2064,10 +2064,12 @@ void UIController::NavigateToHomeMenu()
|
|||
{
|
||||
// need to stop the streaming audio - by playing streaming audio from the default texture pack now
|
||||
// reset the streaming sounds back to the normal ones
|
||||
pMinecraft->soundEngine->SetStreamingSounds(eStream_Overworld_Calm1,eStream_Overworld_piano3,
|
||||
eStream_Nether1,eStream_Nether4,
|
||||
eStream_end_dragon,eStream_end_end,
|
||||
eStream_CD_1);
|
||||
pMinecraft->soundEngine->SetStreamingSounds(eStream_Overworld_Menu1, eStream_Overworld_Menu4,
|
||||
eStream_Overworld_Calm1, eStream_Overworld_piano3,
|
||||
eStream_Overworld_Creative1, eStream_Overworld_Creative6,
|
||||
eStream_Nether1, eStream_Nether4,
|
||||
eStream_end_dragon, eStream_end_end,
|
||||
eStream_CD_1);
|
||||
pMinecraft->soundEngine->playStreaming(L"", 0, 0, 0, 1, 1);
|
||||
|
||||
// if(pDLCTexPack->m_pStreamedWaveBank!=nullptr)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -483,7 +483,7 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen
|
|||
iEndStart=iOverworldC+iNetherC;
|
||||
iEndC=dlcFile->GetCountofType(DLCAudioFile::e_AudioType_End);
|
||||
|
||||
Minecraft::GetInstance()->soundEngine->SetStreamingSounds(iOverworldStart,iOverworldStart+iOverworldC-1,
|
||||
Minecraft::GetInstance()->soundEngine->SetStreamingSounds(iOverworldStart,iOverworldStart+iOverworldC-1, iOverworldStart, iOverworldStart + iOverworldC - 1, iOverworldStart, iOverworldStart + iOverworldC - 1,
|
||||
iNetherStart,iNetherStart+iNetherC-1,iEndStart,iEndStart+iEndC-1,iEndStart+iEndC); // push the CD start to after
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1806,23 +1806,16 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
|
|||
InputManager.Tick();
|
||||
|
||||
// Detect KBM vs controller input mode
|
||||
if (InputManager.IsPadConnected(0))
|
||||
{
|
||||
const bool controllerUsed = InputManager.ButtonPressed(0) ||
|
||||
InputManager.GetJoypadStick_LX(0, false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_LY(0, false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_RX(0, false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_RY(0, false) != 0.0f;
|
||||
const bool controllerUsed = InputManager.ButtonPressed(0) ||
|
||||
InputManager.GetJoypadStick_LX(0, false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_LY(0, false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_RX(0, false) != 0.0f ||
|
||||
InputManager.GetJoypadStick_RY(0, false) != 0.0f;
|
||||
|
||||
if (controllerUsed)
|
||||
g_KBMInput.SetKBMActive(false);
|
||||
else if (g_KBMInput.HasAnyInput())
|
||||
g_KBMInput.SetKBMActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controllerUsed)
|
||||
g_KBMInput.SetKBMActive(false);
|
||||
else if (g_KBMInput.HasAnyInput())
|
||||
g_KBMInput.SetKBMActive(true);
|
||||
}
|
||||
|
||||
if (!g_KBMInput.IsMouseGrabbed())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<ItemInstance>(Tile::tnt, 1), 0);
|
||||
spawnAtLocation( shared_ptr<ItemInstance>( 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<float>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
27
README.md
27
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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue