Core/Instances: Reimplement resetting instances from UI

Port From (https://github.com/TrinityCore/TrinityCore/commit/ddd0c7b152d65b980db0cc2d00f15ae62a602936)
This commit is contained in:
hondacrx
2022-10-04 14:31:07 -04:00
parent 67d6fa48eb
commit 807caef08c
7 changed files with 147 additions and 140 deletions
+11 -6
View File
@@ -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
}
+8
View File
@@ -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;
}
+33 -3
View File
@@ -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
View File
@@ -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)
+40 -52
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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