diff --git a/FFXIVClassic World Server/DataObjects/Group/Group.cs b/FFXIVClassic World Server/DataObjects/Group/Group.cs new file mode 100644 index 00000000..25a09d59 --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/Group.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class Group + { + public readonly ulong groupIndex; + + public Group(ulong groupIndex) + { + this.groupIndex = groupIndex; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/Linkshell.cs b/FFXIVClassic World Server/DataObjects/Group/Linkshell.cs new file mode 100644 index 00000000..735eae36 --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/Linkshell.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class Linkshell : Group + { + public ulong dbId; + public string name; + public ushort crestId; + public uint master; + public ushort rank; + + public Dictionary members = new Dictionary(); + + public Linkshell(ulong dbId, ulong groupIndex, string name, ushort crestId, uint master, ushort rank) : base(groupIndex) + { + this.dbId = dbId; + this.name = name; + this.crestId = crestId; + this.master = master; + this.rank = rank; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/LinkshellMember.cs b/FFXIVClassic World Server/DataObjects/Group/LinkshellMember.cs new file mode 100644 index 00000000..ea664bdf --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/LinkshellMember.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class LinkshellMember + { + public readonly uint charaId; + public readonly ulong lsId; + public readonly ushort slot; + public readonly ushort rank; + + public LinkshellMember(uint charaId, ulong lsId, ushort slot, ushort rank) + { + this.charaId = charaId; + this.lsId = lsId; + this.slot = slot; + this.rank = rank; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/Party.cs b/FFXIVClassic World Server/DataObjects/Group/Party.cs new file mode 100644 index 00000000..16caf573 --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/Party.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class Party : Group + { + public uint leader; + public List members = new List(); + + public Party(ulong groupId, uint leaderCharaId) : base(groupId) + { + this.leader = leaderCharaId; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/Relation.cs b/FFXIVClassic World Server/DataObjects/Group/Relation.cs new file mode 100644 index 00000000..ad74777b --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/Relation.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class Relation : Group + { + public uint charaHost, charaOther; + public uint command; + + public Relation(ulong groupIndex, uint host, uint other, uint command) : base (groupIndex) + { + this.charaHost = host; + this.charaOther = other; + this.command = command; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/RetainerGroup.cs b/FFXIVClassic World Server/DataObjects/Group/RetainerGroup.cs new file mode 100644 index 00000000..77e935c8 --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/RetainerGroup.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class RetainerGroup : Group + { + public uint owner; + public Dictionary members = new Dictionary(); + + public RetainerGroup(ulong groupId, uint owner) : base(groupId) + { + this.owner = owner; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/RetainerGroupMember.cs b/FFXIVClassic World Server/DataObjects/Group/RetainerGroupMember.cs new file mode 100644 index 00000000..308f263b --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/Group/RetainerGroupMember.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.DataObjects.Group +{ + class RetainerGroupMember + { + } +} diff --git a/FFXIVClassic World Server/Database.cs b/FFXIVClassic World Server/Database.cs index e3a6412a..dc835a18 100644 --- a/FFXIVClassic World Server/Database.cs +++ b/FFXIVClassic World Server/Database.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using FFXIVClassic_World_Server.DataObjects; +using FFXIVClassic_World_Server.DataObjects.Group; namespace FFXIVClassic_World_Server { @@ -49,5 +51,137 @@ namespace FFXIVClassic_World_Server return 0; } } + + public static Dictionary GetRetainers(uint charaId) + { + throw new NotImplementedException(); + } + + public static Linkshell GetLinkshell(ulong groupIndex, ulong id) + { + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + MySqlCommand cmd = new MySqlCommand("SELECT name, crestIcon, master FROM server_linkshells WHERE id = @lsId", conn); + cmd.Parameters.AddWithValue("@lsId", id); + using (MySqlDataReader Reader = cmd.ExecuteReader()) + { + while (Reader.Read()) + { + string name = Reader.GetString("name"); + ushort crest = Reader.GetUInt16("crestIcon"); + uint master = Reader.GetUInt32("master"); + + Linkshell linkshell = new Linkshell(id, groupIndex, name, crest, master, 0xa); + return linkshell; + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + return null; + } + + public static Dictionary GetLSMembers(ulong lsId) + { + Dictionary memberList = new Dictionary(); + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + MySqlCommand cmd = new MySqlCommand("SELECT characterId, linkshellId, slot, rank FROM characters_linkshells WHERE linkshellId = @lsId", conn); + cmd.Parameters.AddWithValue("@lsId", lsId); + using (MySqlDataReader Reader = cmd.ExecuteReader()) + { + while (Reader.Read()) + { + uint characterId = Reader.GetUInt32("characterId"); + ulong linkshellId = Reader.GetUInt64("linkshellId"); + ushort slot = Reader.GetUInt16("slot"); + ushort rank = Reader.GetUInt16("rank"); + + LinkshellMember member = new LinkshellMember(characterId, linkshellId, slot, rank); + memberList.Add(characterId, member); + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + return memberList; + } + + public static List GetPlayerLSMembership(uint charaId) + { + List memberList = new List(); + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + MySqlCommand cmd = new MySqlCommand("SELECT characterId, linkshellId, slot, rank FROM characters_linkshells WHERE characterid = @charaId", conn); + cmd.Parameters.AddWithValue("@lsId", charaId); + using (MySqlDataReader Reader = cmd.ExecuteReader()) + { + while (Reader.Read()) + { + uint characterId = Reader.GetUInt32("characterId"); + ulong linkshellId = Reader.GetUInt64("linkshellId"); + ushort slot = Reader.GetUInt16("slot"); + ushort rank = Reader.GetUInt16("rank"); + + LinkshellMember member = new LinkshellMember(characterId, linkshellId, slot, rank); + memberList.Add(member); + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + return memberList; + } + + public static ulong CreateLinkshell(string name, ushort crest, uint master) + { + throw new NotImplementedException(); + } + + public static bool DeleteLinkshell(ulong lsId) + { + throw new NotImplementedException(); + } + + public static bool LinkshellAddPlayer(ulong dbId, uint charaId) + { + throw new NotImplementedException(); + } + + public static bool LinkshellRemovePlayer(ulong lsId, uint charaId) + { + throw new NotImplementedException(); + } + } } diff --git a/FFXIVClassic World Server/LinkshellManager.cs b/FFXIVClassic World Server/LinkshellManager.cs new file mode 100644 index 00000000..84e263ab --- /dev/null +++ b/FFXIVClassic World Server/LinkshellManager.cs @@ -0,0 +1,156 @@ +using FFXIVClassic_World_Server.DataObjects.Group; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server +{ + class LinkshellManager + { + private Server mServer; + private Object mGroupLockReference; + private Dictionary mCurrentWorldGroupsReference; + private Dictionary mLinkshellList = new Dictionary(); + + public LinkshellManager(Server server, Object groupLock, Dictionary worldGroupList) + { + mServer = server; + mGroupLockReference = groupLock; + mCurrentWorldGroupsReference = worldGroupList; + } + + //Creates a new linkshell and adds it to the list + public ulong CreateLinkshell(string name, ushort crest, uint master) + { + lock (mGroupLockReference) + { + ulong resultId = Database.CreateLinkshell(name, crest, master); + if (resultId >= 0) + { + Linkshell newLs = new Linkshell(resultId, mServer.GetGroupIndex(), name, crest, master, 0xa); + + //Add founder to the LS + if (AddMemberToLinkshell(master, newLs.groupIndex)) + { + mLinkshellList.Add(mServer.GetGroupIndex(), newLs); + mCurrentWorldGroupsReference.Add(mServer.GetGroupIndex(), newLs); + mServer.IncrementGroupIndex(); + } + } + return resultId; + } + } + + //Modifies the LS + public bool ModifyLinkshell() + { + return false; + } + + //Creates a new linkshell and adds it to the list + public bool DeleteLinkshell(uint groupInstanceId) + { + if (mCurrentWorldGroupsReference.ContainsKey(groupInstanceId)) + { + lock (mGroupLockReference) + { + Linkshell ls = (Linkshell)mCurrentWorldGroupsReference[groupInstanceId]; + bool result = Database.DeleteLinkshell(ls.dbId); + + if (result) + { + mCurrentWorldGroupsReference.Remove(groupInstanceId); + mLinkshellList.Remove(groupInstanceId); + return true; + } + } + } + + return false; + } + + //Adds a player to the linkshell + public bool AddMemberToLinkshell(uint charaId, ulong groupId) + { + //Get the LS + Linkshell ls = GetLinkshell(groupId); + if (ls == null) + return false; + + //Add player to ls in db + lock (mGroupLockReference) + { + bool result = Database.LinkshellAddPlayer(ls.dbId, charaId); + + if (result) + { + LinkshellMember newMember = new LinkshellMember(charaId, ls.dbId, 0, 0); + ls.members.Add(charaId, newMember); + return true; + } + else + return false; + } + } + + //Removes a player from the linkshell + public bool RemoveMemberFromLinkshell(uint charaId, ulong groupId) + { + //Get the LS + Linkshell ls = GetLinkshell(groupId); + if (ls == null) + return false; + + //Delete the player in the db + lock (mGroupLockReference) + { + bool result = Database.LinkshellRemovePlayer(ls.dbId, charaId); + + if (!result) + return false; + + //Remove from group instance + if (ls.members.ContainsKey(charaId)) + ls.members.Remove(charaId); + + return true; + } + } + + //Get a single linkshell group either already instantiated or make one from the db + public Linkshell GetLinkshell(ulong id) + { + if (mLinkshellList.ContainsKey(id)) + return mLinkshellList[id]; + else + { + lock (mGroupLockReference) + { + Linkshell ls = Database.GetLinkshell(mServer.GetGroupIndex(), id); + + if (ls != null) + { + ls.members = Database.GetLSMembers(id); + mLinkshellList.Add(id, ls); + mCurrentWorldGroupsReference.Add(mServer.GetGroupIndex(), ls); + mServer.IncrementGroupIndex(); + return ls; + } + } + } + return null; + } + + //Get the linkshells player is part of + public List GetPlayerLinkshellMembership(uint charaId) + { + List memberships = Database.GetPlayerLSMembership(charaId); + List linkshells = new List(); + foreach (LinkshellMember membership in memberships) + linkshells.Add(GetLinkshell(membership.lsId)); + return linkshells; + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupControlCreateModifyPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupControlCreateModifyPacket.cs new file mode 100644 index 00000000..502fef6f --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupControlCreateModifyPacket.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group +{ + class GroupControlCreateModifyPacket + { + public const byte GROUP_CONTROL_CREATE = 0; + public const byte GROUP_CONTROL_MODIFY = 1; + + public const byte GROUP_PARTY = 0; + public const byte GROUP_RETAINER = 1; + public const byte GROUP_LINKSHELL = 2; + public const byte GROUP_RELATION = 3; + + public bool invalidPacket = false; + public byte controlCode; + public ulong groupId; + public byte groupType; + + public GroupControlCreateModifyPacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + controlCode = binReader.ReadByte(); + groupType = binReader.ReadByte(); + groupId = binReader.ReadUInt64(); + + //Work value data + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupControlGetDeletePacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupControlGetDeletePacket.cs new file mode 100644 index 00000000..9090d39d --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupControlGetDeletePacket.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group +{ + class GroupControlGetDeletePacket + { + public const byte GROUP_CONTROL_GET = 0; + public const byte GROUP_CONTROL_DELETE = 1; + + public bool invalidPacket = false; + public uint controlCode; + public ulong groupId; + + public GroupControlGetDeletePacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + controlCode = binReader.ReadUInt32(); + groupId = binReader.ReadUInt64(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupMemberChangePacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupMemberChangePacket.cs new file mode 100644 index 00000000..a207e8d3 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupMemberChangePacket.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group +{ + class GroupMemberChangePacket + { + public const int GROUP_MEMBER_ADD = 0; + public const int GROUP_MEMBER_REMOVE = 0; + + public bool invalidPacket = false; + public uint controlCode; + public ulong groupId; + public uint memberId; + + public GroupMemberChangePacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + controlCode = binReader.ReadUInt32(); + groupId = binReader.ReadUInt64(); + memberId = binReader.ReadUInt32(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs new file mode 100644 index 00000000..c949b6e1 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs @@ -0,0 +1,28 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Send.Group +{ + class GroupMemberListEndPacket + { + public const ushort OPCODE = 0x1022; + public const uint PACKET_SIZE = 0x28; + + public static SubPacket BuildPacket(Session session, ulong groupId) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt64)groupId); + } + } + + return new SubPacket(true, OPCODE, 0, session.sessionId, data); + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListPacket.cs new file mode 100644 index 00000000..7213f2d7 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListPacket.cs @@ -0,0 +1,31 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Send.Group +{ + class GroupMemberListPacket + { + public const ushort OPCODE = 0x1021; + public const uint PACKET_SIZE = 0x2C; + + public static SubPacket BuildPacket(Session session, ulong groupId, uint numMembersInPacket) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt64)groupId); + binWriter.Write((UInt32)numMembersInPacket); + + //Members + } + } + + return new SubPacket(true, OPCODE, 0, session.sessionId, data); + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListStartPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListStartPacket.cs new file mode 100644 index 00000000..06104f59 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListStartPacket.cs @@ -0,0 +1,30 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Send.Group +{ + class GroupMemberListStartPacket + { + public const ushort OPCODE = 0x1020; + public const uint PACKET_SIZE = 0x30; + + public static SubPacket BuildPacket(Session session, int resultCode, ulong groupId, int numMembers) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32) resultCode); + binWriter.Write((UInt64) groupId); + binWriter.Write((UInt32) numMembers); + } + } + + return new SubPacket(true, OPCODE, 0, session.sessionId, data); + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupWorkValuesPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupWorkValuesPacket.cs new file mode 100644 index 00000000..3757a9a1 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupWorkValuesPacket.cs @@ -0,0 +1,29 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.IO; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Send.Group +{ + class GroupWorkValuesPacket + { + public const ushort OPCODE = 0x1023; + public const uint PACKET_SIZE = 0x80; + + public static SubPacket BuildPacket(Session session, ulong groupId) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt64)groupId); + //Write data + } + } + + return new SubPacket(true, OPCODE, 0, session.sessionId, data); + } + } +} diff --git a/FFXIVClassic World Server/PartyManager.cs b/FFXIVClassic World Server/PartyManager.cs new file mode 100644 index 00000000..8d6a582a --- /dev/null +++ b/FFXIVClassic World Server/PartyManager.cs @@ -0,0 +1,85 @@ +using FFXIVClassic_World_Server.DataObjects.Group; +using System; +using System.Collections.Generic; + +namespace FFXIVClassic_World_Server +{ + class PartyManager + { + private Server mServer; + private Object mGroupLockReference; + private Dictionary mCurrentWorldGroupsReference; + private Dictionary mPartyList = new Dictionary(); + + public PartyManager(Server server, Object groupLock, Dictionary worldGroupList) + { + mServer = server; + mGroupLockReference = groupLock; + mCurrentWorldGroupsReference = worldGroupList; + } + + public void CreateParty(uint leaderCharaId) + { + lock (mGroupLockReference) + { + ulong groupId = mServer.GetGroupIndex(); + Party party = new Party(groupId, leaderCharaId); + mPartyList.Add(groupId, party); + mCurrentWorldGroupsReference.Add(groupId, party); + mServer.IncrementGroupIndex(); + } + } + + public void DeleteParty(ulong groupId) + { + if (mCurrentWorldGroupsReference.ContainsKey(groupId)) + mCurrentWorldGroupsReference.Remove(groupId); + if (mPartyList.ContainsKey(groupId)) + mPartyList.Remove(groupId); + } + + public bool AddToParty(ulong groupId, uint charaId) + { + if (mPartyList.ContainsKey(groupId)) + { + Party party = mPartyList[groupId]; + if (!party.members.Contains(charaId)) + party.members.Add(charaId); + return true; + } + return false; + } + + public int RemoveFromParty(ulong groupId, uint charaId) + { + if (mPartyList.ContainsKey(groupId)) + { + Party party = mPartyList[groupId]; + if (party.members.Contains(charaId)) + { + party.members.Remove(charaId); + + //If current ldr, make a new ldr if not empty pt + if (party.leader == charaId && party.members.Count != 0) + party.leader = party.members[0]; + } + return party.members.Count; + } + return -1; + } + + public bool ChangeLeader(ulong groupId, uint charaId) + { + if (mPartyList.ContainsKey(groupId)) + { + Party party = mPartyList[groupId]; + if (party.members.Contains(charaId)) + { + party.leader = charaId; + return true; + } + } + return false; + } + } +} diff --git a/FFXIVClassic World Server/RelationGroupManager.cs b/FFXIVClassic World Server/RelationGroupManager.cs new file mode 100644 index 00000000..27d87749 --- /dev/null +++ b/FFXIVClassic World Server/RelationGroupManager.cs @@ -0,0 +1,41 @@ +using FFXIVClassic_World_Server.DataObjects.Group; +using System; +using System.Collections.Generic; + +namespace FFXIVClassic_World_Server +{ + class RelationGroupManager + { + private Server mServer; + private Object mGroupLockReference; + private Dictionary mCurrentWorldGroupsReference; + private Dictionary mRelationList = new Dictionary(); + + public RelationGroupManager(Server server, Object groupLock, Dictionary worldGroupList) + { + mServer = server; + mGroupLockReference = groupLock; + mCurrentWorldGroupsReference = worldGroupList; + } + + public void CreateRelationGroup(uint hostCharaId, uint otherCharaId, uint command) + { + lock (mGroupLockReference) + { + ulong groupIndex = mServer.GetGroupIndex(); + Relation relation = new Relation(groupIndex, hostCharaId, otherCharaId, command); + mRelationList.Add(groupIndex, relation); + mCurrentWorldGroupsReference.Add(groupIndex, relation); + mServer.IncrementGroupIndex(); + } + } + + public void DeleteRelationGroup(ulong groupId) + { + if (mRelationList.ContainsKey(groupId)) + mRelationList.Remove(groupId); + if (mCurrentWorldGroupsReference.ContainsKey(groupId)) + mCurrentWorldGroupsReference.Remove(groupId); + } + } +} diff --git a/FFXIVClassic World Server/RetainerGroupManager.cs b/FFXIVClassic World Server/RetainerGroupManager.cs new file mode 100644 index 00000000..fdcdf6f4 --- /dev/null +++ b/FFXIVClassic World Server/RetainerGroupManager.cs @@ -0,0 +1,63 @@ +using FFXIVClassic_World_Server.DataObjects.Group; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server +{ + class RetainerGroupManager + { + private Server mServer; + private Object mGroupLockReference; + private Dictionary mCurrentWorldGroupsReference; + private Dictionary mRetainerGroupList = new Dictionary(); + + public RetainerGroupManager(Server server, Object groupLock, Dictionary worldGroupList) + { + mServer = server; + mGroupLockReference = groupLock; + mCurrentWorldGroupsReference = worldGroupList; + } + + public RetainerGroup GetRetainerGroup(uint charaId) + { + if (!mRetainerGroupList.ContainsKey(charaId)) + return LoadRetainerGroup(charaId); + else + return mRetainerGroupList[charaId]; + } + + private RetainerGroup LoadRetainerGroup(uint charaId) + { + lock(mGroupLockReference) + { + ulong groupId = mServer.GetGroupIndex(); + RetainerGroup retainerGroup = new RetainerGroup(groupId, charaId); + + Dictionary members = Database.GetRetainers(charaId); + if (members == null) + return null; + + retainerGroup.members = members; + mRetainerGroupList.Add(charaId, retainerGroup); + mCurrentWorldGroupsReference.Add(groupId, retainerGroup); + + mServer.IncrementGroupIndex(); + + return retainerGroup; + } + } + + public void AddRetainerToGroup(ulong charaId, uint retainerId) + { + + } + + public void RemoveRetainerFromGroup(ulong charaId, uint retainerId) + { + + } + } +} diff --git a/FFXIVClassic World Server/Server.cs b/FFXIVClassic World Server/Server.cs index c010faef..883403f7 100644 --- a/FFXIVClassic World Server/Server.cs +++ b/FFXIVClassic World Server/Server.cs @@ -1,6 +1,8 @@ using FFXIVClassic.Common; using FFXIVClassic_World_Server.DataObjects; +using FFXIVClassic_World_Server.DataObjects.Group; using FFXIVClassic_World_Server.Packets.WorldPackets.Receive; +using FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group; using System; using System.Collections.Generic; using System.Net; @@ -13,22 +15,36 @@ namespace FFXIVClassic_World_Server public const int FFXIV_MAP_PORT = 54992; public const int BUFFER_SIZE = 0xFFFF; //Max basepacket size is 0xFFFF public const int BACKLOG = 100; - public const int HEALTH_THREAD_SLEEP_TIME = 5; - private static Server mSelf; + //Connection and Session Management private Socket mServerSocket; WorldManager mWorldManager; PacketProcessor mPacketProcessor; private List mConnectionList = new List(); + private Dictionary mZoneSessionList = new Dictionary(); private Dictionary mChatSessionList = new Dictionary(); + //World Scope Group Management + private Object mGroupLock = new object(); + private ulong mRunningGroupIndex = 1; + private Dictionary mCurrentWorldGroups = new Dictionary(); + + private PartyManager mPartyManager; + private RetainerGroupManager mRetainerGroupManager; + private LinkshellManager mLinkshellManager; + private RelationGroupManager mRelationGroupManager; + public Server() { mSelf = this; + mPartyManager = new PartyManager(this, mGroupLock, mCurrentWorldGroups); + mLinkshellManager = new LinkshellManager(this, mGroupLock, mCurrentWorldGroups); + mRetainerGroupManager = new RetainerGroupManager(this, mGroupLock, mCurrentWorldGroups); + mRelationGroupManager = new RelationGroupManager(this, mGroupLock, mCurrentWorldGroups); } public static Server GetServer() @@ -135,7 +151,7 @@ namespace FFXIVClassic_World_Server return null; } - + public void OnReceiveSubPacketFromZone(ZoneServer zoneServer, SubPacket subpacket) { uint sessionId = subpacket.header.targetId; @@ -187,6 +203,65 @@ namespace FFXIVClassic_World_Server GetWorldManager().DoZoneServerChange(session, zoneChangePacket.destinationZoneId, "", zoneChangePacket.destinationSpawnType, zoneChangePacket.destinationX, zoneChangePacket.destinationY, zoneChangePacket.destinationZ, zoneChangePacket.destinationRot); } + break; + //Group Control Create or Modify + case 0x1020: + GroupControlCreateModifyPacket gCreateModifyPacket = new GroupControlCreateModifyPacket(subpacket.data); + + if (gCreateModifyPacket.controlCode == GroupControlCreateModifyPacket.GROUP_CONTROL_CREATE) + { + ulong groupId; + switch (gCreateModifyPacket.groupType) + { + case GroupControlCreateModifyPacket.GROUP_PARTY: + //mPartyManager.CreateParty(); + break; + case GroupControlCreateModifyPacket.GROUP_RETAINER: + //mPartyManager.CreateParty(); + break; + case GroupControlCreateModifyPacket.GROUP_LINKSHELL: + //mPartyManager.CreateParty(); + break; + case GroupControlCreateModifyPacket.GROUP_RELATION: + //mPartyManager.CreateParty(); + break; + } + } + else if (gCreateModifyPacket.controlCode == GroupControlCreateModifyPacket.GROUP_CONTROL_MODIFY) + { + switch (gCreateModifyPacket.groupType) + { + case GroupControlCreateModifyPacket.GROUP_PARTY: + //mPartyManager.CreateParty(); + break; + case GroupControlCreateModifyPacket.GROUP_RETAINER: + //mPartyManager.CreateParty(); + break; + case GroupControlCreateModifyPacket.GROUP_LINKSHELL: + //mPartyManager.CreateParty(); + break; + case GroupControlCreateModifyPacket.GROUP_RELATION: + //mPartyManager.CreateParty(); + break; + } + } + + break; + //Group Control Get or Delete + case 0x1021: + GroupControlGetDeletePacket gGetDeletePacket = new GroupControlGetDeletePacket(subpacket.data); + if (gGetDeletePacket.controlCode == GroupControlGetDeletePacket.GROUP_CONTROL_GET) + { + + } + else if (gGetDeletePacket.controlCode == GroupControlGetDeletePacket.GROUP_CONTROL_DELETE) + { + + } + break; + //Group Add/Remove Member + case 0x1022: + GroupMemberChangePacket gMemberChangePacket = new GroupMemberChangePacket(subpacket.data); break; } } @@ -333,9 +408,19 @@ namespace FFXIVClassic_World_Server } } } - } + } #endregion + public void IncrementGroupIndex() + { + mRunningGroupIndex++; + } + + public ulong GetGroupIndex() + { + return mRunningGroupIndex; + } + } }