Enabling solo-dungeon queue and implementing first dungeonhelper prototyp

This commit is contained in:
Nico committed 2026-09-20 12:11:44 +02:00
1 parent fcedaf23b9
commit f35c3c4ae9
5 files changed
+168 -3

No files matched your search

+102 -2
View File
@@ -23,6 +23,7 @@
#include "Group.h"
#include "LFGQueue.h"
#include "LFGMgr.h"
#include "World.h"
#include "Log.h"
namespace lfg
@@ -331,6 +332,105 @@ uint8 LFGQueue::FindGroups()
else
AddToCurrentQueue(frontguid); // Lfg group not found, add this group to the queue.
}
// Keep normal TrinityCore matchmaking authoritative. Only after normal
// matching has failed do we permit an underfilled low-population group.
if (sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_LOW_POPULATION_ENABLE))
proposals += FindLowPopulationGroups();
return proposals;
}
GuidList LFGQueue::GetQueueGuidsFromKey(std::string const& key) const
{
GuidList result;
for (GuidList::const_iterator itr = currentQueueStore.begin(); itr != currentQueueStore.end(); ++itr)
{
std::ostringstream o;
o << itr->GetRawValue();
std::string token = o.str();
size_t pos = key.find(token);
while (pos != std::string::npos)
{
size_t end = pos + token.length();
bool leftBoundary = pos == 0 || key[pos - 1] == '|';
bool rightBoundary = end == key.length() || key[end] == '|';
if (leftBoundary && rightBoundary)
{
result.push_back(*itr);
break;
}
pos = key.find(token, pos + 1);
}
}
return result;
}
uint8 LFGQueue::FindLowPopulationGroups()
{
uint8 proposals = 0;
uint32 const fallbackDelay =
sWorld->getIntConfig(CONFIG_DUNGEON_FINDER_LOW_POPULATION_FALLBACK_DELAY);
uint32 const minPlayers =
sWorld->getIntConfig(CONFIG_DUNGEON_FINDER_LOW_POPULATION_MIN_PLAYERS);
time_t const now = GameTime::GetGameTime();
bool createdProposal = true;
while (createdProposal)
{
createdProposal = false;
GuidList queueSnapshot = currentQueueStore;
for (GuidList::const_iterator itrGuid = queueSnapshot.begin(); itrGuid != queueSnapshot.end(); ++itrGuid)
{
LfgQueueDataContainer::iterator itrQueue = QueueDataStore.find(*itrGuid);
if (itrQueue == QueueDataStore.end())
continue;
LfgQueueData& queueData = itrQueue->second;
if (now < queueData.joinTime || uint32(now - queueData.joinTime) < fallbackDelay)
continue;
if (queueData.bestCompatible.empty())
FindBestCompatibleInQueue(itrQueue);
if (queueData.bestCompatible.empty())
continue;
GuidList check = GetQueueGuidsFromKey(queueData.bestCompatible);
if (check.empty())
continue;
uint32 playerCount = 0;
for (GuidList::const_iterator itrCheck = check.begin(); itrCheck != check.end(); ++itrCheck)
{
LfgQueueDataContainer::const_iterator itrData = QueueDataStore.find(*itrCheck);
if (itrData != QueueDataStore.end())
playerCount += uint32(itrData->second.roles.size());
}
if (playerCount < minPlayers)
continue;
TC_LOG_DEBUG("lfg.queue.lowpopulation",
"Low population fallback after %u seconds: trying partial group (%s) with %u player(s)",
fallbackDelay, ConcatenateGuids(check).c_str(), playerCount);
if (CheckCompatibility(check, true) == LFG_COMPATIBLES_MATCH)
{
++proposals;
createdProposal = true;
break;
}
}
}
return proposals;
}
@@ -379,7 +479,7 @@ LfgCompatibility LFGQueue::FindNewGroups(GuidList& check, GuidList& all)
@param[in] check List of guids to check compatibilities
@return LfgCompatibility type of compatibility
*/
LfgCompatibility LFGQueue::CheckCompatibility(GuidList check)
LfgCompatibility LFGQueue::CheckCompatibility(GuidList check, bool allowIncomplete)
{
std::string strGuids = ConcatenateGuids(check);
LfgProposal proposal;
@@ -544,7 +644,7 @@ LfgCompatibility LFGQueue::CheckCompatibility(GuidList check)
}
// Enough players?
if (numPlayers != dungeon->GetMaxGroupSize())
if (numPlayers != dungeon->GetMaxGroupSize() && !allowIncomplete)
{
TC_LOG_DEBUG("lfg.queue.match.compatibility.check", "Guids: (%s) Compatibles but not enough players(%u)", GetDetailedMatchRoles(check).c_str(), numPlayers);
LfgCompatibilityData data(LFG_COMPATIBLES_WITH_LESS_PLAYERS);
+3 -1
View File
@@ -138,9 +138,11 @@ class TC_GAME_API LFGQueue
LfgCompatibilityData* GetCompatibilityData(std::string const& key);
void FindBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQueue);
void UpdateBestCompatibleInQueue(LfgQueueDataContainer::iterator itrQueue, std::string const& key, LfgRolesMap const& roles);
uint8 FindLowPopulationGroups();
GuidList GetQueueGuidsFromKey(std::string const& key) const;
LfgCompatibility FindNewGroups(GuidList& check, GuidList& all);
LfgCompatibility CheckCompatibility(GuidList check);
LfgCompatibility CheckCompatibility(GuidList check, bool allowIncomplete = false);
// Queue
LfgQueueDataContainer QueueDataStore; ///< Queued groups
+14
View File
@@ -1521,6 +1521,20 @@ void World::LoadConfigSettings(bool reload)
// Dungeon finder
m_int_configs[CONFIG_LFG_OPTIONSMASK] = sConfigMgr->GetIntDefault("DungeonFinder.OptionsMask", 1);
m_bool_configs[CONFIG_DUNGEON_FINDER_LOW_POPULATION_ENABLE] =
sConfigMgr->GetBoolDefault("DungeonFinder.LowPopulation.Enable", false);
m_int_configs[CONFIG_DUNGEON_FINDER_LOW_POPULATION_FALLBACK_DELAY] =
sConfigMgr->GetIntDefault("DungeonFinder.LowPopulation.FallbackDelay", 60);
m_int_configs[CONFIG_DUNGEON_FINDER_LOW_POPULATION_MIN_PLAYERS] =
sConfigMgr->GetIntDefault("DungeonFinder.LowPopulation.MinPlayers", 1);
if (m_int_configs[CONFIG_DUNGEON_FINDER_LOW_POPULATION_MIN_PLAYERS] < 1)
{
TC_LOG_ERROR("server.loading",
"DungeonFinder.LowPopulation.MinPlayers (%u) must be at least 1. Using 1 instead.",
m_int_configs[CONFIG_DUNGEON_FINDER_LOW_POPULATION_MIN_PLAYERS]);
m_int_configs[CONFIG_DUNGEON_FINDER_LOW_POPULATION_MIN_PLAYERS] = 1;
}
// DBC_ItemAttributes
m_bool_configs[CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES] = sConfigMgr->GetBoolDefault("DBC.EnforceItemAttributes", true);
+3
View File
@@ -122,6 +122,7 @@ enum WorldBoolConfigs : uint8
CONFIG_CHAT_PARTY_RAID_WARNINGS,
CONFIG_DETECT_POS_COLLISION,
CONFIG_RESTRICTED_LFG_CHANNEL,
CONFIG_DUNGEON_FINDER_LOW_POPULATION_ENABLE,
CONFIG_CHAT_FAKE_MESSAGE_PREVENTING,
CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP,
CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE,
@@ -364,6 +365,8 @@ enum WorldIntConfigs : uint8
CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION,
CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS,
CONFIG_LFG_OPTIONSMASK,
CONFIG_DUNGEON_FINDER_LOW_POPULATION_FALLBACK_DELAY,
CONFIG_DUNGEON_FINDER_LOW_POPULATION_MIN_PLAYERS,
CONFIG_MAX_INSTANCES_PER_HOUR,
CONFIG_WARDEN_CLIENT_RESPONSE_DELAY,
CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF,
+46
View File
@@ -37,6 +37,7 @@ EndContentData */
#include "Spell.h"
#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Log.h"
namespace ItemScripts
{
@@ -412,6 +413,50 @@ class item_generic_limit_chance_above_60 : public ItemScript
return true;
}
};
/*#####
# item_horde_adventurer_horn
#####*/
enum HordeAdventurerHorn
{
NPC_ORGRIMMAR_GRUNT = 3296
};
class item_horde_adventurer_horn : public ItemScript
{
public:
item_horde_adventurer_horn() : ItemScript("item_horde_adventurer_horn") { }
bool OnUse(Player* player, Item* item, SpellCastTargets const& /*targets*/) override
{
TC_LOG_INFO("scripts", "Horde adventureer horn OnUse triggered by player %s (item %u)", player->GetName().c_str(), item->GetEntry());
// The horn may not be used during combat.
if (player->IsInCombat())
{
player->SendEquipError(EQUIP_ERR_CLIENT_LOCKED_OUT, item, nullptr);
return true;
}
// The horn may only be used inside a dungeon instance.
if (!player->GetMap()->IsDungeon())
{
player->SendEquipError(EQUIP_ERR_CLIENT_LOCKED_OUT, item, nullptr);
return true;
}
player->SummonCreature(
NPC_ORGRIMMAR_GRUNT,
player->GetPositionX() + 2.0f,
player->GetPositionY(),
player->GetPositionZ(),
player->GetOrientation(),
TEMPSUMMON_MANUAL_DESPAWN,
0
);
return true;
}
};
}
void AddSC_item_scripts()
@@ -428,4 +473,5 @@ void AddSC_item_scripts()
new item_dehta_trap_smasher();
new item_captured_frog();
new item_generic_limit_chance_above_60();
new item_horde_adventurer_horn();
}