Core/Instances: Delete InstanceSaveMgr and replace most of its uses with new InstanceLockMgr
Port From (https://github.com/TrinityCore/TrinityCore/commit/9b924522d0549dd67b10e2cbdfc20297dd21e182)
This commit is contained in:
@@ -740,14 +740,6 @@ namespace Framework.Constants
|
||||
Error = 1
|
||||
}
|
||||
|
||||
public enum BindExtensionState
|
||||
{
|
||||
Expired = 0,
|
||||
Normal = 1,
|
||||
Extended = 2,
|
||||
Keep = 255 // special state: keep current save type
|
||||
}
|
||||
|
||||
public enum TalentLearnResult
|
||||
{
|
||||
LearnOk = 0,
|
||||
|
||||
@@ -879,28 +879,6 @@ namespace Game.Chat
|
||||
return true;
|
||||
}
|
||||
|
||||
if (difficulty == 0)
|
||||
{
|
||||
handler.SendSysMessage($"Resetting all difficulties for '{mEntry.MapName[handler.GetSessionDbcLocale()]}'.");
|
||||
foreach (var diff in CliDB.DifficultyStorage.Values)
|
||||
{
|
||||
if (Global.DB2Mgr.GetMapDifficultyData(mapId, (Difficulty)diff.Id) != null)
|
||||
{
|
||||
handler.SendSysMessage($"Resetting difficulty {diff.Id} for '{mEntry.MapName[handler.GetSessionDbcLocale()]}'.");
|
||||
Global.InstanceSaveMgr.ForceGlobalReset(mapId, (Difficulty)diff.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mEntry.IsNonRaidDungeon() && difficulty == (int)Difficulty.Normal)
|
||||
{
|
||||
handler.SendSysMessage($"'{mEntry.MapName[handler.GetSessionDbcLocale()]}' does not have any permanent saves for difficulty {(Difficulty)difficulty}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
handler.SendSysMessage($"Resetting difficulty {(Difficulty)difficulty} for '{mEntry.MapName[handler.GetSessionDbcLocale()]}'.");
|
||||
Global.InstanceSaveMgr.ForceGlobalReset(mapId, (Difficulty)difficulty);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Game.Chat
|
||||
[Command("listbinds", RBACPermissions.CommandInstanceListbinds)]
|
||||
static bool HandleInstanceListBindsCommand(CommandHandler handler)
|
||||
{
|
||||
Player player = handler.GetSelectedPlayer();
|
||||
/*Player player = handler.GetSelectedPlayer();
|
||||
if (!player)
|
||||
player = handler.GetSession().GetPlayer();
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Game.Chat
|
||||
}
|
||||
}
|
||||
}
|
||||
handler.SendSysMessage("group binds: {0}", counter);
|
||||
handler.SendSysMessage("group binds: {0}", counter);*/
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -186,9 +186,9 @@ namespace Game.Chat
|
||||
{
|
||||
handler.SendSysMessage("instances loaded: {0}", Global.MapMgr.GetNumInstances());
|
||||
handler.SendSysMessage("players in instances: {0}", Global.MapMgr.GetNumPlayersInInstances());
|
||||
handler.SendSysMessage("instance saves: {0}", Global.InstanceSaveMgr.GetNumInstanceSaves());
|
||||
handler.SendSysMessage("players bound: {0}", Global.InstanceSaveMgr.GetNumBoundPlayersTotal());
|
||||
handler.SendSysMessage("groups bound: {0}", Global.InstanceSaveMgr.GetNumBoundGroupsTotal());
|
||||
//handler.SendSysMessage("instance saves: {0}", Global.InstanceSaveMgr.GetNumInstanceSaves());
|
||||
//handler.SendSysMessage("players bound: {0}", Global.InstanceSaveMgr.GetNumBoundPlayersTotal());
|
||||
//handler.SendSysMessage("groups bound: {0}", Global.InstanceSaveMgr.GetNumBoundGroupsTotal());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -196,7 +196,7 @@ namespace Game.Chat
|
||||
[Command("unbind", RBACPermissions.CommandInstanceUnbind)]
|
||||
static bool HandleInstanceUnbindCommand(CommandHandler handler, string mapArg, byte? difficultyArg)
|
||||
{
|
||||
Player player = handler.GetSelectedPlayer();
|
||||
/*Player player = handler.GetSelectedPlayer();
|
||||
if (!player)
|
||||
player = handler.GetSession().GetPlayer();
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Game.Chat
|
||||
}
|
||||
}
|
||||
}
|
||||
handler.SendSysMessage("instances unbound: {0}", counter);
|
||||
handler.SendSysMessage("instances unbound: {0}", counter);*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -795,21 +795,17 @@ namespace Game.DungeonFinding
|
||||
LFGDungeonData dungeon = GetLFGDungeon(dungeonId);
|
||||
Cypher.Assert(dungeon != null);
|
||||
Cypher.Assert(player);
|
||||
InstanceBind playerBind = player.GetBoundInstance(dungeon.map, dungeon.difficulty);
|
||||
MapDb2Entries entries = new(dungeon.map, dungeon.difficulty);
|
||||
InstanceLock playerBind = Global.InstanceLockMgr.FindActiveInstanceLock(guid, entries);
|
||||
if (playerBind != null)
|
||||
{
|
||||
InstanceSave playerSave = playerBind.save;
|
||||
if (playerSave != null)
|
||||
{
|
||||
uint dungeonInstanceId = playerSave.GetInstanceId();
|
||||
var itLockedDungeon = lockedDungeons.LookupByKey(dungeonId);
|
||||
if (itLockedDungeon == 0 || itLockedDungeon == dungeonInstanceId)
|
||||
uint dungeonInstanceId = playerBind.GetInstanceId();
|
||||
if (!lockedDungeons.TryGetValue(dungeonId, out uint lockedDungeon) || lockedDungeon == dungeonInstanceId)
|
||||
eraseDungeon = false;
|
||||
|
||||
lockedDungeons[dungeonId] = dungeonInstanceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eraseDungeon)
|
||||
dungeonsToRemove.Add(dungeonId);
|
||||
@@ -1625,7 +1621,7 @@ namespace Game.DungeonFinding
|
||||
lockStatus = LfgLockStatusType.NotInSeason;
|
||||
else if (Global.DisableMgr.IsDisabledFor(DisableType.LFGMap, dungeon.map, player))
|
||||
lockStatus = LfgLockStatusType.RaidLocked;
|
||||
else if (dungeon.difficulty > Difficulty.Normal && player.GetBoundInstance(dungeon.map, dungeon.difficulty) != null)
|
||||
else if (dungeon.difficulty > Difficulty.Normal && Global.InstanceLockMgr.FindActiveInstanceLock(guid, new MapDb2Entries(dungeon.map, dungeon.difficulty)) != null)
|
||||
lockStatus = LfgLockStatusType.RaidLocked;
|
||||
else if (dungeon.seasonal && !IsSeasonActive(dungeon.id))
|
||||
lockStatus = LfgLockStatusType.NotInSeason;
|
||||
|
||||
@@ -1056,78 +1056,6 @@ namespace Game.Entities
|
||||
|
||||
RemoveAtLoginFlag(AtLoginFlags.Resurrect);
|
||||
}
|
||||
void _LoadBoundInstances(SQLResult result)
|
||||
{
|
||||
m_boundInstances.Clear();
|
||||
|
||||
Group group = GetGroup();
|
||||
|
||||
if (!result.IsEmpty())
|
||||
{
|
||||
do
|
||||
{
|
||||
bool perm = result.Read<bool>(1);
|
||||
uint mapId = result.Read<ushort>(2);
|
||||
uint instanceId = result.Read<uint>(0);
|
||||
byte difficulty = result.Read<byte>(3);
|
||||
BindExtensionState extendState = (BindExtensionState)result.Read<byte>(4);
|
||||
|
||||
long resetTime = result.Read<long>(5);
|
||||
// the resettime for normal instances is only saved when the InstanceSave is unloaded
|
||||
// so the value read from the DB may be wrong here but only if the InstanceSave is loaded
|
||||
// and in that case it is not used
|
||||
|
||||
uint entranceId = result.Read<uint>(6);
|
||||
|
||||
bool deleteInstance = false;
|
||||
|
||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId);
|
||||
string mapname = mapEntry != null ? mapEntry.MapName[Global.WorldMgr.GetDefaultDbcLocale()] : "Unknown";
|
||||
|
||||
if (mapEntry == null || !mapEntry.IsDungeon())
|
||||
{
|
||||
Log.outError(LogFilter.Player, "_LoadBoundInstances: player {0}({1}) has bind to not existed or not dungeon map {2} ({3})", GetName(), GetGUID().ToString(), mapId, mapname);
|
||||
deleteInstance = true;
|
||||
}
|
||||
else if (CliDB.DifficultyStorage.HasRecord(difficulty))
|
||||
{
|
||||
Log.outError(LogFilter.Player, "_LoadBoundInstances: player {0}({1}) has bind to not existed difficulty {2} instance for map {3} ({4})", GetName(), GetGUID().ToString(), difficulty, mapId, mapname);
|
||||
deleteInstance = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetMapDifficultyData(mapId, (Difficulty)difficulty);
|
||||
if (mapDiff == null)
|
||||
{
|
||||
Log.outError(LogFilter.Player, "_LoadBoundInstances: player {0}({1}) has bind to not existed difficulty {2} instance for map {3} ({4})", GetName(), GetGUID().ToString(), difficulty, mapId, mapname);
|
||||
deleteInstance = true;
|
||||
}
|
||||
else if (!perm && group)
|
||||
{
|
||||
Log.outError(LogFilter.Player, "_LoadBoundInstances: player {0}({1}) is in group {2} but has a non-permanent character bind to map {3} ({4}), {5}, {6}",
|
||||
GetName(), GetGUID().ToString(), group.GetGUID().ToString(), mapId, mapname, instanceId, difficulty);
|
||||
deleteInstance = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteInstance)
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, instanceId);
|
||||
DB.Characters.Execute(stmt);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// since non permanent binds are always solo bind, they can always be reset
|
||||
InstanceSave save = Global.InstanceSaveMgr.AddInstanceSave(mapId, instanceId, (Difficulty)difficulty, resetTime, entranceId, !perm, true);
|
||||
if (save != null)
|
||||
BindToInstance(save, perm, extendState, true);
|
||||
}
|
||||
while (result.NextRow());
|
||||
}
|
||||
}
|
||||
void _LoadVoidStorage(SQLResult result)
|
||||
{
|
||||
if (result.IsEmpty())
|
||||
@@ -2793,7 +2721,6 @@ namespace Game.Entities
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TodayHonorableKills), todayKills);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.YesterdayHonorableKills), yesterdayKills);
|
||||
|
||||
_LoadBoundInstances(holder.GetResult(PlayerLoginQueryLoad.BoundInstances));
|
||||
_LoadInstanceTimeRestrictions(holder.GetResult(PlayerLoginQueryLoad.InstanceLockTimes));
|
||||
_LoadBGData(holder.GetResult(PlayerLoginQueryLoad.BgData));
|
||||
|
||||
@@ -2976,15 +2903,6 @@ namespace Game.Entities
|
||||
Log.outDebug(LogFilter.Player, "Player {0} using client without required expansion tried login at non accessible map {1}", GetName(), mapId);
|
||||
RelocateToHomebind();
|
||||
}
|
||||
|
||||
// fix crash (because of if (Map* map = _FindMap(instanceId)) in MapInstanced.CreateInstance)
|
||||
if (instance_id != 0)
|
||||
{
|
||||
InstanceSave save = GetInstanceSave(mapId);
|
||||
if (save != null)
|
||||
if (save.GetInstanceId() != instance_id)
|
||||
instance_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// NOW player must have valid map
|
||||
@@ -3028,7 +2946,7 @@ namespace Game.Entities
|
||||
areaTrigger = Global.ObjectMgr.GetGoBackTrigger(mapId);
|
||||
check = true;
|
||||
}
|
||||
else if (instance_id != 0 && Global.InstanceSaveMgr.GetInstanceSave(instance_id) == null) // ... and instance is reseted then look for entrance.
|
||||
else if (instance_id != 0 && Global.InstanceLockMgr.FindActiveInstanceLock(guid, new MapDb2Entries(mapId, map.GetDifficultyID())) != null) // ... and instance is reseted then look for entrance.
|
||||
{
|
||||
areaTrigger = Global.ObjectMgr.GetMapEntranceTrigger(mapId);
|
||||
check = true;
|
||||
|
||||
@@ -78,7 +78,6 @@ namespace Game.Entities
|
||||
bool m_bPassOnGroupLoot;
|
||||
GroupUpdateCounter[] m_groupUpdateSequences = new GroupUpdateCounter[2];
|
||||
|
||||
public Dictionary<Difficulty, Dictionary<uint, InstanceBind>> m_boundInstances = new();
|
||||
Dictionary<uint, uint> m_recentInstances = new();
|
||||
Dictionary<uint, long> _instanceResetTimes = new();
|
||||
uint _pendingBindId;
|
||||
|
||||
@@ -290,152 +290,6 @@ namespace Game.Entities
|
||||
public ZonePVPTypeOverride GetOverrideZonePVPType() { return (ZonePVPTypeOverride)(uint)m_activePlayerData.OverrideZonePVPType; }
|
||||
public void SetOverrideZonePVPType(ZonePVPTypeOverride type) { SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.OverrideZonePVPType), (uint)type); }
|
||||
|
||||
public InstanceBind GetBoundInstance(uint mapid, Difficulty difficulty, bool withExpired = false)
|
||||
{
|
||||
// some instances only have one difficulty
|
||||
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetDownscaledMapDifficultyData(mapid, ref difficulty);
|
||||
if (mapDiff == null)
|
||||
return null;
|
||||
|
||||
var difficultyDic = m_boundInstances.LookupByKey(difficulty);
|
||||
if (difficultyDic == null)
|
||||
return null;
|
||||
|
||||
var instanceBind = difficultyDic.LookupByKey(mapid);
|
||||
if (instanceBind != null)
|
||||
if (instanceBind.extendState != 0 || withExpired)
|
||||
return instanceBind;
|
||||
|
||||
return null;
|
||||
}
|
||||
public Dictionary<uint, InstanceBind> GetBoundInstances(Difficulty difficulty) { return m_boundInstances.LookupByKey(difficulty); }
|
||||
|
||||
public InstanceSave GetInstanceSave(uint mapid)
|
||||
{
|
||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapid);
|
||||
InstanceBind pBind = GetBoundInstance(mapid, GetDifficultyID(mapEntry));
|
||||
InstanceSave pSave = pBind?.save;
|
||||
if (pBind == null || !pBind.perm)
|
||||
{
|
||||
Group group = GetGroup();
|
||||
if (group)
|
||||
{
|
||||
InstanceBind groupBind = group.GetBoundInstance(GetDifficultyID(mapEntry), mapid);
|
||||
if (groupBind != null)
|
||||
pSave = groupBind.save;
|
||||
}
|
||||
}
|
||||
|
||||
return pSave;
|
||||
}
|
||||
|
||||
public void UnbindInstance(uint mapid, Difficulty difficulty, bool unload = false)
|
||||
{
|
||||
var difficultyDic = m_boundInstances.LookupByKey(difficulty);
|
||||
if (difficultyDic != null)
|
||||
{
|
||||
var pair = difficultyDic.Find(mapid);
|
||||
if (pair.Value != null)
|
||||
UnbindInstance(pair, difficultyDic, unload);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnbindInstance(KeyValuePair<uint, InstanceBind> pair, Dictionary<uint, InstanceBind> difficultyDic, bool unload)
|
||||
{
|
||||
if (pair.Value != null)
|
||||
{
|
||||
if (!unload)
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_INSTANCE_BY_INSTANCE_GUID);
|
||||
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, pair.Value.save.GetInstanceId());
|
||||
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
if (pair.Value.perm)
|
||||
GetSession().SendCalendarRaidLockoutRemoved(pair.Value.save);
|
||||
|
||||
pair.Value.save.RemovePlayer(this); // save can become invalid
|
||||
difficultyDic.Remove(pair.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public InstanceBind BindToInstance(InstanceSave save, bool permanent, BindExtensionState extendState = BindExtensionState.Normal, bool load = false)
|
||||
{
|
||||
if (save != null)
|
||||
{
|
||||
InstanceBind bind = new();
|
||||
if (m_boundInstances.ContainsKey(save.GetDifficultyID()) && m_boundInstances[save.GetDifficultyID()].ContainsKey(save.GetMapId()))
|
||||
bind = m_boundInstances[save.GetDifficultyID()][save.GetMapId()];
|
||||
|
||||
if (extendState == BindExtensionState.Keep) // special flag, keep the player's current extend state when updating for new boss down
|
||||
{
|
||||
if (save == bind.save)
|
||||
extendState = bind.extendState;
|
||||
else
|
||||
extendState = BindExtensionState.Normal;
|
||||
}
|
||||
|
||||
if (!load)
|
||||
{
|
||||
PreparedStatement stmt;
|
||||
if (bind.save != null)
|
||||
{
|
||||
// update the save when the group kills a boss
|
||||
if (permanent != bind.perm || save != bind.save || extendState != bind.extendState)
|
||||
{
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHAR_INSTANCE);
|
||||
|
||||
stmt.AddValue(0, save.GetInstanceId());
|
||||
stmt.AddValue(1, permanent);
|
||||
stmt.AddValue(2, (byte)extendState);
|
||||
stmt.AddValue(3, GetGUID().GetCounter());
|
||||
stmt.AddValue(4, bind.save.GetInstanceId());
|
||||
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHAR_INSTANCE);
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, save.GetInstanceId());
|
||||
stmt.AddValue(2, permanent);
|
||||
stmt.AddValue(3, (byte)extendState);
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bind.save != save)
|
||||
{
|
||||
if (bind.save != null)
|
||||
bind.save.RemovePlayer(this);
|
||||
save.AddPlayer(this);
|
||||
}
|
||||
|
||||
if (permanent)
|
||||
save.SetCanReset(false);
|
||||
|
||||
bind.save = save;
|
||||
bind.perm = permanent;
|
||||
bind.extendState = extendState;
|
||||
if (!load)
|
||||
Log.outDebug(LogFilter.Maps, "Player.BindToInstance: Player '{0}' ({1}) is now bound to map (ID: {2}, Instance {3}, Difficulty {4})", GetName(), GetGUID().ToString(), save.GetMapId(), save.GetInstanceId(), save.GetDifficultyID());
|
||||
|
||||
Global.ScriptMgr.OnPlayerBindToInstance(this, save.GetDifficultyID(), save.GetMapId(), permanent, extendState);
|
||||
|
||||
if (!m_boundInstances.ContainsKey(save.GetDifficultyID()))
|
||||
m_boundInstances[save.GetDifficultyID()] = new Dictionary<uint, InstanceBind>();
|
||||
|
||||
m_boundInstances[save.GetDifficultyID()][save.GetMapId()] = bind;
|
||||
return bind;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ConfirmPendingBind()
|
||||
{
|
||||
InstanceMap map = GetMap().ToInstanceMap();
|
||||
@@ -466,7 +320,7 @@ namespace Game.Entities
|
||||
lockInfos.InstanceID = instanceLock.GetInstanceId();
|
||||
lockInfos.MapID = instanceLock.GetMapId();
|
||||
lockInfos.DifficultyID = (uint)instanceLock.GetDifficultyId();
|
||||
lockInfos.TimeRemaining = (int)(instanceLock.GetEffectiveExpiryTime() - now).TotalSeconds;
|
||||
lockInfos.TimeRemaining = (int)Math.Max((instanceLock.GetEffectiveExpiryTime() - now).TotalSeconds, 0);
|
||||
lockInfos.CompletedMask = instanceLock.GetData().CompletedEncountersMask;
|
||||
|
||||
lockInfos.Locked = !instanceLock.IsExpired();
|
||||
@@ -646,52 +500,7 @@ 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)
|
||||
{
|
||||
// method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
|
||||
|
||||
// we assume that when the difficulty changes, all instances that can be reset will be
|
||||
Difficulty difficulty = GetDungeonDifficultyID();
|
||||
if (isRaid)
|
||||
{
|
||||
if (!isLegacy)
|
||||
difficulty = GetRaidDifficultyID();
|
||||
else
|
||||
difficulty = GetLegacyRaidDifficultyID();
|
||||
}
|
||||
|
||||
var difficultyDic = m_boundInstances.LookupByKey(difficulty);
|
||||
if (difficultyDic == null)
|
||||
return;
|
||||
|
||||
foreach (var pair in difficultyDic)
|
||||
{
|
||||
InstanceSave p = pair.Value.save;
|
||||
MapRecord entry = CliDB.MapStorage.LookupByKey(difficulty);
|
||||
if (entry == null || entry.IsRaid() != isRaid || !p.CanReset())
|
||||
continue;
|
||||
|
||||
if (method == InstanceResetMethod.All)
|
||||
{
|
||||
// the "reset all instances" method can only reset normal maps
|
||||
if (entry.InstanceType == MapTypes.Raid || difficulty == Difficulty.Heroic)
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the map is loaded, reset it
|
||||
Map map = Global.MapMgr.FindMap(p.GetMapId(), p.GetInstanceId());
|
||||
if (map != null && map.IsDungeon())
|
||||
if (!map.ToInstanceMap().Reset(method))
|
||||
continue;
|
||||
|
||||
// since this is a solo instance there should not be any players inside
|
||||
if (method == InstanceResetMethod.All || method == InstanceResetMethod.ChangeDifficulty)
|
||||
SendResetInstanceSuccess(p.GetMapId());
|
||||
|
||||
p.DeleteFromDB();
|
||||
difficultyDic.Remove(pair.Key);
|
||||
|
||||
// the following should remove the instance save from the manager and delete it as well
|
||||
p.RemovePlayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendResetInstanceSuccess(uint MapId)
|
||||
@@ -719,35 +528,6 @@ namespace Game.Entities
|
||||
SendPacket(transferAborted);
|
||||
}
|
||||
|
||||
public void SendInstanceResetWarning(uint mapid, Difficulty difficulty, uint time, bool welcome)
|
||||
{
|
||||
// type of warning, based on the time remaining until reset
|
||||
InstanceResetWarningType type;
|
||||
if (welcome)
|
||||
type = InstanceResetWarningType.Welcome;
|
||||
else if (time > 21600)
|
||||
type = InstanceResetWarningType.Welcome;
|
||||
else if (time > 3600)
|
||||
type = InstanceResetWarningType.WarningHours;
|
||||
else if (time > 300)
|
||||
type = InstanceResetWarningType.WarningMin;
|
||||
else
|
||||
type = InstanceResetWarningType.WarningMinSoon;
|
||||
|
||||
RaidInstanceMessage raidInstanceMessage = new();
|
||||
raidInstanceMessage.Type = type;
|
||||
raidInstanceMessage.MapID = mapid;
|
||||
raidInstanceMessage.DifficultyID = difficulty;
|
||||
|
||||
InstanceBind bind = GetBoundInstance(mapid, difficulty);
|
||||
if (bind != null)
|
||||
raidInstanceMessage.Locked = bind.perm;
|
||||
else
|
||||
raidInstanceMessage.Locked = false;
|
||||
raidInstanceMessage.Extended = false;
|
||||
SendPacket(raidInstanceMessage);
|
||||
}
|
||||
|
||||
public override void ProcessTerrainStatusUpdate(ZLiquidStatus oldLiquidStatus, LiquidData newLiquidData)
|
||||
{
|
||||
// process liquid auras using generic unit code
|
||||
|
||||
@@ -727,11 +727,6 @@ namespace Game.Entities
|
||||
|
||||
if (GetTransport() != null)
|
||||
GetTransport().RemovePassenger(this);
|
||||
|
||||
// clean up player-instance binds, may unload some instance saves
|
||||
foreach (var difficultyDic in m_boundInstances.Values)
|
||||
foreach (var instanceBind in difficultyDic.Values)
|
||||
instanceBind.save.RemovePlayer(this);
|
||||
}
|
||||
|
||||
public override void AddToWorld()
|
||||
|
||||
@@ -73,7 +73,6 @@ public static class Global
|
||||
public static VMapManager VMapMgr { get { return VMapManager.Instance; } }
|
||||
public static WaypointManager WaypointMgr { get { return WaypointManager.Instance; } }
|
||||
public static TransportManager TransportMgr { get { return TransportManager.Instance; } }
|
||||
public static InstanceSaveManager InstanceSaveMgr { get { return InstanceSaveManager.Instance; } }
|
||||
public static InstanceLockManager InstanceLockMgr { get { return InstanceLockManager.Instance; } }
|
||||
public static ScenarioManager ScenarioMgr { get { return ScenarioManager.Instance; } }
|
||||
|
||||
|
||||
@@ -12061,4 +12061,10 @@ namespace Game
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InstanceTemplate
|
||||
{
|
||||
public uint Parent;
|
||||
public uint ScriptId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,26 +673,6 @@ namespace Game.Groups
|
||||
PreparedStatement stmt;
|
||||
SQLTransaction trans = new();
|
||||
|
||||
// Remove the groups permanent instance bindings
|
||||
foreach (var difficultyDic in m_boundInstances.Values)
|
||||
{
|
||||
foreach (var pair in difficultyDic.ToList())
|
||||
{
|
||||
// Do not unbind saves of instances that already had map created (a newLeader entered)
|
||||
// forcing a new instance with another leader requires group disbanding (confirmed on retail)
|
||||
if (pair.Value.perm && Global.MapMgr.FindMap(pair.Key, pair.Value.save.GetInstanceId()) == null)
|
||||
{
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GROUP_INSTANCE_PERM_BINDING);
|
||||
stmt.AddValue(0, m_dbStoreId);
|
||||
stmt.AddValue(1, pair.Value.save.GetInstanceId());
|
||||
trans.Append(stmt);
|
||||
|
||||
pair.Value.save.RemoveGroup(this);
|
||||
difficultyDic.Remove(pair.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the group leader
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_GROUP_LEADER);
|
||||
|
||||
@@ -1381,166 +1361,7 @@ namespace Game.Groups
|
||||
|
||||
public void ResetInstances(InstanceResetMethod method, bool isRaid, bool isLegacy, Player SendMsgTo)
|
||||
{
|
||||
if (IsBGGroup() || IsBFGroup())
|
||||
return;
|
||||
|
||||
// method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_DISBAND
|
||||
|
||||
// we assume that when the difficulty changes, all instances that can be reset will be
|
||||
Difficulty diff = GetDungeonDifficultyID();
|
||||
if (isRaid)
|
||||
{
|
||||
if (!isLegacy)
|
||||
diff = GetRaidDifficultyID();
|
||||
else
|
||||
diff = GetLegacyRaidDifficultyID();
|
||||
}
|
||||
|
||||
var difficultyDic = m_boundInstances.LookupByKey(diff);
|
||||
if (difficultyDic == null)
|
||||
return;
|
||||
|
||||
foreach (var pair in difficultyDic.ToList())
|
||||
{
|
||||
InstanceSave instanceSave = pair.Value.save;
|
||||
MapRecord entry = CliDB.MapStorage.LookupByKey(pair.Key);
|
||||
if (entry == null || entry.IsRaid() != isRaid || (!instanceSave.CanReset() && method != InstanceResetMethod.GroupDisband))
|
||||
continue;
|
||||
|
||||
if (method == InstanceResetMethod.All)
|
||||
{
|
||||
// the "reset all instances" method can only reset normal maps
|
||||
if (entry.InstanceType == MapTypes.Raid || diff == Difficulty.Heroic)
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isEmpty = true;
|
||||
// if the map is loaded, reset it
|
||||
Map map = Global.MapMgr.FindMap(instanceSave.GetMapId(), instanceSave.GetInstanceId());
|
||||
if (map && map.IsDungeon() && !(method == InstanceResetMethod.GroupDisband && !instanceSave.CanReset()))
|
||||
{
|
||||
if (instanceSave.CanReset())
|
||||
isEmpty = ((InstanceMap)map).Reset(method);
|
||||
else
|
||||
isEmpty = !map.HavePlayers();
|
||||
}
|
||||
|
||||
if (SendMsgTo)
|
||||
{
|
||||
if (!isEmpty)
|
||||
SendMsgTo.SendResetInstanceFailed(ResetFailedReason.Failed, instanceSave.GetMapId());
|
||||
else if (WorldConfig.GetBoolValue(WorldCfg.InstancesResetAnnounce))
|
||||
{
|
||||
Group group = SendMsgTo.GetGroup();
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||
{
|
||||
Player player = refe.GetSource();
|
||||
if (player)
|
||||
player.SendResetInstanceSuccess(instanceSave.GetMapId());
|
||||
}
|
||||
}
|
||||
else
|
||||
SendMsgTo.SendResetInstanceSuccess(instanceSave.GetMapId());
|
||||
}
|
||||
else
|
||||
SendMsgTo.SendResetInstanceSuccess(instanceSave.GetMapId());
|
||||
}
|
||||
|
||||
if (isEmpty || method == InstanceResetMethod.GroupDisband || method == InstanceResetMethod.ChangeDifficulty)
|
||||
{
|
||||
// do not reset the instance, just unbind if others are permanently bound to it
|
||||
if (instanceSave.CanReset())
|
||||
{
|
||||
if (map != null && map.IsDungeon() && SendMsgTo)
|
||||
{
|
||||
AreaTriggerStruct instanceEntrance = Global.ObjectMgr.GetGoBackTrigger(map.GetId());
|
||||
|
||||
if (instanceEntrance == null)
|
||||
Log.outDebug(LogFilter.Misc, $"Instance entrance not found for map {map.GetId()}");
|
||||
else
|
||||
{
|
||||
WorldSafeLocsEntry graveyardLocation = Global.ObjectMgr.GetClosestGraveYard(
|
||||
new WorldLocation(instanceEntrance.target_mapId, instanceEntrance.target_X, instanceEntrance.target_Y, instanceEntrance.target_Z),
|
||||
SendMsgTo.GetTeam(), null);
|
||||
uint zoneId = Global.TerrainMgr.GetZoneId(PhasingHandler.EmptyPhaseShift, graveyardLocation.Loc.GetMapId(),
|
||||
graveyardLocation.Loc.GetPositionX(), graveyardLocation.Loc.GetPositionY(), graveyardLocation.Loc.GetPositionZ());
|
||||
|
||||
foreach (MemberSlot member in GetMemberSlots())
|
||||
{
|
||||
if (!Global.ObjAccessor.FindConnectedPlayer(member.guid))
|
||||
{
|
||||
var stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_CHARACTER_POSITION_BY_MAPID);
|
||||
|
||||
stmt.AddValue(0, graveyardLocation.Loc.GetPositionX());
|
||||
stmt.AddValue(1, graveyardLocation.Loc.GetPositionY());
|
||||
stmt.AddValue(2, graveyardLocation.Loc.GetPositionZ());
|
||||
stmt.AddValue(3, instanceEntrance.target_Orientation);
|
||||
stmt.AddValue(4, graveyardLocation.Loc.GetMapId());
|
||||
stmt.AddValue(5, zoneId);
|
||||
stmt.AddValue(6, member.guid.GetCounter());
|
||||
stmt.AddValue(7, map.GetId());
|
||||
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
instanceSave.DeleteFromDB();
|
||||
}
|
||||
else
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GROUP_INSTANCE_BY_INSTANCE);
|
||||
stmt.AddValue(0, instanceSave.GetInstanceId());
|
||||
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
|
||||
difficultyDic.Remove(pair.Key);
|
||||
// this unloads the instance save unless online players are bound to it
|
||||
// (eg. permanent binds or GM solo binds)
|
||||
instanceSave.RemoveGroup(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InstanceBind GetBoundInstance(Player player)
|
||||
{
|
||||
uint mapid = player.GetMapId();
|
||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapid);
|
||||
return GetBoundInstance(mapEntry);
|
||||
}
|
||||
|
||||
public InstanceBind GetBoundInstance(Map aMap)
|
||||
{
|
||||
return GetBoundInstance(aMap.GetEntry());
|
||||
}
|
||||
|
||||
public InstanceBind GetBoundInstance(MapRecord mapEntry)
|
||||
{
|
||||
if (mapEntry == null || !mapEntry.IsDungeon())
|
||||
return null;
|
||||
|
||||
Difficulty difficulty = GetDifficultyID(mapEntry);
|
||||
return GetBoundInstance(difficulty, mapEntry.Id);
|
||||
}
|
||||
|
||||
public InstanceBind GetBoundInstance(Difficulty difficulty, uint mapId)
|
||||
{
|
||||
// some instances only have one difficulty
|
||||
Global.DB2Mgr.GetDownscaledMapDifficultyData(mapId, ref difficulty);
|
||||
|
||||
var difficultyDic = m_boundInstances.LookupByKey(difficulty);
|
||||
if (difficultyDic == null)
|
||||
return null;
|
||||
|
||||
var instanceBind = difficultyDic.LookupByKey(mapId);
|
||||
if (instanceBind != null)
|
||||
return instanceBind;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void LinkOwnedInstance(GroupInstanceReference refe)
|
||||
@@ -1548,30 +1369,6 @@ namespace Game.Groups
|
||||
m_ownedInstancesMgr.InsertLast(refe);
|
||||
}
|
||||
|
||||
public void UnbindInstance(uint mapid, Difficulty difficulty, bool unload = false)
|
||||
{
|
||||
var difficultyDic = m_boundInstances.LookupByKey(difficulty);
|
||||
if (difficultyDic == null)
|
||||
return;
|
||||
|
||||
var instanceBind = difficultyDic.LookupByKey(mapid);
|
||||
if (instanceBind != null)
|
||||
{
|
||||
if (!unload)
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GROUP_INSTANCE_BY_GUID);
|
||||
|
||||
stmt.AddValue(0, m_dbStoreId);
|
||||
stmt.AddValue(1, instanceBind.save.GetInstanceId());
|
||||
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
instanceBind.save.RemoveGroup(this); // save can become invalid
|
||||
difficultyDic.Remove(mapid);
|
||||
}
|
||||
}
|
||||
|
||||
void _homebindIfInstance(Player player)
|
||||
{
|
||||
if (player && !player.IsGameMaster() && CliDB.MapStorage.LookupByKey(player.GetMapId()).IsDungeon())
|
||||
@@ -1986,11 +1783,6 @@ namespace Game.Groups
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<uint, InstanceBind> GetBoundInstances(Difficulty difficulty)
|
||||
{
|
||||
return m_boundInstances.LookupByKey(difficulty);
|
||||
}
|
||||
|
||||
void _initRaidSubGroupsCounter()
|
||||
{
|
||||
// Sub group counters initialization
|
||||
@@ -2077,8 +1869,6 @@ namespace Game.Groups
|
||||
worker(refe.GetSource());
|
||||
}
|
||||
|
||||
uint GetInstanceId(MapRecord mapEntry) { return 0; }
|
||||
|
||||
public ObjectGuid GetRecentInstanceOwner(uint mapId)
|
||||
{
|
||||
|
||||
@@ -2119,7 +1909,6 @@ namespace Game.Groups
|
||||
ItemQuality m_lootThreshold;
|
||||
ObjectGuid m_looterGuid;
|
||||
ObjectGuid m_masterLooterGuid;
|
||||
Dictionary<Difficulty, Dictionary<uint, InstanceBind>> m_boundInstances = new();
|
||||
Dictionary<uint, Tuple<ObjectGuid, uint>> m_recentInstances = new();
|
||||
GroupInstanceRefManager m_ownedInstancesMgr = new();
|
||||
byte[] m_subGroupsCounts;
|
||||
|
||||
@@ -182,49 +182,6 @@ namespace Game.Groups
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} group members in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Group instance saves...");
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
// 0 1 2 3 4 5 6
|
||||
SQLResult result = DB.Characters.Query("SELECT gi.guid, i.map, gi.instance, gi.permanent, i.difficulty, i.resettime, i.entranceId, " +
|
||||
// 7
|
||||
"(SELECT COUNT(1) FROM character_instance ci LEFT JOIN `groups` g ON ci.guid = g.leaderGuid WHERE ci.instance = gi.instance AND ci.permanent = 1 LIMIT 1) " +
|
||||
"FROM group_instance gi LEFT JOIN instance i ON gi.instance = i.id ORDER BY guid");
|
||||
|
||||
if (result.IsEmpty())
|
||||
{
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 group-instance saves. DB table `group_instance` is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
uint count = 0;
|
||||
do
|
||||
{
|
||||
Group group = GetGroupByDbStoreId(result.Read<uint>(0));
|
||||
// group will never be NULL (we have run consistency sql's before loading)
|
||||
|
||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(result.Read<ushort>(1));
|
||||
if (mapEntry == null || !mapEntry.IsDungeon())
|
||||
{
|
||||
Log.outError(LogFilter.Sql, "Incorrect entry in group_instance table : no dungeon map {0}", result.Read<ushort>(1));
|
||||
continue;
|
||||
}
|
||||
|
||||
uint diff = result.Read<byte>(4);
|
||||
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(diff);
|
||||
if (difficultyEntry == null || difficultyEntry.InstanceType != mapEntry.InstanceType)
|
||||
continue;
|
||||
|
||||
InstanceSave save = Global.InstanceSaveMgr.AddInstanceSave(mapEntry.Id, result.Read<uint>(2), (Difficulty)diff, result.Read<long>(5), result.Read<uint>(6), result.Read<ulong>(7) == 0, true);
|
||||
group.BindToInstance(save, result.Read<bool>(3), true);
|
||||
++count;
|
||||
}
|
||||
while (result.NextRow());
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} group-instance saves in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<ulong, Group> GroupStore = new();
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Game
|
||||
|
||||
lockoutInfo.MapID = (int)instanceLock.GetMapId();
|
||||
lockoutInfo.DifficultyID = (uint)instanceLock.GetDifficultyId();
|
||||
lockoutInfo.ExpireTime = (int)(instanceLock.GetEffectiveExpiryTime() - GameTime.GetSystemTime()).TotalSeconds;
|
||||
lockoutInfo.ExpireTime = (int)Math.Max((instanceLock.GetEffectiveExpiryTime() - GameTime.GetSystemTime()).TotalSeconds, 0);
|
||||
lockoutInfo.InstanceID = instanceLock.GetInstanceId();
|
||||
|
||||
packet.RaidLockouts.Add(lockoutInfo);
|
||||
@@ -538,21 +538,22 @@ namespace Game
|
||||
[WorldPacketHandler(ClientOpcodes.SetSavedInstanceExtend)]
|
||||
void HandleSetSavedInstanceExtend(SetSavedInstanceExtend setSavedInstanceExtend)
|
||||
{
|
||||
Player player = GetPlayer();
|
||||
if (player)
|
||||
{
|
||||
InstanceBind instanceBind = player.GetBoundInstance((uint)setSavedInstanceExtend.MapID, (Difficulty)setSavedInstanceExtend.DifficultyID, setSavedInstanceExtend.Extend); // include expired instances if we are toggling extend on
|
||||
if (instanceBind == null || instanceBind.save == null || !instanceBind.perm)
|
||||
// cannot modify locks currently in use
|
||||
if (_player.GetMapId() == setSavedInstanceExtend.MapID)
|
||||
return;
|
||||
|
||||
BindExtensionState newState;
|
||||
if (!setSavedInstanceExtend.Extend || instanceBind.extendState == BindExtensionState.Expired)
|
||||
newState = BindExtensionState.Normal;
|
||||
else
|
||||
newState = BindExtensionState.Extended;
|
||||
var expiryTimes = Global.InstanceLockMgr.UpdateInstanceLockExtensionForPlayer(_player.GetGUID(), new MapDb2Entries((uint)setSavedInstanceExtend.MapID, (Difficulty)setSavedInstanceExtend.DifficultyID), setSavedInstanceExtend.Extend);
|
||||
|
||||
player.BindToInstance(instanceBind.save, true, newState, false);
|
||||
}
|
||||
if (expiryTimes.Item1 == DateTime.MinValue)
|
||||
return;
|
||||
|
||||
CalendarRaidLockoutUpdated calendarRaidLockoutUpdated = new();
|
||||
calendarRaidLockoutUpdated.ServerTime = GameTime.GetGameTime();
|
||||
calendarRaidLockoutUpdated.MapID = setSavedInstanceExtend.MapID;
|
||||
calendarRaidLockoutUpdated.DifficultyID = setSavedInstanceExtend.DifficultyID;
|
||||
calendarRaidLockoutUpdated.OldTimeRemaining = (int)Math.Max((expiryTimes.Item1 - GameTime.GetSystemTime()).TotalSeconds, 0);
|
||||
calendarRaidLockoutUpdated.NewTimeRemaining = (int)Math.Max((expiryTimes.Item2 - GameTime.GetSystemTime()).TotalSeconds, 0);
|
||||
SendPacket(calendarRaidLockoutUpdated);
|
||||
}
|
||||
|
||||
public void SendCalendarRaidLockoutAdded(InstanceLock instanceLock)
|
||||
@@ -562,35 +563,10 @@ namespace Game
|
||||
calendarRaidLockoutAdded.ServerTime = (uint)GameTime.GetGameTime();
|
||||
calendarRaidLockoutAdded.MapID = (int)instanceLock.GetMapId();
|
||||
calendarRaidLockoutAdded.DifficultyID = instanceLock.GetDifficultyId();
|
||||
calendarRaidLockoutAdded.TimeRemaining = (int)(instanceLock.GetExpiryTime() - GameTime.GetSystemTime()).TotalSeconds;
|
||||
calendarRaidLockoutAdded.TimeRemaining = (int)(instanceLock.GetEffectiveExpiryTime() - GameTime.GetSystemTime()).TotalSeconds;
|
||||
SendPacket(calendarRaidLockoutAdded);
|
||||
}
|
||||
|
||||
public void SendCalendarRaidLockoutUpdated(InstanceSave save)
|
||||
{
|
||||
if (save == null)
|
||||
return;
|
||||
|
||||
long currTime = GameTime.GetGameTime();
|
||||
|
||||
CalendarRaidLockoutUpdated packet = new();
|
||||
packet.DifficultyID = (uint)save.GetDifficultyID();
|
||||
packet.MapID = (int)save.GetMapId();
|
||||
packet.NewTimeRemaining = 0; // FIXME
|
||||
packet.OldTimeRemaining = (int)(save.GetResetTime() - currTime);
|
||||
|
||||
SendPacket(packet);
|
||||
}
|
||||
|
||||
public void SendCalendarRaidLockoutRemoved(InstanceSave save)
|
||||
{
|
||||
CalendarRaidLockoutRemoved calendarRaidLockoutRemoved = new();
|
||||
calendarRaidLockoutRemoved.InstanceID = save.GetInstanceId();
|
||||
calendarRaidLockoutRemoved.MapID = (int)save.GetMapId();
|
||||
calendarRaidLockoutRemoved.DifficultyID = save.GetDifficultyID();
|
||||
SendPacket(calendarRaidLockoutRemoved);
|
||||
}
|
||||
|
||||
void SendCalendarRaidLockoutRemoved(InstanceLock instanceLock)
|
||||
{
|
||||
CalendarRaidLockoutRemoved calendarRaidLockoutRemoved = new();
|
||||
|
||||
@@ -2583,10 +2583,6 @@ namespace Game
|
||||
stmt.AddValue(0, lowGuid);
|
||||
SetQuery(PlayerLoginQueryLoad.Group, stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_INSTANCE);
|
||||
stmt.AddValue(0, lowGuid);
|
||||
SetQuery(PlayerLoginQueryLoad.BoundInstances, stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_AURAS);
|
||||
stmt.AddValue(0, lowGuid);
|
||||
SetQuery(PlayerLoginQueryLoad.Auras, stmt);
|
||||
@@ -2866,7 +2862,6 @@ namespace Game
|
||||
From,
|
||||
Customizations,
|
||||
Group,
|
||||
BoundInstances,
|
||||
Auras,
|
||||
AuraEffects,
|
||||
AuraStoredLocations,
|
||||
|
||||
@@ -354,25 +354,36 @@ namespace Game
|
||||
if (!teleported)
|
||||
{
|
||||
WorldSafeLocsEntry entranceLocation = null;
|
||||
InstanceSave instanceSave = player.GetInstanceSave(at.target_mapId);
|
||||
if (instanceSave != null)
|
||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(at.target_mapId);
|
||||
if (mapEntry.Instanceable())
|
||||
{
|
||||
// Check if we can contact the instancescript of the instance for an updated entrance location
|
||||
Map map = Global.MapMgr.FindMap(at.target_mapId, player.GetInstanceSave(at.target_mapId).GetInstanceId());
|
||||
if (map)
|
||||
uint targetInstanceId = Global.MapMgr.FindInstanceIdForPlayer(at.target_mapId, _player);
|
||||
if (targetInstanceId != 0)
|
||||
{
|
||||
Map map = Global.MapMgr.FindMap(at.target_mapId, targetInstanceId);
|
||||
if (map != null)
|
||||
{
|
||||
InstanceMap instanceMap = map.ToInstanceMap();
|
||||
if (instanceMap != null)
|
||||
if (instanceMap)
|
||||
{
|
||||
InstanceScript instanceScript = instanceMap.GetInstanceScript();
|
||||
if (instanceScript != null)
|
||||
entranceLocation = Global.ObjectMgr.GetWorldSafeLoc(instanceScript.GetEntranceLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally check with the instancesave for an entrance location if we did not get a valid one from the instancescript
|
||||
if (entranceLocation == null)
|
||||
entranceLocation = Global.ObjectMgr.GetWorldSafeLoc(instanceSave.GetEntranceLocation());
|
||||
{
|
||||
Group group = player.GetGroup();
|
||||
Difficulty difficulty = group ? group.GetDifficultyID(mapEntry) : player.GetDifficultyID(mapEntry);
|
||||
ObjectGuid instanceOwnerGuid = group ? group.GetRecentInstanceOwner(at.target_mapId) : player.GetGUID();
|
||||
InstanceLock instanceLock = Global.InstanceLockMgr.FindActiveInstanceLock(instanceOwnerGuid, new MapDb2Entries(mapEntry, Global.DB2Mgr.GetDownscaledMapDifficultyData(at.target_mapId, ref difficulty)));
|
||||
if (instanceLock != null)
|
||||
entranceLocation = Global.ObjectMgr.GetWorldSafeLoc(instanceLock.GetData().EntranceWorldSafeLocId);
|
||||
}
|
||||
}
|
||||
|
||||
if (entranceLocation != null)
|
||||
|
||||
@@ -377,19 +377,27 @@ namespace Game
|
||||
if (mapEntry.IsDungeon())
|
||||
{
|
||||
// check if this instance has a reset time and send it to player if so
|
||||
Difficulty diff = newMap.GetDifficultyID();
|
||||
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetMapDifficultyData(mapEntry.Id, diff);
|
||||
if (mapDiff != null)
|
||||
MapDb2Entries entries = new(mapEntry.Id, newMap.GetDifficultyID());
|
||||
if (entries.MapDifficulty.HasResetSchedule())
|
||||
{
|
||||
if (mapDiff.GetRaidDuration() != 0)
|
||||
RaidInstanceMessage raidInstanceMessage = new();
|
||||
raidInstanceMessage.Type = InstanceResetWarningType.Welcome;
|
||||
raidInstanceMessage.MapID = mapEntry.Id;
|
||||
raidInstanceMessage.DifficultyID = newMap.GetDifficultyID();
|
||||
|
||||
InstanceLock playerLock = Global.InstanceLockMgr.FindActiveInstanceLock(GetPlayer().GetGUID(), entries);
|
||||
if (playerLock != null)
|
||||
{
|
||||
long timeReset = Global.InstanceSaveMgr.GetResetTimeFor(mapEntry.Id, diff);
|
||||
if (timeReset != 0)
|
||||
{
|
||||
uint timeleft = (uint)(timeReset - GameTime.GetGameTime());
|
||||
player.SendInstanceResetWarning(mapEntry.Id, diff, timeleft, true);
|
||||
raidInstanceMessage.Locked = !playerLock.IsExpired();
|
||||
raidInstanceMessage.Extended = playerLock.IsExtended();
|
||||
}
|
||||
else
|
||||
{
|
||||
raidInstanceMessage.Locked = false;
|
||||
raidInstanceMessage.Extended = false;
|
||||
}
|
||||
|
||||
SendPacket(raidInstanceMessage);
|
||||
}
|
||||
|
||||
// check if instance is valid
|
||||
|
||||
@@ -315,16 +315,20 @@ namespace Game.Maps
|
||||
Log.outDebug(LogFilter.Instance, $"Deleting instance {instanceId} as it is no longer referenced by any player");
|
||||
}
|
||||
|
||||
public void UpdateInstanceLockExtensionForPlayer(ObjectGuid playerGuid, MapDb2Entries entries, bool extended)
|
||||
public Tuple<DateTime, DateTime> UpdateInstanceLockExtensionForPlayer(ObjectGuid playerGuid, MapDb2Entries entries, bool extended)
|
||||
{
|
||||
InstanceLock instanceLock = FindActiveInstanceLock(playerGuid, entries, true, false);
|
||||
if (instanceLock != null)
|
||||
{
|
||||
DateTime oldExpiryTime = instanceLock.GetEffectiveExpiryTime();
|
||||
instanceLock.SetExtended(extended);
|
||||
DB.Characters.Execute($"UPDATE character_instance_lock SET extended = {(extended ? 1 : 0)} WHERE guid = {playerGuid.GetCounter()} AND mapId = {entries.MapDifficulty.MapID} AND lockId = {entries.MapDifficulty.LockID}");
|
||||
Log.outDebug(LogFilter.Instance, $"[{entries.Map.Id}-{entries.Map.MapName[Global.WorldMgr.GetDefaultDbcLocale()]} | " +
|
||||
$"{entries.MapDifficulty.DifficultyID}-{CliDB.DifficultyStorage.LookupByKey(entries.MapDifficulty.DifficultyID).Name}] Instance lock for {playerGuid} is {(extended ? "now" : "no longer")} extended");
|
||||
return Tuple.Create(oldExpiryTime, instanceLock.GetEffectiveExpiryTime());
|
||||
}
|
||||
|
||||
return Tuple.Create(DateTime.MinValue, DateTime.MinValue);
|
||||
}
|
||||
|
||||
public DateTime GetNextResetTime(MapDb2Entries entries)
|
||||
|
||||
@@ -1,778 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Framework.Database;
|
||||
using Game.DataStorage;
|
||||
using Game.Entities;
|
||||
using Game.Groups;
|
||||
using Game.Scenarios;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Game.Maps
|
||||
{
|
||||
public class InstanceSaveManager : Singleton<InstanceSaveManager>
|
||||
{
|
||||
InstanceSaveManager() { }
|
||||
|
||||
public InstanceSave AddInstanceSave(uint mapId, uint instanceId, Difficulty difficulty, long resetTime, uint entranceId, bool canReset, bool load = false)
|
||||
{
|
||||
InstanceSave old_save = GetInstanceSave(instanceId);
|
||||
if (old_save != null)
|
||||
return old_save;
|
||||
|
||||
MapRecord entry = CliDB.MapStorage.LookupByKey(mapId);
|
||||
if (entry == null)
|
||||
{
|
||||
Log.outError(LogFilter.Server, "InstanceSaveManager.AddInstanceSave: wrong mapid = {0}, instanceid = {1}!", mapId, instanceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (instanceId == 0)
|
||||
{
|
||||
Log.outError(LogFilter.Server, "InstanceSaveManager.AddInstanceSave: mapid = {0}, wrong instanceid = {1}!", mapId, instanceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
|
||||
if (difficultyEntry == null || difficultyEntry.InstanceType != entry.InstanceType)
|
||||
{
|
||||
Log.outError(LogFilter.Server, "InstanceSaveManager.AddInstanceSave: mapid = {0}, instanceid = {1}, wrong dificalty {2}!", mapId, instanceId, difficulty);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (entranceId != 0 && Global.ObjectMgr.GetWorldSafeLoc(entranceId) == null)
|
||||
{
|
||||
Log.outWarn(LogFilter.Misc, "InstanceSaveManager.AddInstanceSave: invalid entranceId = {0} defined for instance save with mapid = {1}, instanceid = {2}!", entranceId, mapId, instanceId);
|
||||
entranceId = 0;
|
||||
}
|
||||
|
||||
if (resetTime == 0)
|
||||
{
|
||||
// initialize reset time
|
||||
// for normal instances if no creatures are killed the instance will reset in two hours
|
||||
if (entry.InstanceType == MapTypes.Raid || difficulty > Difficulty.Normal)
|
||||
resetTime = GetResetTimeFor(mapId, difficulty);
|
||||
else
|
||||
{
|
||||
resetTime = GameTime.GetGameTime() + 2 * Time.Hour;
|
||||
// normally this will be removed soon after in InstanceMap.Add, prevent error
|
||||
ScheduleReset(true, resetTime, new InstResetEvent(0, mapId, difficulty, instanceId));
|
||||
}
|
||||
}
|
||||
|
||||
Log.outDebug(LogFilter.Maps, "InstanceSaveManager.AddInstanceSave: mapid = {0}, instanceid = {1}", mapId, instanceId);
|
||||
|
||||
InstanceSave save = new(mapId, instanceId, difficulty, entranceId, resetTime, canReset);
|
||||
if (!load)
|
||||
save.SaveToDB();
|
||||
|
||||
m_instanceSaveById[instanceId] = save;
|
||||
return save;
|
||||
}
|
||||
|
||||
public InstanceSave GetInstanceSave(uint InstanceId)
|
||||
{
|
||||
return m_instanceSaveById.LookupByKey(InstanceId);
|
||||
}
|
||||
|
||||
public void DeleteInstanceFromDB(uint instanceid)
|
||||
{
|
||||
SQLTransaction trans = new();
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_INSTANCE_BY_INSTANCE);
|
||||
stmt.AddValue(0, instanceid);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_INSTANCE_BY_INSTANCE);
|
||||
stmt.AddValue(0, instanceid);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GROUP_INSTANCE_BY_INSTANCE);
|
||||
stmt.AddValue(0, instanceid);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_SCENARIO_INSTANCE_CRITERIA_FOR_INSTANCE);
|
||||
stmt.AddValue(0, instanceid);
|
||||
trans.Append(stmt);
|
||||
|
||||
DB.Characters.CommitTransaction(trans);
|
||||
// Respawn times should be deleted only when the map gets unloaded
|
||||
}
|
||||
|
||||
public void RemoveInstanceSave(uint InstanceId)
|
||||
{
|
||||
var instanceSave = m_instanceSaveById.LookupByKey(InstanceId);
|
||||
if (instanceSave != null)
|
||||
{
|
||||
// save the resettime for normal instances only when they get unloaded
|
||||
long resettime = instanceSave.GetResetTimeForDB();
|
||||
if (resettime != 0)
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_INSTANCE_RESETTIME);
|
||||
stmt.AddValue(0, resettime);
|
||||
stmt.AddValue(1, InstanceId);
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
instanceSave.SetToDelete(true);
|
||||
m_instanceSaveById.Remove(InstanceId);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnloadInstanceSave(uint InstanceId)
|
||||
{
|
||||
InstanceSave save = GetInstanceSave(InstanceId);
|
||||
if (save != null)
|
||||
save.UnloadIfEmpty();
|
||||
}
|
||||
|
||||
public void LoadInstances()
|
||||
{
|
||||
uint oldMSTime = Time.GetMSTime();
|
||||
|
||||
// Delete expired instances (Instance related spawns are removed in the following cleanup queries)
|
||||
DB.Characters.DirectExecute("DELETE i FROM instance i LEFT JOIN instance_reset ir ON mapid = map AND i.difficulty = ir.difficulty " +
|
||||
"WHERE (i.resettime > 0 AND i.resettime < UNIX_TIMESTAMP()) OR (ir.resettime IS NOT NULL AND ir.resettime < UNIX_TIMESTAMP())");
|
||||
|
||||
// Delete invalid character_instance and group_instance references
|
||||
DB.Characters.DirectExecute("DELETE ci.* FROM character_instance AS ci LEFT JOIN characters AS c ON ci.guid = c.guid WHERE c.guid IS NULL");
|
||||
DB.Characters.DirectExecute("DELETE gi.* FROM group_instance AS gi LEFT JOIN `groups` AS g ON gi.guid = g.guid WHERE g.guid IS NULL");
|
||||
|
||||
// Delete invalid instance references
|
||||
DB.Characters.DirectExecute("DELETE i.* FROM instance AS i LEFT JOIN character_instance AS ci ON i.id = ci.instance LEFT JOIN group_instance AS gi ON i.id = gi.instance WHERE ci.guid IS NULL AND gi.guid IS NULL");
|
||||
|
||||
// Delete invalid references to instance
|
||||
DB.Characters.DirectExecute("DELETE FROM respawn WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)");
|
||||
DB.Characters.DirectExecute("DELETE tmp.* FROM character_instance AS tmp LEFT JOIN instance ON tmp.instance = instance.id WHERE tmp.instance > 0 AND instance.id IS NULL");
|
||||
DB.Characters.DirectExecute("DELETE tmp.* FROM group_instance AS tmp LEFT JOIN instance ON tmp.instance = instance.id WHERE tmp.instance > 0 AND instance.id IS NULL");
|
||||
|
||||
// Clean invalid references to instance
|
||||
DB.Characters.DirectExecute("UPDATE corpse SET instanceId = 0 WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)");
|
||||
DB.Characters.DirectExecute("UPDATE characters AS tmp LEFT JOIN instance ON tmp.instance_id = instance.id SET tmp.instance_id = 0 WHERE tmp.instance_id > 0 AND instance.id IS NULL");
|
||||
|
||||
// Initialize instance id storage (Needs to be done after the trash has been clean out)
|
||||
Global.MapMgr.InitInstanceIds();
|
||||
|
||||
// Load reset times and clean expired instances
|
||||
LoadResetTimes();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loaded instances in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
void LoadResetTimes()
|
||||
{
|
||||
long now = GameTime.GetGameTime();
|
||||
long today = (now / Time.Day) * Time.Day;
|
||||
|
||||
// NOTE: Use DirectPExecute for tables that will be queried later
|
||||
|
||||
// get the current reset times for normal instances (these may need to be updated)
|
||||
// these are only kept in memory for InstanceSaves that are loaded later
|
||||
// resettime = 0 in the DB for raid/heroic instances so those are skipped
|
||||
Dictionary<uint, Tuple<uint, long>> instResetTime = new();
|
||||
|
||||
// index instance ids by map/difficulty pairs for fast reset warning send
|
||||
MultiMap<uint, uint> mapDiffResetInstances = new();
|
||||
|
||||
SQLResult result = DB.Characters.Query("SELECT id, map, difficulty, resettime FROM instance ORDER BY id ASC");
|
||||
if (!result.IsEmpty())
|
||||
{
|
||||
do
|
||||
{
|
||||
uint instanceId = result.Read<uint>(0);
|
||||
|
||||
// Mark instance id as being used
|
||||
Global.MapMgr.RegisterInstanceId(instanceId);
|
||||
long resettime = result.Read<long>(3);
|
||||
if (resettime != 0)
|
||||
{
|
||||
uint mapid = result.Read<ushort>(1);
|
||||
uint difficulty = result.Read<byte>(2);
|
||||
|
||||
instResetTime[instanceId] = Tuple.Create(MathFunctions.MakePair32(mapid, difficulty), resettime);
|
||||
mapDiffResetInstances.Add(MathFunctions.MakePair32(mapid, difficulty), instanceId);
|
||||
}
|
||||
}
|
||||
while (result.NextRow());
|
||||
|
||||
// schedule the reset times
|
||||
foreach (var pair in instResetTime)
|
||||
if (pair.Value.Item2 > now)
|
||||
ScheduleReset(true, pair.Value.Item2, new InstResetEvent(0, MathFunctions.Pair32_LoPart(pair.Value.Item1), (Difficulty)MathFunctions.Pair32_HiPart(pair.Value.Item1), pair.Key));
|
||||
}
|
||||
|
||||
// load the global respawn times for raid/heroic instances
|
||||
uint resetHour = WorldConfig.GetUIntValue(WorldCfg.InstanceResetTimeHour);
|
||||
result = DB.Characters.Query("SELECT mapid, difficulty, resettime FROM instance_reset");
|
||||
if (!result.IsEmpty())
|
||||
{
|
||||
do
|
||||
{
|
||||
uint mapid = result.Read<ushort>(0);
|
||||
Difficulty difficulty = (Difficulty)result.Read<byte>(1);
|
||||
long oldresettime = result.Read<long>(2);
|
||||
|
||||
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetMapDifficultyData(mapid, difficulty);
|
||||
if (mapDiff == null)
|
||||
{
|
||||
Log.outError(LogFilter.Server, "InstanceSaveManager.LoadResetTimes: invalid mapid({0})/difficulty({1}) pair in instance_reset!", mapid, difficulty);
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GLOBAL_INSTANCE_RESETTIME);
|
||||
stmt.AddValue(0, mapid);
|
||||
stmt.AddValue(1, (byte)difficulty);
|
||||
DB.Characters.DirectExecute(stmt);
|
||||
continue;
|
||||
}
|
||||
|
||||
// update the reset time if the hour in the configs changes
|
||||
long newresettime = Time.GetLocalHourTimestamp(oldresettime, resetHour, false);
|
||||
if (oldresettime != newresettime)
|
||||
{
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_GLOBAL_INSTANCE_RESETTIME);
|
||||
stmt.AddValue(0, newresettime);
|
||||
stmt.AddValue(1, mapid);
|
||||
stmt.AddValue(2, (byte)difficulty);
|
||||
DB.Characters.DirectExecute(stmt);
|
||||
}
|
||||
|
||||
InitializeResetTimeFor(mapid, difficulty, newresettime);
|
||||
} while (result.NextRow());
|
||||
}
|
||||
|
||||
// calculate new global reset times for expired instances and those that have never been reset yet
|
||||
// add the global reset times to the priority queue
|
||||
foreach (var mapDifficultyPair in Global.DB2Mgr.GetMapDifficulties())
|
||||
{
|
||||
uint mapid = mapDifficultyPair.Key;
|
||||
|
||||
foreach (var difficultyPair in mapDifficultyPair.Value)
|
||||
{
|
||||
Difficulty difficulty = (Difficulty)difficultyPair.Key;
|
||||
MapDifficultyRecord mapDiff = difficultyPair.Value;
|
||||
if (mapDiff.GetRaidDuration() == 0)
|
||||
continue;
|
||||
|
||||
// the reset_delay must be at least one day
|
||||
uint period = (uint)(((mapDiff.GetRaidDuration() * WorldConfig.GetFloatValue(WorldCfg.RateInstanceResetTime)) / Time.Day) * Time.Day);
|
||||
if (period < Time.Day)
|
||||
period = Time.Day;
|
||||
|
||||
long t = GetResetTimeFor(mapid, difficulty);
|
||||
if (t == 0)
|
||||
{
|
||||
// initialize the reset time
|
||||
t = Time.GetLocalHourTimestamp(today + period, resetHour);
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_GLOBAL_INSTANCE_RESETTIME);
|
||||
stmt.AddValue(0, mapid);
|
||||
stmt.AddValue(1, (byte)difficulty);
|
||||
stmt.AddValue(2, t);
|
||||
DB.Characters.DirectExecute(stmt);
|
||||
}
|
||||
|
||||
if (t < now)
|
||||
{
|
||||
// assume that expired instances have already been cleaned
|
||||
// calculate the next reset time
|
||||
long day = (t / Time.Day) * Time.Day;
|
||||
t = Time.GetLocalHourTimestamp(day + ((today - day) / period + 1) * period, resetHour);
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_GLOBAL_INSTANCE_RESETTIME);
|
||||
stmt.AddValue(0, t);
|
||||
stmt.AddValue(1, mapid);
|
||||
stmt.AddValue(2, (byte)difficulty);
|
||||
DB.Characters.DirectExecute(stmt);
|
||||
}
|
||||
|
||||
InitializeResetTimeFor(mapid, difficulty, t);
|
||||
|
||||
// schedule the global reset/warning
|
||||
byte type;
|
||||
for (type = 1; type < 4; ++type)
|
||||
if (t - ResetTimeDelay[type - 1] > now)
|
||||
break;
|
||||
|
||||
ScheduleReset(true, t - ResetTimeDelay[type - 1], new InstResetEvent(type, mapid, difficulty, 0));
|
||||
|
||||
var range = mapDiffResetInstances.LookupByKey(MathFunctions.MakePair32(mapid, (uint)difficulty));
|
||||
foreach (var id in range)
|
||||
ScheduleReset(true, t - ResetTimeDelay[type - 1], new InstResetEvent(type, mapid, difficulty, id));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long GetSubsequentResetTime(uint mapid, Difficulty difficulty, long resetTime)
|
||||
{
|
||||
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetMapDifficultyData(mapid, difficulty);
|
||||
if (mapDiff == null || mapDiff.GetRaidDuration() == 0)
|
||||
{
|
||||
Log.outError(LogFilter.Misc, "InstanceSaveManager.GetSubsequentResetTime: not valid difficulty or no reset delay for map {0}", mapid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long resetHour = WorldConfig.GetIntValue(WorldCfg.InstanceResetTimeHour);
|
||||
long period = (uint)(((mapDiff.GetRaidDuration() * WorldConfig.GetFloatValue(WorldCfg.RateInstanceResetTime)) / Time.Day) * Time.Day);
|
||||
if (period < Time.Day)
|
||||
period = Time.Day;
|
||||
|
||||
return Time.GetLocalHourTimestamp(((resetTime + Time.Minute) / Time.Day * Time.Day) + period, (uint)resetHour);
|
||||
}
|
||||
|
||||
public void ScheduleReset(bool add, long time, InstResetEvent Event)
|
||||
{
|
||||
if (!add)
|
||||
{
|
||||
// find the event in the queue and remove it
|
||||
var range = m_resetTimeQueue.LookupByKey(time);
|
||||
foreach (var instResetEvent in range)
|
||||
{
|
||||
if (instResetEvent == Event)
|
||||
{
|
||||
m_resetTimeQueue.Remove(time, instResetEvent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// in case the reset time changed (should happen very rarely), we search the whole queue
|
||||
foreach (var pair in m_resetTimeQueue)
|
||||
{
|
||||
if (pair.Value == Event)
|
||||
{
|
||||
m_resetTimeQueue.Remove(pair);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.outError(LogFilter.Server, "InstanceSaveManager.ScheduleReset: cannot cancel the reset, the event({0}, {1}, {2}) was not found!", Event.type, Event.mapid, Event.instanceId);
|
||||
}
|
||||
else
|
||||
m_resetTimeQueue.Add(time, Event);
|
||||
}
|
||||
|
||||
public void ForceGlobalReset(uint mapId, Difficulty difficulty)
|
||||
{
|
||||
if (Global.DB2Mgr.GetDownscaledMapDifficultyData(mapId, ref difficulty) == null)
|
||||
return;
|
||||
// remove currently scheduled reset times
|
||||
ScheduleReset(false, 0, new InstResetEvent(1, mapId, difficulty, 0));
|
||||
ScheduleReset(false, 0, new InstResetEvent(4, mapId, difficulty, 0));
|
||||
// force global reset on the instance
|
||||
_ResetOrWarnAll(mapId, difficulty, false, GameTime.GetGameTime());
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
long now = GameTime.GetGameTime();
|
||||
|
||||
while (!m_resetTimeQueue.Empty())
|
||||
{
|
||||
var pair = m_resetTimeQueue.First();
|
||||
long time = pair.Key;
|
||||
if (time >= now)
|
||||
break;
|
||||
|
||||
InstResetEvent Event = pair.Value;
|
||||
if (Event.type == 0)
|
||||
{
|
||||
// for individual normal instances, max creature respawn + X hours
|
||||
_ResetInstance(Event.mapid, Event.instanceId);
|
||||
m_resetTimeQueue.Remove(pair);
|
||||
}
|
||||
else
|
||||
{
|
||||
// global reset/warning for a certain map
|
||||
long resetTime = GetResetTimeFor(Event.mapid, Event.difficulty);
|
||||
_ResetOrWarnAll(Event.mapid, Event.difficulty, Event.type != 4, resetTime);
|
||||
if (Event.type != 4)
|
||||
{
|
||||
// schedule the next warning/reset
|
||||
++Event.type;
|
||||
ScheduleReset(true, resetTime - ResetTimeDelay[Event.type - 1], Event);
|
||||
}
|
||||
m_resetTimeQueue.Remove(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _ResetSave(KeyValuePair<uint, InstanceSave> pair)
|
||||
{
|
||||
// unbind all players bound to the instance
|
||||
// do not allow UnbindInstance to automatically unload the InstanceSaves
|
||||
lock_instLists = true;
|
||||
|
||||
bool shouldDelete = true;
|
||||
var pList = pair.Value.m_playerList;
|
||||
List<Player> temp = new(); // list of expired binds that should be unbound
|
||||
foreach (var player in pList)
|
||||
{
|
||||
InstanceBind bind = player.GetBoundInstance(pair.Value.GetMapId(), pair.Value.GetDifficultyID());
|
||||
if (bind != null)
|
||||
{
|
||||
Cypher.Assert(bind.save == pair.Value);
|
||||
if (bind.perm && bind.extendState != 0) // permanent and not already expired
|
||||
{
|
||||
// actual promotion in DB already happened in caller
|
||||
bind.extendState = bind.extendState == BindExtensionState.Extended ? BindExtensionState.Normal : BindExtensionState.Expired;
|
||||
shouldDelete = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
temp.Add(player);
|
||||
}
|
||||
|
||||
var gList = pair.Value.m_groupList;
|
||||
while (!gList.Empty())
|
||||
{
|
||||
Group group = gList.First();
|
||||
group.UnbindInstance(pair.Value.GetMapId(), pair.Value.GetDifficultyID(), true);
|
||||
}
|
||||
|
||||
if (shouldDelete)
|
||||
m_instanceSaveById.Remove(pair.Key);
|
||||
|
||||
lock_instLists = false;
|
||||
}
|
||||
|
||||
void _ResetInstance(uint mapid, uint instanceId)
|
||||
{
|
||||
Log.outDebug(LogFilter.Maps, "InstanceSaveMgr._ResetInstance {0}, {1}", mapid, instanceId);
|
||||
var map = CliDB.MapStorage.LookupByKey(mapid);
|
||||
if (!map.IsDungeon())
|
||||
return;
|
||||
|
||||
var pair = m_instanceSaveById.Find(instanceId);
|
||||
if (pair.Value != null)
|
||||
_ResetSave(pair);
|
||||
|
||||
DeleteInstanceFromDB(instanceId); // even if save not loaded
|
||||
|
||||
Map iMap = Global.MapMgr.FindMap(mapid, instanceId);
|
||||
if (iMap != null)
|
||||
{
|
||||
((InstanceMap)iMap).Reset(InstanceResetMethod.RespawnDelay);
|
||||
iMap.DeleteRespawnTimes();
|
||||
iMap.DeleteCorpseData();
|
||||
}
|
||||
else
|
||||
Map.DeleteRespawnTimesInDB(mapid, instanceId);
|
||||
|
||||
// Free up the instance id and allow it to be reused
|
||||
Global.MapMgr.FreeInstanceId(instanceId);
|
||||
}
|
||||
|
||||
void _ResetOrWarnAll(uint mapid, Difficulty difficulty, bool warn, long resetTime)
|
||||
{
|
||||
// global reset for all instances of the given map
|
||||
MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapid);
|
||||
if (!mapEntry.Instanceable())
|
||||
return;
|
||||
|
||||
Log.outDebug(LogFilter.Misc, "InstanceSaveManager.ResetOrWarnAll: Processing map {0} ({1}) on difficulty {2} (warn? {3})", mapEntry.MapName[Global.WorldMgr.GetDefaultDbcLocale()], mapid, difficulty, warn);
|
||||
long now = GameTime.GetGameTime();
|
||||
|
||||
if (!warn)
|
||||
{
|
||||
// calculate the next reset time
|
||||
long next_reset = GetSubsequentResetTime(mapid, difficulty, resetTime);
|
||||
if (next_reset == 0)
|
||||
return;
|
||||
|
||||
// delete them from the DB, even if not loaded
|
||||
SQLTransaction trans = new();
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF);
|
||||
stmt.AddValue(0, mapid);
|
||||
stmt.AddValue(1, (byte)difficulty);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_GROUP_INSTANCE_BY_MAP_DIFF);
|
||||
stmt.AddValue(0, mapid);
|
||||
stmt.AddValue(1, (byte)difficulty);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_EXPIRED_INSTANCE_BY_MAP_DIFF);
|
||||
stmt.AddValue(0, mapid);
|
||||
stmt.AddValue(1, (byte)difficulty);
|
||||
trans.Append(stmt);
|
||||
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF);
|
||||
stmt.AddValue(0, mapid);
|
||||
stmt.AddValue(1, (byte)difficulty);
|
||||
trans.Append(stmt);
|
||||
|
||||
DB.Characters.CommitTransaction(trans);
|
||||
|
||||
// promote loaded binds to instances of the given map
|
||||
foreach (var pair in m_instanceSaveById.ToList())
|
||||
{
|
||||
if (pair.Value.GetMapId() == mapid && pair.Value.GetDifficultyID() == difficulty)
|
||||
_ResetSave(pair);
|
||||
}
|
||||
|
||||
SetResetTimeFor(mapid, difficulty, next_reset);
|
||||
ScheduleReset(true, next_reset - 3600, new InstResetEvent(1, mapid, difficulty, 0));
|
||||
|
||||
// Update it in the DB
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_GLOBAL_INSTANCE_RESETTIME);
|
||||
stmt.AddValue(0, (uint)next_reset);
|
||||
stmt.AddValue(1, (ushort)mapid);
|
||||
stmt.AddValue(2, (byte)difficulty);
|
||||
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
// note: this isn't fast but it's meant to be executed very rarely
|
||||
if (mapEntry.IsDungeon())
|
||||
{
|
||||
Global.MapMgr.DoForAllMapsWithMapId(mapid, map =>
|
||||
{
|
||||
if (warn)
|
||||
{
|
||||
uint timeLeft;
|
||||
if (now >= resetTime)
|
||||
timeLeft = 0;
|
||||
else
|
||||
timeLeft = (uint)(resetTime - now);
|
||||
|
||||
((InstanceMap)map).SendResetWarnings(timeLeft);
|
||||
}
|
||||
else
|
||||
((InstanceMap)map).Reset(InstanceResetMethod.Global);
|
||||
});
|
||||
}
|
||||
|
||||
// @todo delete creature/gameobject respawn times even if the maps are not loaded
|
||||
}
|
||||
|
||||
public uint GetNumBoundPlayersTotal()
|
||||
{
|
||||
uint ret = 0;
|
||||
foreach (var pair in m_instanceSaveById)
|
||||
ret += pair.Value.GetPlayerCount();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public uint GetNumBoundGroupsTotal()
|
||||
{
|
||||
uint ret = 0;
|
||||
foreach (var pair in m_instanceSaveById)
|
||||
ret += pair.Value.GetGroupCount();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public long GetResetTimeFor(uint mapid, Difficulty d)
|
||||
{
|
||||
return m_resetTimeByMapDifficulty.LookupByKey(MathFunctions.MakePair64(mapid, (uint)d));
|
||||
}
|
||||
|
||||
// Use this on startup when initializing reset times
|
||||
void InitializeResetTimeFor(uint mapid, Difficulty d, long t)
|
||||
{
|
||||
m_resetTimeByMapDifficulty[MathFunctions.MakePair64(mapid, (uint)d)] = t;
|
||||
}
|
||||
// Use this only when updating existing reset times
|
||||
void SetResetTimeFor(uint mapid, Difficulty d, long t)
|
||||
{
|
||||
var key = MathFunctions.MakePair64(mapid, (uint)d);
|
||||
Cypher.Assert(m_resetTimeByMapDifficulty.ContainsKey(key));
|
||||
m_resetTimeByMapDifficulty[key] = t;
|
||||
}
|
||||
|
||||
public Dictionary<ulong, long> GetResetTimeMap()
|
||||
{
|
||||
return m_resetTimeByMapDifficulty;
|
||||
}
|
||||
|
||||
public int GetNumInstanceSaves() { return m_instanceSaveById.Count; }
|
||||
|
||||
public class InstResetEvent
|
||||
{
|
||||
public InstResetEvent(byte t = 0, uint _mapid = 0, Difficulty d = Difficulty.Normal, uint _instanceid = 0)
|
||||
{
|
||||
type = t;
|
||||
difficulty = d;
|
||||
mapid = _mapid;
|
||||
instanceId = _instanceid;
|
||||
}
|
||||
|
||||
public byte type;
|
||||
public Difficulty difficulty;
|
||||
public uint mapid;
|
||||
public uint instanceId;
|
||||
}
|
||||
|
||||
static ushort[] ResetTimeDelay = { 3600, 900, 300, 60 };
|
||||
|
||||
// used during global instance resets
|
||||
public bool lock_instLists;
|
||||
// fast lookup by instance id
|
||||
Dictionary<uint, InstanceSave> m_instanceSaveById = new();
|
||||
// fast lookup for reset times (always use existed functions for access/set)
|
||||
Dictionary<ulong, long> m_resetTimeByMapDifficulty = new();
|
||||
MultiMap<long, InstResetEvent> m_resetTimeQueue = new();
|
||||
}
|
||||
|
||||
public class InstanceSave
|
||||
{
|
||||
public InstanceSave(uint MapId, uint InstanceId, Difficulty difficulty, uint entranceId, long resetTime, bool canReset)
|
||||
{
|
||||
m_resetTime = resetTime;
|
||||
m_instanceid = InstanceId;
|
||||
m_mapid = MapId;
|
||||
m_difficulty = difficulty;
|
||||
m_entranceId = entranceId;
|
||||
m_canReset = canReset;
|
||||
m_toDelete = false;
|
||||
}
|
||||
|
||||
public void SaveToDB()
|
||||
{
|
||||
// save instance data too
|
||||
string data = "";
|
||||
uint completedEncounters = 0;
|
||||
|
||||
Map map = Global.MapMgr.FindMap(GetMapId(), m_instanceid);
|
||||
if (map != null)
|
||||
{
|
||||
Cypher.Assert(map.IsDungeon());
|
||||
InstanceScript instanceScript = ((InstanceMap)map).GetInstanceScript();
|
||||
if (instanceScript != null)
|
||||
{
|
||||
data = instanceScript.GetSaveData();
|
||||
completedEncounters = instanceScript.GetCompletedEncounterMask();
|
||||
m_entranceId = instanceScript.GetEntranceLocation();
|
||||
}
|
||||
|
||||
InstanceScenario scenario = map.ToInstanceMap().GetInstanceScenario();
|
||||
if (scenario != null)
|
||||
scenario.SaveToDB();
|
||||
}
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_INSTANCE_SAVE);
|
||||
stmt.AddValue(0, m_instanceid);
|
||||
stmt.AddValue(1, GetMapId());
|
||||
stmt.AddValue(2, GetResetTimeForDB());
|
||||
stmt.AddValue(3, (uint)GetDifficultyID());
|
||||
stmt.AddValue(4, completedEncounters);
|
||||
stmt.AddValue(5, data);
|
||||
stmt.AddValue(6, m_entranceId);
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
public long GetResetTimeForDB()
|
||||
{
|
||||
// only save the reset time for normal instances
|
||||
MapRecord entry = CliDB.MapStorage.LookupByKey(GetMapId());
|
||||
if (entry == null || entry.InstanceType == MapTypes.Raid || GetDifficultyID() == Difficulty.Heroic)
|
||||
return 0;
|
||||
else
|
||||
return GetResetTime();
|
||||
}
|
||||
|
||||
MapRecord GetMapEntry()
|
||||
{
|
||||
return CliDB.MapStorage.LookupByKey(m_mapid);
|
||||
}
|
||||
|
||||
public void DeleteFromDB()
|
||||
{
|
||||
Global.InstanceSaveMgr.DeleteInstanceFromDB(GetInstanceId());
|
||||
}
|
||||
|
||||
public bool UnloadIfEmpty()
|
||||
{
|
||||
if (m_playerList.Empty() && m_groupList.Empty())
|
||||
{
|
||||
if (!Global.InstanceSaveMgr.lock_instLists)
|
||||
Global.InstanceSaveMgr.RemoveInstanceSave(GetInstanceId());
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
public uint GetPlayerCount() { return (uint)m_playerList.Count; }
|
||||
public uint GetGroupCount() { return (uint)m_groupList.Count; }
|
||||
|
||||
public uint GetInstanceId() { return m_instanceid; }
|
||||
public uint GetMapId() { return m_mapid; }
|
||||
|
||||
public long GetResetTime() { return m_resetTime; }
|
||||
public void SetResetTime(long resetTime) { m_resetTime = resetTime; }
|
||||
|
||||
public uint GetEntranceLocation() { return m_entranceId; }
|
||||
void SetEntranceLocation(uint entranceId) { m_entranceId = entranceId; }
|
||||
|
||||
public void AddPlayer(Player player)
|
||||
{
|
||||
m_playerList.Add(player);
|
||||
}
|
||||
public bool RemovePlayer(Player player)
|
||||
{
|
||||
m_playerList.Remove(player);
|
||||
|
||||
return UnloadIfEmpty();
|
||||
}
|
||||
|
||||
public void AddGroup(Group group) { m_groupList.Add(group); }
|
||||
public bool RemoveGroup(Group group)
|
||||
{
|
||||
m_groupList.Remove(group);
|
||||
|
||||
return UnloadIfEmpty();
|
||||
}
|
||||
|
||||
public bool CanReset() { return m_canReset; }
|
||||
public void SetCanReset(bool canReset) { m_canReset = canReset; }
|
||||
|
||||
public Difficulty GetDifficultyID() { return m_difficulty; }
|
||||
|
||||
public void SetToDelete(bool toDelete)
|
||||
{
|
||||
m_toDelete = toDelete;
|
||||
}
|
||||
|
||||
public List<Player> m_playerList = new();
|
||||
public List<Group> m_groupList = new();
|
||||
long m_resetTime;
|
||||
uint m_instanceid;
|
||||
uint m_mapid;
|
||||
Difficulty m_difficulty;
|
||||
uint m_entranceId;
|
||||
bool m_canReset;
|
||||
bool m_toDelete;
|
||||
}
|
||||
|
||||
public class InstanceTemplate
|
||||
{
|
||||
public uint Parent;
|
||||
public uint ScriptId;
|
||||
}
|
||||
|
||||
public class InstanceBind
|
||||
{
|
||||
public InstanceSave save;
|
||||
public bool perm;
|
||||
public BindExtensionState extendState;
|
||||
}
|
||||
}
|
||||
@@ -430,7 +430,7 @@ namespace Game.Maps
|
||||
|
||||
bossInfo.state = state;
|
||||
SaveToDB();
|
||||
if (state == EncounterState.Done)
|
||||
if (state == EncounterState.Done && dungeonEncounter != null)
|
||||
instance.UpdateInstanceLock(dungeonEncounter, new(id, state));
|
||||
}
|
||||
|
||||
@@ -754,10 +754,19 @@ namespace Game.Maps
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsEncounterCompleted(uint dungeonEncounterId)
|
||||
{
|
||||
for (uint i = 0; i < bosses.Count; ++i)
|
||||
for (var j = 0; j < bosses[i].DungeonEncounters.Length; ++j)
|
||||
if (bosses[i].DungeonEncounters[j] != null && bosses[i].DungeonEncounters[j].Id == dungeonEncounterId)
|
||||
return bosses[i].state == EncounterState.Done;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetEntranceLocation(uint worldSafeLocationId)
|
||||
{
|
||||
_entranceId = worldSafeLocationId;
|
||||
if (_temporaryEntranceId != 0)
|
||||
_temporaryEntranceId = 0;
|
||||
}
|
||||
|
||||
|
||||
+21
-51
@@ -1713,8 +1713,6 @@ namespace Game.Maps
|
||||
{
|
||||
//Get instance where player's group is bound & its map
|
||||
uint instanceIdToCheck = Global.MapMgr.FindInstanceIdForPlayer(mapid, player);
|
||||
if (instanceIdToCheck != 0)
|
||||
{
|
||||
Map boundMap = Global.MapMgr.FindMap(mapid, instanceIdToCheck);
|
||||
if (boundMap != null)
|
||||
{
|
||||
@@ -1727,7 +1725,6 @@ namespace Game.Maps
|
||||
if (entry.IsDungeon() && !player.CheckInstanceCount(instanceIdToCheck) && !player.IsDead())
|
||||
return EnterState.CannotEnterTooManyInstances;
|
||||
}
|
||||
}
|
||||
|
||||
return EnterState.CanEnter;
|
||||
}
|
||||
@@ -2090,6 +2087,9 @@ namespace Game.Maps
|
||||
|
||||
void DeleteRespawnInfoFromDB(SpawnObjectType type, ulong spawnId, SQLTransaction dbTrans = null)
|
||||
{
|
||||
if (Instanceable())
|
||||
return;
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESPAWN);
|
||||
stmt.AddValue(0, (ushort)type);
|
||||
stmt.AddValue(1, spawnId);
|
||||
@@ -2727,6 +2727,9 @@ namespace Game.Maps
|
||||
|
||||
public void SaveRespawnInfoDB(RespawnInfo info, SQLTransaction dbTrans = null)
|
||||
{
|
||||
if (Instanceable())
|
||||
return;
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_RESPAWN);
|
||||
stmt.AddValue(0, (ushort)info.type);
|
||||
stmt.AddValue(1, info.spawnId);
|
||||
@@ -2738,6 +2741,9 @@ namespace Game.Maps
|
||||
|
||||
public void LoadRespawnTimes()
|
||||
{
|
||||
if (Instanceable())
|
||||
return;
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_RESPAWNS);
|
||||
stmt.AddValue(0, GetId());
|
||||
stmt.AddValue(1, GetInstanceId());
|
||||
@@ -2768,14 +2774,17 @@ namespace Game.Maps
|
||||
public void DeleteRespawnTimes()
|
||||
{
|
||||
UnloadAllRespawnInfos();
|
||||
DeleteRespawnTimesInDB(GetId(), GetInstanceId());
|
||||
DeleteRespawnTimesInDB();
|
||||
}
|
||||
|
||||
public static void DeleteRespawnTimesInDB(uint mapId, uint instanceId)
|
||||
public void DeleteRespawnTimesInDB()
|
||||
{
|
||||
if (Instanceable())
|
||||
return;
|
||||
|
||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_ALL_RESPAWNS);
|
||||
stmt.AddValue(0, mapId);
|
||||
stmt.AddValue(1, instanceId);
|
||||
stmt.AddValue(0, GetId());
|
||||
stmt.AddValue(1, GetInstanceId());
|
||||
DB.Characters.Execute(stmt);
|
||||
}
|
||||
|
||||
@@ -3284,11 +3293,6 @@ namespace Game.Maps
|
||||
return i_mapRecord != null && i_mapRecord.IsRaid();
|
||||
}
|
||||
|
||||
public bool IsRaidOrHeroicDungeon()
|
||||
{
|
||||
return IsRaid() || IsHeroic();
|
||||
}
|
||||
|
||||
public bool IsHeroic()
|
||||
{
|
||||
DifficultyRecord difficulty = CliDB.DifficultyStorage.LookupByKey(i_spawnMode);
|
||||
@@ -4893,7 +4897,6 @@ namespace Game.Maps
|
||||
|
||||
// for normal instances schedule the reset after all players have left
|
||||
SetResetSchedule(true);
|
||||
Global.InstanceSaveMgr.UnloadInstanceSave(GetInstanceId());
|
||||
}
|
||||
|
||||
public void CreateInstanceData()
|
||||
@@ -4942,10 +4945,11 @@ namespace Game.Maps
|
||||
public bool 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 by InstanceSaveManager
|
||||
// the instance must be deleted from the DB
|
||||
|
||||
if (HavePlayers())
|
||||
{
|
||||
// on manual reset, fail
|
||||
if (method == InstanceResetMethod.All || method == InstanceResetMethod.ChangeDifficulty)
|
||||
{
|
||||
// notify the players to leave the instance so it can be reset
|
||||
@@ -4954,24 +4958,15 @@ namespace Game.Maps
|
||||
}
|
||||
else
|
||||
{
|
||||
bool doUnload = true;
|
||||
// on lock expiration boot players (do we also care about extension state?)
|
||||
if (method == InstanceResetMethod.Global)
|
||||
{
|
||||
// set the homebind timer for players inside (1 minute)
|
||||
foreach (Player player in GetPlayers())
|
||||
{
|
||||
InstanceBind bind = player.GetBoundInstance(GetId(), GetDifficultyID());
|
||||
if (bind != null && bind.extendState != 0 && bind.save.GetInstanceId() == GetInstanceId())
|
||||
doUnload = false;
|
||||
else
|
||||
player.m_InstanceValid = false;
|
||||
}
|
||||
|
||||
if (doUnload && HasPermBoundPlayers()) // check if any unloaded players have a nonexpired save to this
|
||||
doUnload = false;
|
||||
}
|
||||
|
||||
if (doUnload)
|
||||
if (!HasPermBoundPlayers())
|
||||
{
|
||||
// the unload timer is not started
|
||||
// instead the map will unload immediately after the players have left
|
||||
@@ -5075,28 +5070,9 @@ namespace Game.Maps
|
||||
base.UnloadAll();
|
||||
}
|
||||
|
||||
public void SendResetWarnings(uint timeLeft)
|
||||
{
|
||||
foreach (Player player in GetPlayers())
|
||||
player.SendInstanceResetWarning(GetId(), player.GetDifficultyID(GetEntry()), timeLeft, true);
|
||||
}
|
||||
|
||||
public void SetResetSchedule(bool on)
|
||||
{
|
||||
// only for normal instances
|
||||
// the reset time is only scheduled when there are no payers inside
|
||||
// it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
|
||||
if (IsDungeon() && !HavePlayers() && !IsRaidOrHeroicDungeon())
|
||||
{
|
||||
InstanceSave save = Global.InstanceSaveMgr.GetInstanceSave(GetInstanceId());
|
||||
if (save != null)
|
||||
Global.InstanceSaveMgr.ScheduleReset(on, save.GetResetTime(),
|
||||
new InstanceSaveManager.InstResetEvent(0, GetId(), GetDifficultyID(), GetInstanceId()));
|
||||
else
|
||||
Log.outError(LogFilter.Maps,
|
||||
"InstanceMap.SetResetSchedule: cannot turn schedule {0}, there is no save information for instance (map [id: {1}, name: {2}], instance id: {3}, difficulty: {4})",
|
||||
on ? "on" : "off", GetId(), GetMapName(), GetInstanceId(), GetDifficultyID());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool HasPermBoundPlayers()
|
||||
@@ -5115,12 +5091,6 @@ namespace Game.Maps
|
||||
return GetEntry().MaxPlayers;
|
||||
}
|
||||
|
||||
public uint GetMaxResetDelay()
|
||||
{
|
||||
MapDifficultyRecord mapDiff = GetMapDifficulty();
|
||||
return mapDiff != null ? mapDiff.GetRaidDuration() : 0;
|
||||
}
|
||||
|
||||
public int GetTeamIdInInstance()
|
||||
{
|
||||
if (Global.WorldStateMgr.GetValue(WorldStates.TeamInInstanceAlliance, this) != 0)
|
||||
|
||||
@@ -242,6 +242,50 @@ namespace Game.Entities
|
||||
return FindMap_i(mapId, instanceId);
|
||||
}
|
||||
|
||||
public uint FindInstanceIdForPlayer(uint mapId, Player player)
|
||||
{
|
||||
MapRecord entry = CliDB.MapStorage.LookupByKey(mapId);
|
||||
if (entry == null)
|
||||
return 0;
|
||||
|
||||
if (entry.IsBattlegroundOrArena())
|
||||
return player.GetBattlegroundId();
|
||||
else if (entry.IsDungeon())
|
||||
{
|
||||
Group group = player.GetGroup();
|
||||
Difficulty difficulty = group != null ? group.GetDifficultyID(entry) : player.GetDifficultyID(entry);
|
||||
MapDb2Entries entries = new(entry, Global.DB2Mgr.GetDownscaledMapDifficultyData(mapId, ref difficulty));
|
||||
|
||||
ObjectGuid instanceOwnerGuid = group ? group.GetRecentInstanceOwner(mapId) : player.GetGUID();
|
||||
InstanceLock instanceLock = Global.InstanceLockMgr.FindActiveInstanceLock(instanceOwnerGuid, entries);
|
||||
uint newInstanceId = 0;
|
||||
if (instanceLock != null)
|
||||
newInstanceId = instanceLock.GetInstanceId();
|
||||
else if (!entries.MapDifficulty.HasResetSchedule()) // Try finding instance id for normal dungeon
|
||||
newInstanceId = group ? group.GetRecentInstanceId(mapId) : player.GetRecentInstanceId(mapId);
|
||||
|
||||
if (newInstanceId == 0)
|
||||
return 0;
|
||||
|
||||
Map map = FindMap(mapId, newInstanceId);
|
||||
|
||||
// is is possible that instance id is already in use by another group for boss-based locks
|
||||
if (!entries.IsInstanceIdBound() && instanceLock != null && map != null && map.ToInstanceMap().GetInstanceLock() != instanceLock)
|
||||
return 0;
|
||||
|
||||
return newInstanceId;
|
||||
}
|
||||
else if (entry.IsGarrison())
|
||||
return (uint)player.GetGUID().GetCounter();
|
||||
else
|
||||
{
|
||||
if (entry.IsSplitByFaction())
|
||||
return (uint)player.GetTeamId();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(uint diff)
|
||||
{
|
||||
i_timer.Update(diff);
|
||||
|
||||
@@ -632,11 +632,11 @@ namespace Game.Networking.Packets
|
||||
_worldPacket.WriteInt32(NewTimeRemaining);
|
||||
}
|
||||
|
||||
public int MapID;
|
||||
public int OldTimeRemaining;
|
||||
public long ServerTime;
|
||||
public int MapID;
|
||||
public uint DifficultyID;
|
||||
public int NewTimeRemaining;
|
||||
public int OldTimeRemaining;
|
||||
}
|
||||
|
||||
class CalendarCommunityInvite : ServerPacket
|
||||
|
||||
@@ -65,8 +65,9 @@ namespace Game.Scenarios
|
||||
Criteria criteria = Global.CriteriaMgr.GetCriteria(iter.Key);
|
||||
switch (criteria.Entry.Type)
|
||||
{
|
||||
// Blizzard only appears to store creature kills
|
||||
// Blizzard only appears to store creature kills and dungeon encounters
|
||||
case CriteriaType.KillCreature:
|
||||
case CriteriaType.DefeatDungeonEncounter:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
@@ -131,6 +132,7 @@ namespace Game.Scenarios
|
||||
{
|
||||
// Blizzard appears to only stores creatures killed progress for unknown reasons. Either technical shortcoming or intentional
|
||||
case CriteriaType.KillCreature:
|
||||
case CriteriaType.DefeatDungeonEncounter:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
|
||||
@@ -681,7 +681,7 @@ namespace Game.Scripting
|
||||
public virtual void OnSave(Player player) { }
|
||||
|
||||
// Called when a player is bound to an instance
|
||||
public virtual void OnBindToInstance(Player player, Difficulty difficulty, uint mapId, bool permanent, BindExtensionState extendState) { }
|
||||
public virtual void OnBindToInstance(Player player, Difficulty difficulty, uint mapId, bool permanent, byte extendState) { }
|
||||
|
||||
// Called when a player switches to a new zone
|
||||
public virtual void OnUpdateZone(Player player, uint newZone, uint newArea) { }
|
||||
|
||||
@@ -963,7 +963,7 @@ namespace Game.Scripting
|
||||
{
|
||||
ForEach<PlayerScript>(p => p.OnSave(player));
|
||||
}
|
||||
public void OnPlayerBindToInstance(Player player, Difficulty difficulty, uint mapid, bool permanent, BindExtensionState extendState)
|
||||
public void OnPlayerBindToInstance(Player player, Difficulty difficulty, uint mapid, bool permanent, byte extendState)
|
||||
{
|
||||
ForEach<PlayerScript>(p => p.OnBindToInstance(player, difficulty, mapid, permanent, extendState));
|
||||
}
|
||||
|
||||
@@ -5411,22 +5411,16 @@ namespace Game.Spells
|
||||
return SpellCastResult.SummonPending;
|
||||
|
||||
// check if our map is dungeon
|
||||
MapRecord map = CliDB.MapStorage.LookupByKey(m_caster.GetMapId());
|
||||
if (map.IsDungeon())
|
||||
InstanceMap map = m_caster.GetMap().ToInstanceMap();
|
||||
if (map != null)
|
||||
{
|
||||
uint mapId = m_caster.GetMap().GetId();
|
||||
Difficulty difficulty = m_caster.GetMap().GetDifficultyID();
|
||||
if (map.IsRaid())
|
||||
{
|
||||
InstanceBind targetBind = target.GetBoundInstance(mapId, difficulty);
|
||||
if (targetBind != null)
|
||||
{
|
||||
InstanceBind casterBind = m_caster.ToPlayer().GetBoundInstance(mapId, difficulty);
|
||||
if (casterBind != null)
|
||||
if (targetBind.perm && targetBind.save != casterBind.save)
|
||||
uint mapId = map.GetId();
|
||||
Difficulty difficulty = map.GetDifficultyID();
|
||||
InstanceLock mapLock = map.GetInstanceLock();
|
||||
if (mapLock != null)
|
||||
if (Global.InstanceLockMgr.CanJoinInstanceLock(target.GetGUID(), new MapDb2Entries(mapId, difficulty), mapLock) != TransferAbortReason.None)
|
||||
return SpellCastResult.TargetLockedToRaidInstance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target.Satisfy(Global.ObjectMgr.GetAccessRequirement(mapId, difficulty), mapId))
|
||||
return SpellCastResult.BadTargets;
|
||||
}
|
||||
|
||||
@@ -514,8 +514,8 @@ namespace Game
|
||||
|
||||
// Must be called before `respawn` data
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading instances...");
|
||||
Global.InstanceSaveMgr.LoadInstances();
|
||||
|
||||
Global.MapMgr.InitInstanceIds();
|
||||
Global.InstanceLockMgr.Load();
|
||||
|
||||
Log.outInfo(LogFilter.ServerLoading, "Loading Localization strings...");
|
||||
@@ -1493,9 +1493,6 @@ namespace Game
|
||||
Global.GuildMgr.SaveGuilds();
|
||||
}
|
||||
|
||||
// update the instance reset times
|
||||
Global.InstanceSaveMgr.Update();
|
||||
|
||||
// Check for shutdown warning
|
||||
if (_guidWarn && !_guidAlert)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user