Core/Instances: Reimplement resetting instances from UI
Port From (https://github.com/TrinityCore/TrinityCore/commit/ddd0c7b152d65b980db0cc2d00f15ae62a602936)
This commit is contained in:
@@ -92,12 +92,16 @@ namespace Framework.Constants
|
||||
|
||||
public enum InstanceResetMethod
|
||||
{
|
||||
All,
|
||||
ChangeDifficulty,
|
||||
Global,
|
||||
GroupDisband,
|
||||
GroupJoin,
|
||||
RespawnDelay
|
||||
Manual,
|
||||
OnChangeDifficulty,
|
||||
Expire,
|
||||
}
|
||||
|
||||
public enum InstanceResetResult
|
||||
{
|
||||
Success,
|
||||
NotEmpty,
|
||||
CannotReset
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@@ -188,6 +192,7 @@ namespace Framework.Constants
|
||||
CannotEnterTooManyInstances, // Player Has Entered Too Many Instances Recently
|
||||
CannotEnterMaxPlayers, // Target Map Already Has The Maximum Number Of Players Allowed
|
||||
CannotEnterZoneInCombat, // A Boss Encounter Is Currently In Progress On The Target Map
|
||||
CannotEnterInstanceShuttingDown,
|
||||
CannotEnterUnspecifiedReason
|
||||
}
|
||||
|
||||
|
||||
@@ -2892,6 +2892,11 @@ namespace Game.Entities
|
||||
// flight will started later
|
||||
}
|
||||
}
|
||||
else if (mapEntry.IsDungeon() && instanceId != 0)
|
||||
{
|
||||
// try finding instance by id first
|
||||
map = Global.MapMgr.FindMap(mapId, instanceId);
|
||||
}
|
||||
|
||||
// Map could be changed before
|
||||
mapEntry = CliDB.MapStorage.LookupByKey(mapId);
|
||||
@@ -2940,6 +2945,9 @@ namespace Game.Entities
|
||||
case EnterState.CannotEnterZoneInCombat:
|
||||
SendTransferAborted(map.GetId(), TransferAbortReason.ZoneInCombat);
|
||||
break;
|
||||
case EnterState.CannotEnterInstanceShuttingDown:
|
||||
SendTransferAborted(map.GetId(), TransferAbortReason.NotFound);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Framework.Database;
|
||||
using Game.DataStorage;
|
||||
using Game.Groups;
|
||||
using Game.Guilds;
|
||||
@@ -24,7 +23,7 @@ using Game.Maps;
|
||||
using Game.Networking.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Framework.Dynamic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
@@ -498,9 +497,40 @@ namespace Game.Entities
|
||||
}
|
||||
|
||||
// Reset all solo instances and optionally send a message on success for each
|
||||
public void ResetInstances(InstanceResetMethod method, bool isRaid, bool isLegacy)
|
||||
public void ResetInstances(InstanceResetMethod method)
|
||||
{
|
||||
foreach (var (mapId, instanceId) in m_recentInstances.ToList())
|
||||
{
|
||||
Map map = Global.MapMgr.FindMap(mapId, instanceId);
|
||||
bool forgetInstance = false;
|
||||
if (map)
|
||||
{
|
||||
InstanceMap instance = map.ToInstanceMap();
|
||||
if (instance != null)
|
||||
{
|
||||
switch (instance.Reset(method))
|
||||
{
|
||||
case InstanceResetResult.Success:
|
||||
SendResetInstanceSuccess(map.GetId());
|
||||
forgetInstance = true;
|
||||
break;
|
||||
case InstanceResetResult.NotEmpty:
|
||||
if (method == InstanceResetMethod.Manual)
|
||||
SendResetInstanceFailed(ResetFailedReason.Failed, map.GetId());
|
||||
else if (method == InstanceResetMethod.OnChangeDifficulty)
|
||||
forgetInstance = true;
|
||||
break;
|
||||
case InstanceResetResult.CannotReset:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forgetInstance)
|
||||
m_recentInstances.Remove(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendResetInstanceSuccess(uint MapId)
|
||||
|
||||
+26
-15
@@ -463,12 +463,6 @@ namespace Game.Groups
|
||||
|
||||
if (!IsLeader(player.GetGUID()) && !IsBGGroup() && !IsBFGroup())
|
||||
{
|
||||
// reset the new member's instances, unless he is currently in one of them
|
||||
// including raid/heroic instances that they are not permanently bound to!
|
||||
player.ResetInstances(InstanceResetMethod.GroupJoin, false, false);
|
||||
player.ResetInstances(InstanceResetMethod.GroupJoin, true, false);
|
||||
player.ResetInstances(InstanceResetMethod.GroupJoin, true, true);
|
||||
|
||||
if (player.GetDungeonDifficultyID() != GetDungeonDifficultyID())
|
||||
{
|
||||
player.SetDungeonDifficultyID(GetDungeonDifficultyID());
|
||||
@@ -754,15 +748,11 @@ namespace Game.Groups
|
||||
stmt.AddValue(0, m_dbStoreId);
|
||||
trans.Append(stmt);
|
||||
|
||||
DB.Characters.CommitTransaction(trans);
|
||||
|
||||
ResetInstances(InstanceResetMethod.GroupDisband, false, false, null);
|
||||
ResetInstances(InstanceResetMethod.GroupDisband, true, false, null);
|
||||
ResetInstances(InstanceResetMethod.GroupDisband, true, true, null);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_LFG_DATA);
|
||||
stmt.AddValue(0, m_dbStoreId);
|
||||
DB.Characters.Execute(stmt);
|
||||
trans.Append(stmt);
|
||||
|
||||
DB.Characters.CommitTransaction(trans);
|
||||
|
||||
Global.GroupMgr.FreeGroupDbStoreId(this);
|
||||
}
|
||||
@@ -1359,9 +1349,30 @@ namespace Game.Groups
|
||||
public Difficulty GetRaidDifficultyID() { return m_raidDifficulty; }
|
||||
public Difficulty GetLegacyRaidDifficultyID() { return m_legacyRaidDifficulty; }
|
||||
|
||||
public void ResetInstances(InstanceResetMethod method, bool isRaid, bool isLegacy, Player SendMsgTo)
|
||||
public void ResetInstances(InstanceResetMethod method, Player notifyPlayer)
|
||||
{
|
||||
|
||||
for (GroupInstanceReference refe = m_ownedInstancesMgr.GetFirst(); refe != null; refe = refe.Next())
|
||||
{
|
||||
InstanceMap map = refe.GetSource();
|
||||
switch (map.Reset(method))
|
||||
{
|
||||
case InstanceResetResult.Success:
|
||||
notifyPlayer.SendResetInstanceSuccess(map.GetId());
|
||||
m_recentInstances.Remove(map.GetId());
|
||||
break;
|
||||
case InstanceResetResult.NotEmpty:
|
||||
if (method == InstanceResetMethod.Manual)
|
||||
notifyPlayer.SendResetInstanceFailed(ResetFailedReason.Failed, map.GetId());
|
||||
else if (method == InstanceResetMethod.OnChangeDifficulty)
|
||||
m_recentInstances.Remove(map.GetId()); // map might not have been reset on difficulty change but we still don't want to zone in there again
|
||||
break;
|
||||
case InstanceResetResult.CannotReset:
|
||||
m_recentInstances.Remove(map.GetId()); // forget the instance, allows retrying different lockout with a new leader
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LinkOwnedInstance(GroupInstanceReference refe)
|
||||
|
||||
@@ -300,7 +300,7 @@ namespace Game
|
||||
reviveAtTrigger = true;
|
||||
break;
|
||||
case EnterState.CannotEnterCorpseInDifferentInstance:
|
||||
player.SendPacket(new AreaTriggerNoCorpse());
|
||||
SendPacket(new AreaTriggerNoCorpse());
|
||||
Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' does not have a corpse in instance map {1} and cannot enter", player.GetName(), at.target_mapId);
|
||||
break;
|
||||
case EnterState.CannotEnterInstanceBindMismatch:
|
||||
@@ -326,6 +326,11 @@ namespace Game
|
||||
player.SendTransferAborted(at.target_mapId, TransferAbortReason.ZoneInCombat);
|
||||
reviveAtTrigger = true;
|
||||
break;
|
||||
case EnterState.CannotEnterInstanceShuttingDown:
|
||||
player.SendTransferAborted(at.target_mapId, TransferAbortReason.NotFound);
|
||||
Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' cannot enter instance map {at.target_mapId} because instance is resetting.");
|
||||
reviveAtTrigger = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -594,14 +599,23 @@ namespace Game
|
||||
[WorldPacketHandler(ClientOpcodes.ResetInstances)]
|
||||
void HandleResetInstances(ResetInstances packet)
|
||||
{
|
||||
Map map = _player.GetMap();
|
||||
if (map != null && map.Instanceable())
|
||||
return;
|
||||
|
||||
Group group = GetPlayer().GetGroup();
|
||||
if (group)
|
||||
{
|
||||
if (group.IsLeader(GetPlayer().GetGUID()))
|
||||
group.ResetInstances(InstanceResetMethod.All, false, false, GetPlayer());
|
||||
if (!group.IsLeader(GetPlayer().GetGUID()))
|
||||
return;
|
||||
|
||||
if (group.IsLFGGroup())
|
||||
return;
|
||||
|
||||
group.ResetInstances(InstanceResetMethod.Manual, _player);
|
||||
}
|
||||
else
|
||||
GetPlayer().ResetInstances(InstanceResetMethod.All, false, false);
|
||||
GetPlayer().ResetInstances(InstanceResetMethod.Manual);
|
||||
}
|
||||
|
||||
[WorldPacketHandler(ClientOpcodes.SetDungeonDifficulty)]
|
||||
@@ -635,7 +649,7 @@ namespace Game
|
||||
|
||||
// cannot reset while in an instance
|
||||
Map map = GetPlayer().GetMap();
|
||||
if (map && map.IsDungeon())
|
||||
if (map && map.Instanceable())
|
||||
{
|
||||
Log.outDebug(LogFilter.Network, "WorldSession:HandleSetDungeonDifficulty: player (Name: {0}, {1}) tried to reset the instance while player is inside!",
|
||||
GetPlayer().GetName(), GetPlayer().GetGUID().ToString());
|
||||
@@ -645,33 +659,19 @@ namespace Game
|
||||
Group group = GetPlayer().GetGroup();
|
||||
if (group)
|
||||
{
|
||||
if (group.IsLeader(GetPlayer().GetGUID()))
|
||||
{
|
||||
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||
{
|
||||
Player groupGuy = refe.GetSource();
|
||||
if (!groupGuy)
|
||||
continue;
|
||||
if (!group.IsLeader(_player.GetGUID()))
|
||||
return;
|
||||
|
||||
if (!groupGuy.IsInWorld)
|
||||
return;
|
||||
if (group.IsLFGGroup())
|
||||
return;
|
||||
|
||||
if (groupGuy.GetMap().IsNonRaidDungeon())
|
||||
{
|
||||
Log.outDebug(LogFilter.Network, "WorldSession:HandleSetDungeonDifficulty: {0} tried to reset the instance while group member (Name: {1}, {2}) is inside!",
|
||||
GetPlayer().GetGUID().ToString(), groupGuy.GetName(), groupGuy.GetGUID().ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// the difficulty is set even if the instances can't be reset
|
||||
//_player.SendDungeonDifficulty(true);
|
||||
group.ResetInstances(InstanceResetMethod.ChangeDifficulty, false, false, GetPlayer());
|
||||
group.SetDungeonDifficultyID(difficultyID);
|
||||
}
|
||||
// the difficulty is set even if the instances can't be reset
|
||||
group.ResetInstances(InstanceResetMethod.OnChangeDifficulty, _player);
|
||||
group.SetDungeonDifficultyID(difficultyID);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetPlayer().ResetInstances(InstanceResetMethod.ChangeDifficulty, false, false);
|
||||
GetPlayer().ResetInstances(InstanceResetMethod.OnChangeDifficulty);
|
||||
GetPlayer().SetDungeonDifficultyID(difficultyID);
|
||||
GetPlayer().SendDungeonDifficulty();
|
||||
}
|
||||
@@ -702,7 +702,7 @@ namespace Game
|
||||
return;
|
||||
}
|
||||
|
||||
if (((int)(difficultyEntry.Flags & DifficultyFlags.Legacy) >> 5) != setRaidDifficulty.Legacy)
|
||||
if (((int)(difficultyEntry.Flags & DifficultyFlags.Legacy) != 0) != (setRaidDifficulty.Legacy != 0))
|
||||
{
|
||||
Log.outDebug(LogFilter.Network, "WorldSession.HandleSetDungeonDifficulty: {0} sent not matching legacy difficulty {1}!",
|
||||
GetPlayer().GetGUID().ToString(), difficultyEntry.Id);
|
||||
@@ -715,7 +715,7 @@ namespace Game
|
||||
|
||||
// cannot reset while in an instance
|
||||
Map map = GetPlayer().GetMap();
|
||||
if (map && map.IsDungeon())
|
||||
if (map && map.Instanceable())
|
||||
{
|
||||
Log.outDebug(LogFilter.Network, "WorldSession:HandleSetRaidDifficulty: player (Name: {0}, {1} tried to reset the instance while inside!",
|
||||
GetPlayer().GetName(), GetPlayer().GetGUID().ToString());
|
||||
@@ -725,34 +725,22 @@ namespace Game
|
||||
Group group = GetPlayer().GetGroup();
|
||||
if (group)
|
||||
{
|
||||
if (group.IsLeader(GetPlayer().GetGUID()))
|
||||
{
|
||||
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||
{
|
||||
Player groupGuy = refe.GetSource();
|
||||
if (!groupGuy)
|
||||
continue;
|
||||
if (!group.IsLeader(_player.GetGUID()))
|
||||
return;
|
||||
|
||||
if (!groupGuy.IsInWorld)
|
||||
return;
|
||||
if (group.IsLFGGroup())
|
||||
return;
|
||||
|
||||
if (groupGuy.GetMap().IsRaid())
|
||||
{
|
||||
Log.outDebug(LogFilter.Network, "WorldSession:HandleSetRaidDifficulty: player {0} tried to reset the instance while inside!", GetPlayer().GetGUID().ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// the difficulty is set even if the instances can't be reset
|
||||
group.ResetInstances(InstanceResetMethod.ChangeDifficulty, true, setRaidDifficulty.Legacy != 0, GetPlayer());
|
||||
if (setRaidDifficulty.Legacy != 0)
|
||||
group.SetLegacyRaidDifficultyID(difficultyID);
|
||||
else
|
||||
group.SetRaidDifficultyID(difficultyID);
|
||||
}
|
||||
// the difficulty is set even if the instances can't be reset
|
||||
group.ResetInstances(InstanceResetMethod.OnChangeDifficulty, _player);
|
||||
if (setRaidDifficulty.Legacy != 0)
|
||||
group.SetLegacyRaidDifficultyID(difficultyID);
|
||||
else
|
||||
group.SetRaidDifficultyID(difficultyID);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetPlayer().ResetInstances(InstanceResetMethod.ChangeDifficulty, true, setRaidDifficulty.Legacy != 0);
|
||||
GetPlayer().ResetInstances(InstanceResetMethod.OnChangeDifficulty);
|
||||
if (setRaidDifficulty.Legacy != 0)
|
||||
GetPlayer().SetLegacyRaidDifficultyID(difficultyID);
|
||||
else
|
||||
|
||||
+27
-62
@@ -4786,6 +4786,9 @@ namespace Game.Maps
|
||||
return EnterState.CannotEnterAlreadyInMap;
|
||||
}
|
||||
|
||||
if (m_shuttingDown)
|
||||
return EnterState.CannotEnterInstanceShuttingDown;
|
||||
|
||||
// allow GM's to enter
|
||||
if (player.IsGameMaster())
|
||||
return base.CannotEnter(player);
|
||||
@@ -4842,16 +4845,10 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
// for normal instances cancel the reset schedule when the
|
||||
// first player enters (no players yet)
|
||||
SetResetSchedule(false);
|
||||
|
||||
Log.outInfo(LogFilter.Maps, "MAP: Player '{0}' entered instance '{1}' of map '{2}'", player.GetName(),
|
||||
GetInstanceId(), GetMapName());
|
||||
// initialize unload state
|
||||
m_unloadTimer = 0;
|
||||
m_resetAfterUnload = false;
|
||||
m_unloadWhenEmpty = false;
|
||||
|
||||
// this will acquire the same mutex so it cannot be in the previous block
|
||||
base.AddPlayerToMap(player, initPlayer);
|
||||
@@ -4888,15 +4885,12 @@ namespace Game.Maps
|
||||
|
||||
// if last player set unload timer
|
||||
if (m_unloadTimer == 0 && GetPlayers().Count == 1)
|
||||
m_unloadTimer = m_unloadWhenEmpty ? 1 : (uint)Math.Max(WorldConfig.GetIntValue(WorldCfg.InstanceUnloadDelay), 1);
|
||||
m_unloadTimer = m_shuttingDown ? 1 : (uint)Math.Max(WorldConfig.GetIntValue(WorldCfg.InstanceUnloadDelay), 1);
|
||||
|
||||
if (i_scenario != null)
|
||||
i_scenario.OnPlayerExit(player);
|
||||
|
||||
base.RemovePlayerFromMap(player, remove);
|
||||
|
||||
// for normal instances schedule the reset after all players have left
|
||||
SetResetSchedule(true);
|
||||
}
|
||||
|
||||
public void CreateInstanceData()
|
||||
@@ -4942,47 +4936,44 @@ namespace Game.Maps
|
||||
i_owningGroupRef.Link(group, this);
|
||||
}
|
||||
|
||||
public bool Reset(InstanceResetMethod method)
|
||||
public InstanceResetResult Reset(InstanceResetMethod method)
|
||||
{
|
||||
// note: since the map may not be loaded when the instance needs to be reset
|
||||
// the instance must be deleted from the DB
|
||||
// raids can be reset if no boss was killed
|
||||
if (method != InstanceResetMethod.Expire && i_instanceLock != null && i_instanceLock.GetData().CompletedEncountersMask != 0)
|
||||
return InstanceResetResult.CannotReset;
|
||||
|
||||
if (HavePlayers())
|
||||
{
|
||||
// on manual reset, fail
|
||||
if (method == InstanceResetMethod.All || method == InstanceResetMethod.ChangeDifficulty)
|
||||
switch (method)
|
||||
{
|
||||
// notify the players to leave the instance so it can be reset
|
||||
foreach (Player player in GetPlayers())
|
||||
player.SendResetFailedNotify(GetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
// on lock expiration boot players (do we also care about extension state?)
|
||||
if (method == InstanceResetMethod.Global)
|
||||
{
|
||||
case InstanceResetMethod.Manual:
|
||||
// notify the players to leave the instance so it can be reset
|
||||
foreach (var player in GetPlayers())
|
||||
player.SendResetFailedNotify(GetId());
|
||||
break;
|
||||
case InstanceResetMethod.OnChangeDifficulty:
|
||||
// no client notification
|
||||
break;
|
||||
case InstanceResetMethod.Expire:
|
||||
// on lock expiration boot players (do we also care about extension state?)
|
||||
// set the homebind timer for players inside (1 minute)
|
||||
foreach (Player player in GetPlayers())
|
||||
player.m_InstanceValid = false;
|
||||
}
|
||||
|
||||
if (!HasPermBoundPlayers())
|
||||
{
|
||||
// the unload timer is not started
|
||||
// instead the map will unload immediately after the players have left
|
||||
m_unloadWhenEmpty = true;
|
||||
m_resetAfterUnload = true;
|
||||
}
|
||||
m_shuttingDown = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return InstanceResetResult.NotEmpty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unloaded at next update
|
||||
m_unloadTimer = 1;
|
||||
m_resetAfterUnload = !(method == InstanceResetMethod.Global && HasPermBoundPlayers());
|
||||
}
|
||||
|
||||
return GetPlayers().Empty();
|
||||
return InstanceResetResult.Success;
|
||||
}
|
||||
|
||||
public string GetScriptName()
|
||||
@@ -5097,31 +5088,6 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnloadAll()
|
||||
{
|
||||
Cypher.Assert(!HavePlayers());
|
||||
|
||||
if (m_resetAfterUnload)
|
||||
{
|
||||
DeleteRespawnTimes();
|
||||
DeleteCorpseData();
|
||||
}
|
||||
|
||||
base.UnloadAll();
|
||||
}
|
||||
|
||||
public void SetResetSchedule(bool on)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HasPermBoundPlayers()
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_PERM_BIND_BY_INSTANCE);
|
||||
stmt.AddValue(0, GetInstanceId());
|
||||
return !DB.Characters.Query(stmt).IsEmpty();
|
||||
}
|
||||
|
||||
public uint GetMaxPlayers()
|
||||
{
|
||||
MapDifficultyRecord mapDiff = GetMapDifficulty();
|
||||
@@ -5168,8 +5134,7 @@ namespace Game.Maps
|
||||
InstanceScenario i_scenario;
|
||||
InstanceLock i_instanceLock;
|
||||
GroupInstanceReference i_owningGroupRef = new();
|
||||
bool m_resetAfterUnload;
|
||||
bool m_unloadWhenEmpty;
|
||||
bool m_shuttingDown;
|
||||
}
|
||||
|
||||
public class BattlegroundMap : Map
|
||||
|
||||
@@ -326,8 +326,8 @@ namespace Game.Entities
|
||||
|
||||
map.UnloadAll();
|
||||
|
||||
// Free up the instance id and allow it to be reused for bgs and arenas (other instances are handled in the InstanceSaveMgr)
|
||||
if (map.IsBattlegroundOrArena())
|
||||
// Free up the instance id and allow it to be reused for normal dungeons, bgs and arenas
|
||||
if (map.IsBattlegroundOrArena() || (map.IsDungeon() && !map.GetMapDifficulty().HasResetSchedule()))
|
||||
FreeInstanceId(map.GetInstanceId());
|
||||
|
||||
// erase map
|
||||
|
||||
Reference in New Issue
Block a user