diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 21977630..862dba9c 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -285,6 +285,9 @@ + + + diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 0d8b717b..764a1136 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using FFXIVClassic_Map_Server.packets.WorldPackets.Send.Group; namespace FFXIVClassic_Map_Server { @@ -652,6 +653,39 @@ namespace FFXIVClassic_Map_Server //LoadNPCs(zone.actorId); } + + public void RequestWorldLinkshellCreate(Player player, string name, ushort crest) + { + SubPacket packet = CreateLinkshellPacket.BuildPacket(player.playerSession, name, crest, player.actorId); + Server.GetWorldConnection().QueuePacket(packet, true, false); + } + + public void RequestWorldLinkshellCrestModify(Player player, string name, ushort crest) + { + SubPacket packet = ModifyLinkshellPacket.BuildPacket(player.playerSession, 1, name, null, crest, 0); + Server.GetWorldConnection().QueuePacket(packet, true, false); + } + + public void RequestWorldLinkshellDelete(Player player, string name) + { + SubPacket packet = DeleteLinkshellPacket.BuildPacket(player.playerSession, name); + Server.GetWorldConnection().QueuePacket(packet, true, false); + } + + public bool RequestWorldLinkshellRankChange(Player player, string lsname, string memberName, byte newRank) + { + return false; + } + + public bool RequestWorldLinkshellAddMember(Player player, string lsname, string memberName) + { + return false; + } + + public bool RequestWorldLinkshellRemoveMember(Player player, bool wasKicked, string lsname, string memberName) + { + return false; + } private void RequestWorldServerZoneChange(Player player, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) { diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/CreateLinkshellPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/CreateLinkshellPacket.cs new file mode 100644 index 00000000..c58edf78 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/CreateLinkshellPacket.cs @@ -0,0 +1,30 @@ +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.dataobjects; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send.Group +{ + class CreateLinkshellPacket + { + public const ushort OPCODE = 0x1000; + public const uint PACKET_SIZE = 0x48; + + public static SubPacket BuildPacket(Session session, string name, ushort crest, uint master) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name)); + binWriter.Write((UInt16)crest); + binWriter.Write((UInt32)master); + } + } + return new SubPacket(true, OPCODE, 0, session.id, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/DeleteLinkshellPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/DeleteLinkshellPacket.cs new file mode 100644 index 00000000..14c0c6a7 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/DeleteLinkshellPacket.cs @@ -0,0 +1,27 @@ +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.dataobjects; +using System; +using System.IO; +using System.Text; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send.Group +{ + class DeleteLinkshellPacket + { + public const ushort OPCODE = 0x1000; + public const uint PACKET_SIZE = 0x40; + + public static SubPacket BuildPacket(Session session, string name) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name)); + } + } + return new SubPacket(true, OPCODE, 0, session.id, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/ModifyLinkshellPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/ModifyLinkshellPacket.cs new file mode 100644 index 00000000..05cf0adf --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/ModifyLinkshellPacket.cs @@ -0,0 +1,41 @@ +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.dataobjects; +using System; +using System.IO; +using System.Text; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send.Group +{ + class ModifyLinkshellPacket + { + public const ushort OPCODE = 0x1000; + public const uint PACKET_SIZE = 0x60; + + public static SubPacket BuildPacket(Session session, ushort changeArg, string name, string newName, ushort crest, uint master) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name)); + binWriter.Write((UInt16)changeArg); + switch (changeArg) + { + case 0: + binWriter.Write(Encoding.ASCII.GetBytes(newName), 0, Encoding.ASCII.GetByteCount(newName) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(newName)); + break; + case 1: + binWriter.Write((UInt16)crest); + break; + case 2: + binWriter.Write((UInt32)master); + break; + } + + } + } + return new SubPacket(true, OPCODE, 0, session.id, data); + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/Group/Party.cs b/FFXIVClassic World Server/DataObjects/Group/Party.cs index 25a98aac..6bf4fb27 100644 --- a/FFXIVClassic World Server/DataObjects/Group/Party.cs +++ b/FFXIVClassic World Server/DataObjects/Group/Party.cs @@ -16,18 +16,18 @@ namespace FFXIVClassic_World_Server.DataObjects.Group public Party(ulong groupId, uint leaderCharaId) : base(groupId) { - partyGroupWork._globalTemp.owner = (ulong)((0xB36F92 << 8) | leaderCharaId); + partyGroupWork._globalTemp.owner = (ulong)(((ulong)leaderCharaId << 32) | 0xB36F92); members.Add(leaderCharaId); } public void SetLeader(uint actorId) { - partyGroupWork._globalTemp.owner = (ulong)((0xB36F92 << 8) | actorId); + partyGroupWork._globalTemp.owner = (ulong)((actorId << 32) | 0xB36F92); } public uint GetLeader() { - return (uint)(partyGroupWork._globalTemp.owner & 0xFFFFFF); + return (uint)((partyGroupWork._globalTemp.owner >> 32) & 0xFFFFFFFF); } public bool IsInParty(uint charaId) @@ -43,6 +43,7 @@ namespace FFXIVClassic_World_Server.DataObjects.Group SubPacket test = groupWork.buildPacket(session.sessionId, session.sessionId); session.clientConnection.QueuePacket(test, true, false); + test.DebugPrintSubPacket(); } public override int GetMemberCount() @@ -59,7 +60,7 @@ namespace FFXIVClassic_World_Server.DataObjects.Group { List groupMembers = new List(); foreach (uint charaId in members) - groupMembers.Add(new GroupMember(charaId, -1, 0, false, Server.GetServer().GetSession(charaId) != null, Server.GetServer().GetNameForId(charaId))); + groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetServer().GetNameForId(charaId))); return groupMembers; } diff --git a/FFXIVClassic World Server/Database.cs b/FFXIVClassic World Server/Database.cs index a2d14992..b181fdcc 100644 --- a/FFXIVClassic World Server/Database.cs +++ b/FFXIVClassic World Server/Database.cs @@ -158,7 +158,7 @@ namespace FFXIVClassic_World_Server return members; } - public static Linkshell GetLinkshell(ulong groupIndex, ulong id) + public static Linkshell GetLinkshell(ulong groupIndex, ulong lsId) { 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))) { @@ -166,7 +166,7 @@ namespace FFXIVClassic_World_Server { conn.Open(); MySqlCommand cmd = new MySqlCommand("SELECT name, crestIcon, master FROM server_linkshells WHERE id = @lsId", conn); - cmd.Parameters.AddWithValue("@lsId", id); + cmd.Parameters.AddWithValue("@lsId", lsId); using (MySqlDataReader Reader = cmd.ExecuteReader()) { while (Reader.Read()) @@ -175,7 +175,7 @@ namespace FFXIVClassic_World_Server ushort crest = Reader.GetUInt16("crestIcon"); uint master = Reader.GetUInt32("master"); - Linkshell linkshell = new Linkshell(id, groupIndex, name, crest, master, 0xa); + Linkshell linkshell = new Linkshell(lsId, groupIndex, name, crest, master, 0xa); return linkshell; } } diff --git a/FFXIVClassic World Server/FFXIVClassic World Server.csproj b/FFXIVClassic World Server/FFXIVClassic World Server.csproj index 5b2e5c53..c8c34566 100644 --- a/FFXIVClassic World Server/FFXIVClassic World Server.csproj +++ b/FFXIVClassic World Server/FFXIVClassic World Server.csproj @@ -104,11 +104,12 @@ + - + diff --git a/FFXIVClassic World Server/LinkshellManager.cs b/FFXIVClassic World Server/LinkshellManager.cs index f98da1cf..1622c491 100644 --- a/FFXIVClassic World Server/LinkshellManager.cs +++ b/FFXIVClassic World Server/LinkshellManager.cs @@ -13,6 +13,7 @@ namespace FFXIVClassic_World_Server private Object mGroupLockReference; private Dictionary mCurrentWorldGroupsReference; private Dictionary mLinkshellList = new Dictionary(); + private Dictionary mNameToIdLookup = new Dictionary(); public LinkshellManager(WorldManager worldManager, Object groupLock, Dictionary worldGroupList) { @@ -35,6 +36,7 @@ namespace FFXIVClassic_World_Server if (AddMemberToLinkshell(master, newLs.groupIndex)) { mLinkshellList.Add(mWorldManager.GetGroupIndex(), newLs); + mNameToIdLookup.Add(newLs.name, newLs.groupIndex); mCurrentWorldGroupsReference.Add(mWorldManager.GetGroupIndex(), newLs); mWorldManager.IncrementGroupIndex(); } @@ -46,36 +48,52 @@ namespace FFXIVClassic_World_Server //Modifies the LS master public bool ChangeLinkshellMaster(string name, uint newMaster) { - foreach (Linkshell ls in mLinkshellList.Values) + ulong groupInstanceId; + if (mNameToIdLookup.ContainsKey(name)) + groupInstanceId = mNameToIdLookup[name]; + else + return false; + + if (mCurrentWorldGroupsReference.ContainsKey(groupInstanceId)) { - if (ls.name.Equals(name)) - { - return false; - } + Linkshell ls = (Linkshell)mCurrentWorldGroupsReference[groupInstanceId]; + return false; } + return false; } //Modifies the LS crest public bool ChangeLinkshellCrest(string name, ushort newCrestId) { - foreach (Linkshell ls in mLinkshellList.Values) + ulong groupInstanceId; + if (mNameToIdLookup.ContainsKey(name)) + groupInstanceId = mNameToIdLookup[name]; + else + return false; + + if (mCurrentWorldGroupsReference.ContainsKey(groupInstanceId)) { - if (ls.name.Equals(name)) - { - return Database.ChangeLinkshellCrest(ls.dbId, newCrestId); - } + Linkshell ls = (Linkshell)mCurrentWorldGroupsReference[groupInstanceId]; + return Database.ChangeLinkshellCrest(ls.dbId, newCrestId); } + return false; } - //Creates a new linkshell and adds it to the list - public bool DeleteLinkshell(ulong groupInstanceId) + //Deletes a LS + public bool DeleteLinkshell(string name) { - if (mCurrentWorldGroupsReference.ContainsKey(groupInstanceId)) + lock (mGroupLockReference) { - lock (mGroupLockReference) - { + ulong groupInstanceId; + if (mNameToIdLookup.ContainsKey(name)) + groupInstanceId = mNameToIdLookup[name]; + else + return false; + + if (mCurrentWorldGroupsReference.ContainsKey(groupInstanceId)) + { Linkshell ls = (Linkshell)mCurrentWorldGroupsReference[groupInstanceId]; bool result = Database.DeleteLinkshell(ls.dbId); @@ -83,6 +101,7 @@ namespace FFXIVClassic_World_Server { mCurrentWorldGroupsReference.Remove(groupInstanceId); mLinkshellList.Remove(groupInstanceId); + mNameToIdLookup.Remove(name); return true; } } @@ -138,21 +157,31 @@ namespace FFXIVClassic_World_Server } //Get a single linkshell group either already instantiated or make one from the db - public Linkshell GetLinkshell(ulong id) + public Linkshell GetLinkshell(string name) { - if (mLinkshellList.ContainsKey(id)) - return mLinkshellList[id]; + if (mNameToIdLookup.ContainsKey(name)) + return mCurrentWorldGroupsReference[mNameToIdLookup[name]]; + else + return null; + } + + //Get a single linkshell group either already instantiated or make one from the db + public Linkshell GetLinkshell(ulong lsId) + { + if (mLinkshellList.ContainsKey(lsId)) + return mLinkshellList[lsId]; else { lock (mGroupLockReference) { - Linkshell ls = Database.GetLinkshell(mWorldManager.GetGroupIndex(), id); + Linkshell ls = Database.GetLinkshell(mWorldManager.GetGroupIndex(), lsId); ls.LoadMembers(); if (ls != null) { - mLinkshellList.Add(id, ls); - mCurrentWorldGroupsReference.Add(mWorldManager.GetGroupIndex(), ls); + mLinkshellList.Add(ls.groupIndex, ls); + mNameToIdLookup.Add(ls.name, ls.groupIndex); + mCurrentWorldGroupsReference.Add(ls.groupIndex, ls); mWorldManager.IncrementGroupIndex(); return ls; } @@ -170,5 +199,6 @@ namespace FFXIVClassic_World_Server linkshells.Add(GetLinkshell(membership.lsId)); return linkshells; } + } } diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/DeleteGroupPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/DeleteLinkshellPacket.cs similarity index 70% rename from FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/DeleteGroupPacket.cs rename to FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/DeleteLinkshellPacket.cs index 9400b018..7c2a05c2 100644 --- a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/DeleteGroupPacket.cs +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/DeleteLinkshellPacket.cs @@ -1,14 +1,15 @@ using System; using System.IO; +using System.Text; namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group { - class DeleteGroupPacket + class DeleteLinkshellPacket { public bool invalidPacket = false; - public ulong groupId; + public string name; - public DeleteGroupPacket(byte[] data) + public DeleteLinkshellPacket(byte[] data) { using (MemoryStream mem = new MemoryStream(data)) { @@ -16,7 +17,7 @@ namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group { try { - groupId = binReader.ReadUInt64(); + name = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' }); } catch (Exception) { diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/SetActiveLinkshellPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/SetActiveLinkshellPacket.cs new file mode 100644 index 00000000..2f689b2c --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/SetActiveLinkshellPacket.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group +{ + class SetActiveLinkshellPacket + { + public bool invalidPacket = false; + + public string name; + + public SetActiveLinkshellPacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + name = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' }); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + } +} diff --git a/FFXIVClassic World Server/Server.cs b/FFXIVClassic World Server/Server.cs index 37331b0c..a4749535 100644 --- a/FFXIVClassic World Server/Server.cs +++ b/FFXIVClassic World Server/Server.cs @@ -199,32 +199,36 @@ namespace FFXIVClassic_World_Server GetWorldManager().DoZoneServerChange(session, zoneChangePacket.destinationZoneId, "", zoneChangePacket.destinationSpawnType, zoneChangePacket.destinationX, zoneChangePacket.destinationY, zoneChangePacket.destinationZ, zoneChangePacket.destinationRot); } - break; - //Group get data request - case 0x1020: - GetGroupPacket getGroupPacket = new GetGroupPacket(subpacket.data); - mWorldManager.SendGroupData(session, getGroupPacket.groupId); - break; - //Group delete request - case 0x1021: - FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group.DeleteGroupPacket deleteGroupPacket = new FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group.DeleteGroupPacket(subpacket.data); - mWorldManager.DeleteGroup(deleteGroupPacket.groupId); - break; + break; //Linkshell create request - case 0x1023: - CreateLinkshellPacket createLinkshellpacket = new CreateLinkshellPacket(subpacket.data); - mWorldManager.GetLinkshellManager().CreateLinkshell(createLinkshellpacket.name, createLinkshellpacket.crestid, createLinkshellpacket.master); + case 0x1025: + CreateLinkshellPacket createLinkshellPacket = new CreateLinkshellPacket(subpacket.data); + mWorldManager.GetLinkshellManager().CreateLinkshell(createLinkshellPacket.name, createLinkshellPacket.crestid, createLinkshellPacket.master); break; //Linkshell modify request - case 0x1024: - ModifyLinkshellPacket modifyLinkshellpacket = new ModifyLinkshellPacket(subpacket.data); - - if (modifyLinkshellpacket.argCode == 0) - mWorldManager.GetLinkshellManager().ChangeLinkshellCrest(modifyLinkshellpacket.currentName, modifyLinkshellpacket.crestid); + case 0x1026: + ModifyLinkshellPacket modifyLinkshellPacket = new ModifyLinkshellPacket(subpacket.data); + switch (modifyLinkshellPacket.argCode) + { + case 0: + break; + case 1: + mWorldManager.GetLinkshellManager().ChangeLinkshellCrest(modifyLinkshellPacket.currentName, modifyLinkshellPacket.crestid); + break; + case 2: + mWorldManager.GetLinkshellManager().ChangeLinkshellMaster(modifyLinkshellPacket.currentName, modifyLinkshellPacket.master); + break; + } break; - //Group Add/Remove Member - case 0x1022: - GroupMemberChangePacket gMemberChangePacket = new GroupMemberChangePacket(subpacket.data); + //Linkshell delete request + case 0x1027: + DeleteLinkshellPacket deleteLinkshellPacket = new DeleteLinkshellPacket(subpacket.data); + mWorldManager.GetLinkshellManager().DeleteLinkshell(deleteLinkshellPacket.name); + break; + //Linkshell set active + case 0x1028: + SetActiveLinkshellPacket setActiveLinkshellPacket = new SetActiveLinkshellPacket(subpacket.data); + Linkshell ls = mWorldManager.GetLinkshellManager().GetLinkshell(); break; } } diff --git a/FFXIVClassic World Server/WorldMaster.cs b/FFXIVClassic World Server/WorldMaster.cs index d1875e04..22d8b9ee 100644 --- a/FFXIVClassic World Server/WorldMaster.cs +++ b/FFXIVClassic World Server/WorldMaster.cs @@ -210,13 +210,21 @@ namespace FFXIVClassic_World_Server SendMotD(session); //Send party, retainer, ls groups - mPartyManager.GetParty(session.sessionId).SendGroupPackets(session); + Party pt = mPartyManager.GetParty(session.sessionId); + mPartyManager.AddToParty(pt.groupIndex, 156); + mPartyManager.AddToParty(pt.groupIndex, 157); + mPartyManager.AddToParty(pt.groupIndex, 158); + mPartyManager.AddToParty(pt.groupIndex, 159); + mPartyManager.AddToParty(pt.groupIndex, 160); + mPartyManager.AddToParty(pt.groupIndex, 161); + mPartyManager.AddToParty(pt.groupIndex, 162); + pt.SendGroupPackets(session); mRetainerGroupManager.GetRetainerGroup(session.sessionId).SendGroupPackets(session); List linkshells = mLinkshellManager.GetPlayerLinkshellMembership(session.sessionId); foreach (Linkshell ls in linkshells) ls.SendGroupPackets(session); - mRelationGroupManager.CreateRelationGroup(157, session.sessionId, 0x2711).SendGroupPackets(session); + mRelationGroupManager.CreateRelationGroup(157, session.sessionId, 40001).SendGroupPackets(session); } private void SendMotD(Session session) @@ -266,20 +274,7 @@ namespace FFXIVClassic_World_Server foreach (GroupMember member in group.BuildMemberList()) group.SendGroupPackets(mServer.GetSession(member.actorId)); } - } - - public void DeleteGroup(ulong id) - { - if (!mCurrentWorldGroups.ContainsKey(id)) - return; - Group group = mCurrentWorldGroups[id]; - if (group is Party) - mPartyManager.DeleteParty(group.groupIndex); - else if (group is Linkshell) - mLinkshellManager.DeleteLinkshell(group.groupIndex); - else if (group is Relation) - mRelationGroupManager.DeleteRelationGroup(group.groupIndex); - } + } public void IncrementGroupIndex() {