Ported .Net Core commits:

hondacrx:
- Initial commit: Switch to .Net Core 2.0
- Fix build and removed not needed files
Fabi:
- Updated solution platforms.
- Changed folder structure.
- Change library target framework to netstandard2.0.
- Updated solution platforms again...
- Removed windows specific kernel32 function usage (Ctrl-C handler).
This commit is contained in:
Fabian
2017-10-26 17:23:44 +02:00
parent 227702e19c
commit a3dc7b3f48
844 changed files with 26064 additions and 1824 deletions
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2012-2017 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 Game.Entities;
using System.Collections.Generic;
namespace Game.DungeonFinding
{
public class LFGGroupData
{
public LFGGroupData()
{
m_State = LfgState.None;
m_OldState = LfgState.None;
m_KicksLeft = SharedConst.LFGMaxKicks;
}
public bool IsLfgGroup()
{
return m_OldState != LfgState.None;
}
public void SetState(LfgState state)
{
switch (state)
{
case LfgState.None:
m_Dungeon = 0;
m_KicksLeft = SharedConst.LFGMaxKicks;
m_OldState = state;
break;
case LfgState.FinishedDungeon:
case LfgState.Dungeon:
m_OldState = state;
break;
}
m_State = state;
}
public void RestoreState()
{
m_State = m_OldState;
}
public void AddPlayer(ObjectGuid guid)
{
m_Players.Add(guid);
}
public byte RemovePlayer(ObjectGuid guid)
{
m_Players.Remove(guid);
return (byte)m_Players.Count;
}
public void RemoveAllPlayers()
{
m_Players.Clear();
}
public void SetLeader(ObjectGuid guid)
{
m_Leader = guid;
}
public void SetDungeon(uint dungeon)
{
m_Dungeon = dungeon;
}
public void DecreaseKicksLeft()
{
if (m_KicksLeft != 0)
--m_KicksLeft;
}
public LfgState GetState()
{
return m_State;
}
public LfgState GetOldState()
{
return m_OldState;
}
public List<ObjectGuid> GetPlayers()
{
return m_Players;
}
public byte GetPlayerCount()
{
return (byte)m_Players.Count;
}
public ObjectGuid GetLeader()
{
return m_Leader;
}
public uint GetDungeon(bool asId = true)
{
if (asId)
return (m_Dungeon & 0x00FFFFFF);
else
return m_Dungeon;
}
public byte GetKicksLeft()
{
return m_KicksLeft;
}
public void SetVoteKick(bool active)
{
m_VoteKickActive = active;
}
public bool IsVoteKickActive()
{
return m_VoteKickActive;
}
// General
LfgState m_State;
LfgState m_OldState;
ObjectGuid m_Leader;
List<ObjectGuid> m_Players = new List<ObjectGuid>();
// Dungeon
uint m_Dungeon;
// Vote Kick
byte m_KicksLeft;
bool m_VoteKickActive;
}
}
File diff suppressed because it is too large Load Diff
+131
View File
@@ -0,0 +1,131 @@
/*
* Copyright (C) 2012-2017 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 Game.Entities;
using System.Collections.Generic;
using Game.Network.Packets;
namespace Game.DungeonFinding
{
public class LFGPlayerData
{
public LFGPlayerData()
{
m_State = LfgState.None;
m_OldState = LfgState.None;
}
public void SetTicket(RideTicket ticket)
{
m_Ticket = ticket;
}
public void SetState(LfgState state)
{
switch (state)
{
case LfgState.None:
case LfgState.FinishedDungeon:
m_Roles = 0;
m_SelectedDungeons.Clear();
goto case LfgState.Dungeon;
case LfgState.Dungeon:
m_OldState = state;
break;
}
m_State = state;
}
public void RestoreState()
{
if (m_OldState == LfgState.None)
{
m_SelectedDungeons.Clear();
m_Roles = 0;
}
m_State = m_OldState;
}
public void SetTeam(Team team)
{
m_Team = team;
}
public void SetGroup(ObjectGuid group)
{
m_Group = group;
}
public void SetRoles(LfgRoles roles)
{
m_Roles = roles;
}
public void SetSelectedDungeons(List<uint> dungeons)
{
m_SelectedDungeons = dungeons;
}
public RideTicket GetTicket()
{
return m_Ticket;
}
public LfgState GetState()
{
return m_State;
}
public LfgState GetOldState()
{
return m_OldState;
}
public Team GetTeam()
{
return m_Team;
}
public ObjectGuid GetGroup()
{
return m_Group;
}
public LfgRoles GetRoles()
{
return m_Roles;
}
public List<uint> GetSelectedDungeons()
{
return m_SelectedDungeons;
}
// General
RideTicket m_Ticket;
LfgState m_State;
LfgState m_OldState;
// Player
Team m_Team;
ObjectGuid m_Group;
// Queue
LfgRoles m_Roles;
List<uint> m_SelectedDungeons = new List<uint>();
}
}
+780
View File
@@ -0,0 +1,780 @@
/*
* Copyright (C) 2012-2017 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 Game.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Game.DungeonFinding
{
public class LFGQueue
{
public static string ConcatenateGuids(List<ObjectGuid> guids)
{
if (guids.Empty())
return "";
// need the guids in order to avoid duplicates
StringBuilder val = new StringBuilder();
guids.Sort();
var it = guids.First();
val.Append(it);
foreach (var guid in guids)
{
if (guid == it)
continue;
val.AppendFormat("|{0}", guid);
}
return val.ToString();
}
public static string GetRolesString(LfgRoles roles)
{
StringBuilder rolesstr = new StringBuilder();
if (roles.HasAnyFlag(LfgRoles.Tank))
rolesstr.Append("Tank");
if (roles.HasAnyFlag(LfgRoles.Healer))
{
if (rolesstr.Capacity != 0)
rolesstr.Append(", ");
rolesstr.Append("Healer");
}
if (roles.HasAnyFlag(LfgRoles.Damage))
{
if (rolesstr.Capacity != 0)
rolesstr.Append(", ");
rolesstr.Append("Damage");
}
if (roles.HasAnyFlag(LfgRoles.Leader))
{
if (rolesstr.Capacity != 0)
rolesstr.Append(", ");
rolesstr.Append("Leader");
}
if (rolesstr.Capacity == 0)
rolesstr.Append("None");
return rolesstr.ToString();
}
public static string ConcatenateDungeons(List<uint> dungeons)
{
string str = "";
if (!dungeons.Empty())
{
foreach (var it in dungeons)
{
if (!string.IsNullOrEmpty(str))
str += ", ";
str += it;
}
}
return str;
}
string GetCompatibleString(LfgCompatibility compatibles)
{
switch (compatibles)
{
case LfgCompatibility.Pending:
return "Pending";
case LfgCompatibility.BadStates:
return "Compatibles (Bad States)";
case LfgCompatibility.Match:
return "Match";
case LfgCompatibility.WithLessPlayers:
return "Compatibles (Not enough players)";
case LfgCompatibility.HasIgnores:
return "Has ignores";
case LfgCompatibility.MultipleLfgGroups:
return "Multiple Lfg Groups";
case LfgCompatibility.NoDungeons:
return "Incompatible dungeons";
case LfgCompatibility.NoRoles:
return "Incompatible roles";
case LfgCompatibility.TooMuchPlayers:
return "Too much players";
case LfgCompatibility.WrongGroupSize:
return "Wrong group size";
default:
return "Unknown";
}
}
public void AddToQueue(ObjectGuid guid, bool reAdd = false)
{
if (!QueueDataStore.ContainsKey(guid))
{
Log.outError(LogFilter.Lfg, "AddToQueue: Queue data not found for [{0}]", guid);
return;
}
if (reAdd)
AddToFrontCurrentQueue(guid);
else
AddToNewQueue(guid);
}
public void RemoveFromQueue(ObjectGuid guid)
{
RemoveFromNewQueue(guid);
RemoveFromCurrentQueue(guid);
RemoveFromCompatibles(guid);
string sguid = guid.ToString();
var itDelete = QueueDataStore.LastOrDefault().Key;
foreach (var key in QueueDataStore.Keys.ToList())
{
var data = QueueDataStore[key];
if (key != guid)
{
if (data.bestCompatible.Contains(sguid))
{
data.bestCompatible = "";
FindBestCompatibleInQueue(key, data);
}
}
else
itDelete = key;
}
if (!itDelete.IsEmpty())
QueueDataStore.Remove(itDelete);
}
public void AddToNewQueue(ObjectGuid guid)
{
newToQueueStore.Add(guid);
}
public void RemoveFromNewQueue(ObjectGuid guid)
{
newToQueueStore.Remove(guid);
}
public void AddToCurrentQueue(ObjectGuid guid)
{
currentQueueStore.Add(guid);
}
void AddToFrontCurrentQueue(ObjectGuid guid)
{
currentQueueStore.Insert(0, guid);
}
public void RemoveFromCurrentQueue(ObjectGuid guid)
{
currentQueueStore.Remove(guid);
}
public void AddQueueData(ObjectGuid guid, long joinTime, List<uint> dungeons, Dictionary<ObjectGuid, LfgRoles> rolesMap)
{
QueueDataStore[guid] = new LfgQueueData(joinTime, dungeons, rolesMap);
AddToQueue(guid);
}
public void RemoveQueueData(ObjectGuid guid)
{
QueueDataStore.Remove(guid);
}
public void UpdateWaitTimeAvg(int waitTime, uint dungeonId)
{
LfgWaitTime wt = waitTimesAvgStore[dungeonId];
uint old_number = wt.number++;
wt.time = (int)((wt.time * old_number + waitTime) / wt.number);
}
public void UpdateWaitTimeTank(int waitTime, uint dungeonId)
{
LfgWaitTime wt = waitTimesTankStore[dungeonId];
uint old_number = wt.number++;
wt.time = (int)((wt.time * old_number + waitTime) / wt.number);
}
public void UpdateWaitTimeHealer(int waitTime, uint dungeonId)
{
LfgWaitTime wt = waitTimesHealerStore[dungeonId];
uint old_number = wt.number++;
wt.time = (int)((wt.time * old_number + waitTime) / wt.number);
}
public void UpdateWaitTimeDps(int waitTime, uint dungeonId)
{
LfgWaitTime wt = waitTimesDpsStore[dungeonId];
uint old_number = wt.number++;
wt.time = (int)((wt.time * old_number + waitTime) / wt.number);
}
void RemoveFromCompatibles(ObjectGuid guid)
{
string strGuid = guid.ToString();
Log.outDebug(LogFilter.Lfg, "RemoveFromCompatibles: Removing [{0}]", guid);
foreach (var itNext in CompatibleMapStore.ToList())
{
if (itNext.Key.Contains(strGuid))
CompatibleMapStore.Remove(itNext.Key);
}
}
void SetCompatibles(string key, LfgCompatibility compatibles)
{
if (!CompatibleMapStore.ContainsKey(key))
CompatibleMapStore[key] = new LfgCompatibilityData();
CompatibleMapStore[key].compatibility = compatibles;
}
void SetCompatibilityData(string key, LfgCompatibilityData data)
{
CompatibleMapStore[key] = data;
}
LfgCompatibility GetCompatibles(string key)
{
var compatibilityData = CompatibleMapStore.LookupByKey(key);
if (compatibilityData != null)
return compatibilityData.compatibility;
return LfgCompatibility.Pending;
}
LfgCompatibilityData GetCompatibilityData(string key)
{
var compatibilityData = CompatibleMapStore.LookupByKey(key);
if (compatibilityData != null)
return compatibilityData;
return null;
}
public byte FindGroups()
{
byte proposals = 0;
List<ObjectGuid> firstNew = new List<ObjectGuid>();
while (!newToQueueStore.Empty())
{
ObjectGuid frontguid = newToQueueStore.First();
Log.outDebug(LogFilter.Lfg, "FindGroups: checking [{0}] newToQueue({1}), currentQueue({2})", frontguid, newToQueueStore.Count, currentQueueStore.Count);
firstNew.Clear();
firstNew.Add(frontguid);
RemoveFromNewQueue(frontguid);
List<ObjectGuid> temporalList = new List<ObjectGuid>(currentQueueStore);
LfgCompatibility compatibles = FindNewGroups(firstNew, temporalList);
if (compatibles == LfgCompatibility.Match)
++proposals;
else
AddToCurrentQueue(frontguid); // Lfg group not found, add this group to the queue.
}
return proposals;
}
LfgCompatibility FindNewGroups(List<ObjectGuid> check, List<ObjectGuid> all)
{
string strGuids = ConcatenateGuids(check);
LfgCompatibility compatibles = GetCompatibles(strGuids);
Log.outDebug(LogFilter.Lfg, "FindNewGroup: ({0}): {1} - all({2})", strGuids, GetCompatibleString(compatibles), ConcatenateGuids(all));
if (compatibles == LfgCompatibility.Pending) // Not previously cached, calculate
compatibles = CheckCompatibility(check);
if (compatibles == LfgCompatibility.BadStates && Global.LFGMgr.AllQueued(check))
{
Log.outDebug(LogFilter.Lfg, "FindNewGroup: ({0}) compatibles (cached) changed from bad states to match", strGuids);
SetCompatibles(strGuids, LfgCompatibility.Match);
return LfgCompatibility.Match;
}
if (compatibles != LfgCompatibility.WithLessPlayers)
return compatibles;
// Try to match with queued groups
while (!all.Empty())
{
check.Add(all.First());
all.RemoveAt(0);
LfgCompatibility subcompatibility = FindNewGroups(check, all);
if (subcompatibility == LfgCompatibility.Match)
return LfgCompatibility.Match;
check.RemoveAt(check.Count - 1);
}
return compatibles;
}
LfgCompatibility CheckCompatibility(List<ObjectGuid> check)
{
string strGuids = ConcatenateGuids(check);
LfgProposal proposal = new LfgProposal();
List<uint> proposalDungeons;
Dictionary<ObjectGuid, ObjectGuid> proposalGroups = new Dictionary<ObjectGuid, ObjectGuid>();
Dictionary<ObjectGuid, LfgRoles> proposalRoles = new Dictionary<ObjectGuid, LfgRoles>();
// Check for correct size
if (check.Count > MapConst.MaxGroupSize || check.Empty())
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}): Size wrong - Not compatibles", strGuids);
return LfgCompatibility.WrongGroupSize;
}
// Check all-but-new compatiblitity
if (check.Count > 2)
{
ObjectGuid frontGuid = check.First();
check.RemoveAt(0);
// Check all-but-new compatibilities (New, A, B, C, D) -. check(A, B, C, D)
LfgCompatibility child_compatibles = CheckCompatibility(check);
if (child_compatibles < LfgCompatibility.WithLessPlayers) // Group not compatible
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) child {1} not compatibles", strGuids, ConcatenateGuids(check));
SetCompatibles(strGuids, child_compatibles);
return child_compatibles;
}
check.Insert(0, frontGuid);
}
// Check if more than one LFG group and number of players joining
byte numPlayers = 0;
byte numLfgGroups = 0;
foreach (var guid in check)
{
if (!(numLfgGroups < 2) && !(numPlayers <= MapConst.MaxGroupSize))
break; ;
var itQueue = QueueDataStore.LookupByKey(guid);
if (itQueue == null)
{
Log.outError(LogFilter.Lfg, "CheckCompatibility: [{0}] is not queued but listed as queued!", guid);
RemoveFromQueue(guid);
return LfgCompatibility.Pending;
}
// Store group so we don't need to call Mgr to get it later (if it's player group will be 0 otherwise would have joined as group)
foreach (var it2 in itQueue.roles)
proposalGroups[it2.Key] = guid.IsPlayer() ? guid : ObjectGuid.Empty;
numPlayers += (byte)itQueue.roles.Count;
if (Global.LFGMgr.IsLfgGroup(guid))
{
if (numLfgGroups == 0)
proposal.group = guid;
++numLfgGroups;
}
}
// Group with less that MAXGROUPSIZE members always compatible
if (check.Count == 1 && numPlayers != MapConst.MaxGroupSize)
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) sigle group. Compatibles", strGuids);
var guid = check.First();
var itQueue = QueueDataStore.LookupByKey(guid);
LfgCompatibilityData data = new LfgCompatibilityData(LfgCompatibility.WithLessPlayers);
data.roles = itQueue.roles;
Global.LFGMgr.CheckGroupRoles(data.roles);
UpdateBestCompatibleInQueue(guid, itQueue, strGuids, data.roles);
SetCompatibilityData(strGuids, data);
return LfgCompatibility.WithLessPlayers;
}
if (numLfgGroups > 1)
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) More than one Lfggroup ({1})", strGuids, numLfgGroups);
SetCompatibles(strGuids, LfgCompatibility.MultipleLfgGroups);
return LfgCompatibility.MultipleLfgGroups;
}
if (numPlayers > MapConst.MaxGroupSize)
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Too much players ({1})", strGuids, numPlayers);
SetCompatibles(strGuids, LfgCompatibility.TooMuchPlayers);
return LfgCompatibility.TooMuchPlayers;
}
// If it's single group no need to check for duplicate players, ignores, bad roles or bad dungeons as it's been checked before joining
if (check.Count > 1)
{
foreach (var it in check)
{
Dictionary<ObjectGuid, LfgRoles> roles = QueueDataStore[it].roles;
foreach (var rolePair in roles)
{
KeyValuePair<ObjectGuid, LfgRoles> itPlayer = new KeyValuePair<ObjectGuid, LfgRoles>();
foreach (var _player in proposalRoles)
{
itPlayer = _player;
if (rolePair.Key == itPlayer.Key)
Log.outError(LogFilter.Lfg, "CheckCompatibility: ERROR! Player multiple times in queue! [{0}]", rolePair.Key);
else if (Global.LFGMgr.HasIgnore(rolePair.Key, itPlayer.Key))
break;
}
if (itPlayer.Key == proposalRoles.LastOrDefault().Key)
proposalRoles[rolePair.Key] = rolePair.Value;
}
}
byte playersize = (byte)(numPlayers - proposalRoles.Count);
if (playersize != 0)
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) not compatible, {1} players are ignoring each other", strGuids, playersize);
SetCompatibles(strGuids, LfgCompatibility.HasIgnores);
return LfgCompatibility.HasIgnores;
}
StringBuilder o;
Dictionary<ObjectGuid, LfgRoles> debugRoles = proposalRoles;
if (!Global.LFGMgr.CheckGroupRoles(proposalRoles))
{
o = new StringBuilder();
foreach (var it in debugRoles)
o.AppendFormat(", {0}: {1}", it.Key, GetRolesString(it.Value));
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Roles not compatible{1}", strGuids, o.ToString());
SetCompatibles(strGuids, LfgCompatibility.NoRoles);
return LfgCompatibility.NoRoles;
}
var itguid = check.First();
proposalDungeons = QueueDataStore[itguid].dungeons;
o = new StringBuilder();
o.AppendFormat(", {0}: ({1})", itguid, Global.LFGMgr.ConcatenateDungeons(proposalDungeons));
foreach (var guid in check)
{
if (guid == itguid)
continue;
List<uint> temporal;
List<uint> dungeons = QueueDataStore[itguid].dungeons;
o.AppendFormat(", {0}: ({1})", guid, Global.LFGMgr.ConcatenateDungeons(dungeons));
temporal = proposalDungeons.Intersect(dungeons).ToList();
proposalDungeons = temporal;
}
if (proposalDungeons.Empty())
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) No compatible dungeons{1}", strGuids, o.ToString());
SetCompatibles(strGuids, LfgCompatibility.NoDungeons);
return LfgCompatibility.NoDungeons;
}
}
else
{
ObjectGuid gguid = check.First();
LfgQueueData queue = QueueDataStore[gguid];
proposalDungeons = queue.dungeons;
proposalRoles = queue.roles;
Global.LFGMgr.CheckGroupRoles(proposalRoles); // assing new roles
}
// Enough players?
if (numPlayers != MapConst.MaxGroupSize)
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Compatibles but not enough players({1})", strGuids, numPlayers);
LfgCompatibilityData data = new LfgCompatibilityData(LfgCompatibility.WithLessPlayers);
data.roles = proposalRoles;
foreach (var guid in check)
{
var queueData = QueueDataStore.LookupByKey(guid);
UpdateBestCompatibleInQueue(guid, queueData, strGuids, data.roles);
}
SetCompatibilityData(strGuids, data);
return LfgCompatibility.WithLessPlayers;
}
ObjectGuid _guid = check.First();
proposal.queues = check;
proposal.isNew = numLfgGroups != 1 || Global.LFGMgr.GetOldState(_guid) != LfgState.Dungeon;
if (!Global.LFGMgr.AllQueued(check))
{
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Group MATCH but can't create proposal!", strGuids);
SetCompatibles(strGuids, LfgCompatibility.BadStates);
return LfgCompatibility.BadStates;
}
// Create a new proposal
proposal.cancelTime = Time.UnixTime + SharedConst.LFGTimeProposal;
proposal.state = LfgProposalState.Initiating;
proposal.leader = ObjectGuid.Empty;
proposal.dungeonId = proposalDungeons.SelectRandom();
bool leader = false;
foreach (var rolePair in proposalRoles)
{
// Assing new leader
if (rolePair.Value.HasAnyFlag(LfgRoles.Leader))
{
if (!leader || proposal.leader.IsEmpty() || Convert.ToBoolean(RandomHelper.IRand(0, 1)))
proposal.leader = rolePair.Key;
leader = true;
}
else if (!leader && (proposal.leader.IsEmpty() || Convert.ToBoolean(RandomHelper.IRand(0, 1))))
proposal.leader = rolePair.Key;
// Assing player data and roles
LfgProposalPlayer data = new LfgProposalPlayer();
data.role = rolePair.Value;
data.group = proposalGroups.LookupByKey(rolePair.Key);
if (!proposal.isNew && !data.group.IsEmpty() && data.group == proposal.group) // Player from existing group, autoaccept
data.accept = LfgAnswer.Agree;
proposal.players[rolePair.Key] = data;
}
// Mark proposal members as not queued (but not remove queue data)
foreach (var guid in proposal.queues)
{
RemoveFromNewQueue(guid);
RemoveFromCurrentQueue(guid);
}
Global.LFGMgr.AddProposal(proposal);
Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) MATCH! Group formed", strGuids);
SetCompatibles(strGuids, LfgCompatibility.Match);
return LfgCompatibility.Match;
}
public void UpdateQueueTimers(byte queueId, long currTime)
{
Log.outDebug(LogFilter.Lfg, "Updating queue timers...");
foreach (var itQueue in QueueDataStore)
{
LfgQueueData queueinfo = itQueue.Value;
uint dungeonId = queueinfo.dungeons.FirstOrDefault();
uint queuedTime = (uint)(currTime - queueinfo.joinTime);
LfgRoles role = LfgRoles.None;
int waitTime = -1;
if (!waitTimesTankStore.ContainsKey(dungeonId))
waitTimesTankStore[dungeonId] = new LfgWaitTime();
if (!waitTimesHealerStore.ContainsKey(dungeonId))
waitTimesHealerStore[dungeonId] = new LfgWaitTime();
if (!waitTimesDpsStore.ContainsKey(dungeonId))
waitTimesDpsStore[dungeonId] = new LfgWaitTime();
if (!waitTimesAvgStore.ContainsKey(dungeonId))
waitTimesAvgStore[dungeonId] = new LfgWaitTime();
int wtTank = waitTimesTankStore[dungeonId].time;
int wtHealer = waitTimesHealerStore[dungeonId].time;
int wtDps = waitTimesDpsStore[dungeonId].time;
int wtAvg = waitTimesAvgStore[dungeonId].time;
foreach (var itPlayer in queueinfo.roles)
role |= itPlayer.Value;
role &= ~LfgRoles.Leader;
switch (role)
{
case LfgRoles.None: // Should not happen - just in case
waitTime = -1;
break;
case LfgRoles.Tank:
waitTime = wtTank;
break;
case LfgRoles.Healer:
waitTime = wtHealer;
break;
case LfgRoles.Damage:
waitTime = wtDps;
break;
default:
waitTime = wtAvg;
break;
}
if (string.IsNullOrEmpty(queueinfo.bestCompatible))
FindBestCompatibleInQueue(itQueue.Key, itQueue.Value);
LfgQueueStatusData queueData = new LfgQueueStatusData(queueId, dungeonId, waitTime, wtAvg, wtTank, wtHealer, wtDps, queuedTime, queueinfo.tanks, queueinfo.healers, queueinfo.dps);
foreach (var itPlayer in queueinfo.roles)
{
ObjectGuid pguid = itPlayer.Key;
Global.LFGMgr.SendLfgQueueStatus(pguid, queueData);
}
}
}
public long GetJoinTime(ObjectGuid guid)
{
var queueData = QueueDataStore.LookupByKey(guid);
if (queueData != null)
return queueData.joinTime;
return 0;
}
public string DumpQueueInfo()
{
uint players = 0;
uint groups = 0;
uint playersInGroup = 0;
for (byte i = 0; i < 2; ++i)
{
List<ObjectGuid> queue = i != 0 ? newToQueueStore : currentQueueStore;
foreach (var guid in queue)
{
if (guid.IsParty())
{
groups++;
playersInGroup += Global.LFGMgr.GetPlayerCount(guid);
}
else
players++;
}
}
return string.Format("Queued Players: {0} (in group: {1}) Groups: {2}\n", players, playersInGroup, groups);
}
public string DumpCompatibleInfo(bool full = false)
{
string str = "Compatible Map size: " + CompatibleMapStore.Count + "\n";
if (full)
{
foreach (var pair in CompatibleMapStore)
str += "(" + pair.Key + "): " + GetCompatibleString(pair.Value.compatibility) + "\n";
}
return str;
}
void FindBestCompatibleInQueue(ObjectGuid guid, LfgQueueData data)
{
Log.outDebug(LogFilter.Lfg, "FindBestCompatibleInQueue: {0}", guid);
foreach (var pair in CompatibleMapStore)
{
if (pair.Value.compatibility == LfgCompatibility.WithLessPlayers && pair.Key.Contains(guid.ToString()))
UpdateBestCompatibleInQueue(guid, data, pair.Key, pair.Value.roles);
}
}
public void UpdateBestCompatibleInQueue(ObjectGuid guid, LfgQueueData queueData, string key, Dictionary<ObjectGuid, LfgRoles> roles)
{
byte storedSize = (byte)(string.IsNullOrEmpty(queueData.bestCompatible) ? 0 : queueData.bestCompatible.Count(p => p == '|') + 1);
byte size = (byte)(key.Count(p => p == '|') + 1);
if (size <= storedSize)
return;
Log.outDebug(LogFilter.Lfg, "UpdateBestCompatibleInQueue: Changed ({0}) to ({1}) as best compatible group for {2}",
queueData.bestCompatible, key, guid);
queueData.bestCompatible = key;
queueData.tanks = SharedConst.LFGTanksNeeded;
queueData.healers = SharedConst.LFGHealersNeeded;
queueData.dps = SharedConst.LFGDPSNeeded;
foreach (var it in roles)
{
LfgRoles role = it.Value;
if (role.HasAnyFlag(LfgRoles.Tank))
--queueData.tanks;
else if (role.HasAnyFlag(LfgRoles.Healer))
--queueData.healers;
else
--queueData.dps;
}
}
// Queue
Dictionary<ObjectGuid, LfgQueueData> QueueDataStore = new Dictionary<ObjectGuid, LfgQueueData>();
Dictionary<string, LfgCompatibilityData> CompatibleMapStore = new Dictionary<string, LfgCompatibilityData>();
Dictionary<uint, LfgWaitTime> waitTimesAvgStore = new Dictionary<uint, LfgWaitTime>();
Dictionary<uint, LfgWaitTime> waitTimesTankStore = new Dictionary<uint, LfgWaitTime>();
Dictionary<uint, LfgWaitTime> waitTimesHealerStore = new Dictionary<uint, LfgWaitTime>();
Dictionary<uint, LfgWaitTime> waitTimesDpsStore = new Dictionary<uint, LfgWaitTime>();
List<ObjectGuid> currentQueueStore = new List<ObjectGuid>();
List<ObjectGuid> newToQueueStore = new List<ObjectGuid>();
}
public class LfgCompatibilityData
{
public LfgCompatibilityData()
{
compatibility = LfgCompatibility.Pending;
}
public LfgCompatibilityData(LfgCompatibility _compatibility)
{
compatibility = _compatibility;
}
public LfgCompatibilityData(LfgCompatibility _compatibility, Dictionary<ObjectGuid, LfgRoles> _roles)
{
compatibility = _compatibility;
roles = _roles;
}
public LfgCompatibility compatibility;
public Dictionary<ObjectGuid, LfgRoles> roles;
}
// Stores player or group queue info
public class LfgQueueData
{
public LfgQueueData()
{
joinTime = Time.UnixTime;
tanks = SharedConst.LFGTanksNeeded;
healers = SharedConst.LFGHealersNeeded;
dps = SharedConst.LFGDPSNeeded;
}
public LfgQueueData(long _joinTime, List<uint> _dungeons, Dictionary<ObjectGuid, LfgRoles> _roles)
{
joinTime = _joinTime;
tanks = SharedConst.LFGTanksNeeded;
healers = SharedConst.LFGHealersNeeded;
dps = SharedConst.LFGDPSNeeded;
dungeons = _dungeons;
roles = _roles;
}
public long joinTime;
public byte tanks;
public byte healers;
public byte dps;
public List<uint> dungeons;
public Dictionary<ObjectGuid, LfgRoles> roles;
public string bestCompatible = "";
}
public struct LfgWaitTime
{
public int time;
public uint number;
}
}
+242
View File
@@ -0,0 +1,242 @@
/*
* Copyright (C) 2012-2017 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 Game.Entities;
using Game.Groups;
using Game.Maps;
using Game.Scripting;
namespace Game.DungeonFinding
{
class LFGPlayerScript : PlayerScript
{
public LFGPlayerScript() : base("LFGPlayerScript") { }
// Player Hooks
public override void OnLogout(Player player)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
if (!player.GetGroup())
Global.LFGMgr.LeaveLfg(player.GetGUID());
else if (player.GetSession().PlayerDisconnected())
Global.LFGMgr.LeaveLfg(player.GetGUID(), true);
}
public override void OnLogin(Player player)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
// Temporal: Trying to determine when group data and LFG data gets desynched
ObjectGuid guid = player.GetGUID();
ObjectGuid gguid = Global.LFGMgr.GetGroup(guid);
Group group = player.GetGroup();
if (group)
{
ObjectGuid gguid2 = group.GetGUID();
if (gguid != gguid2)
{
Log.outError(LogFilter.Lfg, "{0} on group {1} but LFG has group {2} saved... Fixing.", player.GetSession().GetPlayerInfo(), gguid2.ToString(), gguid.ToString());
Global.LFGMgr.SetupGroupMember(guid, group.GetGUID());
}
}
Global.LFGMgr.SetTeam(player.GetGUID(), player.GetTeam());
/// @todo - Restore LfgPlayerData and send proper status to player if it was in a group
}
public override void OnMapChanged(Player player)
{
Map map = player.GetMap();
if (Global.LFGMgr.inLfgDungeonMap(player.GetGUID(), map.GetId(), map.GetDifficultyID()))
{
Group group = player.GetGroup();
// This function is also called when players log in
// if for some reason the LFG system recognises the player as being in a LFG dungeon,
// but the player was loaded without a valid group, we'll teleport to homebind to prevent
// crashes or other undefined behaviour
if (!group)
{
Global.LFGMgr.LeaveLfg(player.GetGUID());
player.RemoveAurasDueToSpell(SharedConst.LFGSpellLuckOfTheDraw);
player.TeleportTo(player.GetHomebind());
Log.outError(LogFilter.Lfg, "LFGPlayerScript.OnMapChanged, Player {0} ({1}) is in LFG dungeon map but does not have a valid group! Teleporting to homebind.",
player.GetName(), player.GetGUID().ToString());
return;
}
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.next())
{
Player member = refe.GetSource();
if (member)
player.GetSession().SendNameQuery(member.GetGUID());
}
if (Global.LFGMgr.selectedRandomLfgDungeon(player.GetGUID()))
player.CastSpell(player, SharedConst.LFGSpellLuckOfTheDraw, true);
}
else
{
Group group = player.GetGroup();
if (group && group.GetMembersCount() == 1)
{
Global.LFGMgr.LeaveLfg(group.GetGUID());
group.Disband();
Log.outDebug(LogFilter.Lfg, "LFGPlayerScript::OnMapChanged, Player {0}({1}) is last in the lfggroup so we disband the group.",
player.GetName(), player.GetGUID().ToString());
}
player.RemoveAurasDueToSpell(SharedConst.LFGSpellLuckOfTheDraw);
}
}
}
class LFGGroupScript : GroupScript
{
public LFGGroupScript() : base("LFGGroupScript") { }
// Group Hooks
public override void OnAddMember(Group group, ObjectGuid guid)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
ObjectGuid gguid = group.GetGUID();
ObjectGuid leader = group.GetLeaderGUID();
if (leader == guid)
{
Log.outDebug(LogFilter.Lfg, "LFGScripts.OnAddMember [{0}]: added [{1} leader {2}]", gguid, guid, leader);
Global.LFGMgr.SetLeader(gguid, guid);
}
else
{
LfgState gstate = Global.LFGMgr.GetState(gguid);
LfgState state = Global.LFGMgr.GetState(guid);
Log.outDebug(LogFilter.Lfg, "LFGScripts.OnAddMember [{0}]: added [{1} leader {2}] gstate: {3}, state: {4}", gguid, guid, leader, gstate, state);
if (state == LfgState.Queued)
Global.LFGMgr.LeaveLfg(guid);
if (gstate == LfgState.Queued)
Global.LFGMgr.LeaveLfg(gguid);
}
Global.LFGMgr.SetGroup(guid, gguid);
Global.LFGMgr.AddPlayerToGroup(gguid, guid);
}
public override void OnRemoveMember(Group group, ObjectGuid guid, RemoveMethod method, ObjectGuid kicker, string reason)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
ObjectGuid gguid = group.GetGUID();
Log.outDebug(LogFilter.Lfg, "LFGScripts.OnRemoveMember [{0}]: remove [{1}] Method: {2} Kicker: {3} Reason: {4}", gguid, guid, method, kicker, reason);
bool isLFG = group.isLFGGroup();
if (isLFG && method == RemoveMethod.Kick) // Player have been kicked
{
/// @todo - Update internal kick cooldown of kicker
string str_reason = "";
if (!string.IsNullOrEmpty(reason))
str_reason = reason;
Global.LFGMgr.InitBoot(gguid, kicker, guid, str_reason);
return;
}
LfgState state = Global.LFGMgr.GetState(gguid);
// If group is being formed after proposal success do nothing more
if (state == LfgState.Proposal && method == RemoveMethod.Default)
{
// LfgData: Remove player from group
Global.LFGMgr.SetGroup(guid, ObjectGuid.Empty);
Global.LFGMgr.RemovePlayerFromGroup(gguid, guid);
return;
}
Global.LFGMgr.LeaveLfg(guid);
Global.LFGMgr.SetGroup(guid, ObjectGuid.Empty);
byte players = Global.LFGMgr.RemovePlayerFromGroup(gguid, guid);
Player player = Global.ObjAccessor.FindPlayer(guid);
if (player)
{
if (method == RemoveMethod.Leave && state == LfgState.Dungeon &&
players >= SharedConst.LFGKickVotesNeeded)
player.CastSpell(player, SharedConst.LFGSpellDungeonDeserter, true);
//else if (state == LFG_STATE_BOOT)
// Update internal kick cooldown of kicked
player.GetSession().SendLfgUpdateStatus(new LfgUpdateData(LfgUpdateType.LeaderUnk1), true);
if (isLFG && player.GetMap().IsDungeon()) // Teleport player out the dungeon
Global.LFGMgr.TeleportPlayer(player, true);
}
if (isLFG && state != LfgState.FinishedDungeon) // Need more players to finish the dungeon
{
Player leader = Global.ObjAccessor.FindPlayer(Global.LFGMgr.GetLeader(gguid));
if (leader)
leader.GetSession().SendLfgOfferContinue(Global.LFGMgr.GetDungeon(gguid, false));
}
}
public override void OnDisband(Group group)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
ObjectGuid gguid = group.GetGUID();
Log.outDebug(LogFilter.Lfg, "LFGScripts.OnDisband {0}", gguid);
Global.LFGMgr.RemoveGroupData(gguid);
}
public override void OnChangeLeader(Group group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
ObjectGuid gguid = group.GetGUID();
Log.outDebug(LogFilter.Lfg, "LFGScripts.OnChangeLeader {0}: old {0} new {0}", gguid, newLeaderGuid, oldLeaderGuid);
Global.LFGMgr.SetLeader(gguid, newLeaderGuid);
}
public override void OnInviteMember(Group group, ObjectGuid guid)
{
if (!Global.LFGMgr.isOptionEnabled(LfgOptions.EnableDungeonFinder | LfgOptions.EnableRaidBrowser))
return;
ObjectGuid gguid = group.GetGUID();
ObjectGuid leader = group.GetLeaderGUID();
Log.outDebug(LogFilter.Lfg, "LFGScripts.OnInviteMember {0}: invite {0} leader {0}", gguid, guid, leader);
// No gguid == new group being formed
// No leader == after group creation first invite is new leader
// leader and no gguid == first invite after leader is added to new group (this is the real invite)
if (!leader.IsEmpty() && gguid.IsEmpty())
Global.LFGMgr.LeaveLfg(leader);
}
}
}