Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Minecraft.Client/Common/Audio/Consoles_SoundEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ void ConsoleSoundEngine::SetIsPlayingStreamingGameMusic(bool bVal)
{
m_bIsPlayingStreamingGameMusic=bVal;
}
bool ConsoleSoundEngine::GetIsPlayingMenuMusic()
{
return m_bIsPlayingMenuMusic;
}
bool ConsoleSoundEngine::GetIsPlayingEndMusic()
{
return m_bIsPlayingEndMusic;
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions Minecraft.Client/Common/Audio/Consoles_SoundEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ class ConsoleSoundEngine
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];
Expand Down Expand Up @@ -94,6 +96,7 @@ class ConsoleSoundEngine

bool m_bIsPlayingStreamingCDMusic;
bool m_bIsPlayingStreamingGameMusic;
bool m_bIsPlayingMenuMusic;
bool m_bIsPlayingEndMusic;
bool m_bIsPlayingNetherMusic;
};
97 changes: 97 additions & 0 deletions Minecraft.Client/Common/Audio/MusicTrackManager.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 117 additions & 0 deletions Minecraft.Client/Common/Audio/MusicTrackManager.h
Original file line number Diff line number Diff line change
@@ -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;
};
Loading