From 16c9b741bfcfb2ddf43c5fbe54e89fbca7d35112 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Wed, 21 Dec 2016 18:02:50 -0500 Subject: [PATCH] Kicked/Promote leader added but broke login. D/Cing now. --- .../FFXIVClassic Map Server.csproj | 9 +- FFXIVClassic Map Server/PacketProcessor.cs | 2 +- FFXIVClassic Map Server/WorldManager.cs | 58 +-- .../actors/chara/player/Player.cs | 11 +- FFXIVClassic Map Server/actors/group/Party.cs | 6 +- .../WorldPackets/Receive/PartySyncPacket.cs | 5 +- .../Send/Group/PartyModifyPacket.cs | 30 ++ .../packets/send/groups/CreateNamedGroup.cs | 8 +- .../send/groups/CreateNamedGroupMultiple.cs | 8 +- .../packets/send/groups/DeleteGroupPacket.cs | 2 +- .../packets/send/groups/GroupHeaderPacket.cs | 12 +- .../send/groups/GroupMembersBeginPacket.cs | 4 +- .../send/groups/GroupMembersEndPacket.cs | 2 +- .../DataObjects/Group/Group.cs | 11 + .../DataObjects/Group/Party.cs | 99 ++++- .../DataObjects/LuaParam.cs | 16 + .../DataObjects/LuaUtils.cs | 380 ++++++++++++++++++ .../DataObjects/Session.cs | 29 +- FFXIVClassic World Server/Database.cs | 35 ++ .../FFXIVClassic World Server.csproj | 10 +- FFXIVClassic World Server/LinkshellManager.cs | 31 +- .../Send/Subpackets/GameMessagePacket.cs | 348 ++++++++++++++++ .../Receive/Group/PartyLeavePacket.cs | 32 ++ ...erChangePacket.cs => PartyModifyPacket.cs} | 23 +- .../Send/Group/GroupMemberListEndPacket.cs | 28 -- .../Send/Group/GroupMemberListPacket.cs | 31 -- .../Send/Group/GroupMemberListStartPacket.cs | 30 -- .../Send/Group/GroupWorkValuesPacket.cs | 29 -- FFXIVClassic World Server/Server.cs | 20 +- FFXIVClassic World Server/WorldMaster.cs | 22 +- 30 files changed, 1120 insertions(+), 211 deletions(-) create mode 100644 FFXIVClassic Map Server/packets/WorldPackets/Send/Group/PartyModifyPacket.cs create mode 100644 FFXIVClassic World Server/DataObjects/LuaParam.cs create mode 100644 FFXIVClassic World Server/DataObjects/LuaUtils.cs create mode 100644 FFXIVClassic World Server/Packets/Send/Subpackets/GameMessagePacket.cs create mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyLeavePacket.cs rename FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/{GroupMemberChangePacket.cs => PartyModifyPacket.cs} (56%) delete mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs delete mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListPacket.cs delete mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListStartPacket.cs delete mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupWorkValuesPacket.cs diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 74bfe102..2f7e64b5 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -81,10 +81,9 @@ - - - - + + + @@ -94,7 +93,6 @@ - @@ -288,6 +286,7 @@ + diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index d6eab493..6f6bc7fe 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -248,7 +248,7 @@ namespace FFXIVClassic_Map_Server break; //Party Window Opened, Request State case 0x01C5: - client.QueuePacket(BasePacket.CreatePacket(RecruiterStatePacket.BuildPacket(session.id, true, true, 1), true, false)); + client.QueuePacket(BasePacket.CreatePacket(RecruiterStatePacket.BuildPacket(session.id, false, false, 0), true, false)); break; //Search Recruiting case 0x01C7: diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 5e1f9f2f..906fd6f2 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -33,6 +33,8 @@ namespace FFXIVClassic_Map_Server private Dictionary actorClasses = new Dictionary(); private Dictionary currentPlayerParties = new Dictionary(); //GroupId, Party object + private Object groupLock = new Object(); + private Server mServer; public WorldManager(Server server) @@ -700,37 +702,41 @@ namespace FFXIVClassic_Map_Server //World server sent a party member list synch packet to the zone server. Add and update players that may be a part of it. public void PartyMemberListRecieved(PartySyncPacket syncPacket) { - Party group; - - //If no members on this server, get out or clean - if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId) && syncPacket.memberActorIds.Length == 0) - return; - else if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId) && syncPacket.memberActorIds.Length == 0) - NoMembersInParty(currentPlayerParties[syncPacket.partyGroupId]); - - //Get or create group - if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId)) + lock (currentPlayerParties) { - group = new Party(syncPacket.partyGroupId, syncPacket.owner); - currentPlayerParties.Add(syncPacket.partyGroupId, group); - } - else - group = currentPlayerParties[syncPacket.partyGroupId]; + Party group; - currentPlayerParties[syncPacket.partyGroupId].members = syncPacket.memberActorIds.ToList(); + //If no members on this server, get out or clean + if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId) && syncPacket.memberActorIds.Length == 0) + return; + else if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId) && syncPacket.memberActorIds.Length == 0) + NoMembersInParty(currentPlayerParties[syncPacket.partyGroupId]); - //Add group to everyone - foreach (uint member in currentPlayerParties[syncPacket.partyGroupId].members) - { - Session session = Server.GetServer().GetSession(member); + //Get or create group + if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId)) + { + group = new Party(syncPacket.partyGroupId, syncPacket.owner); + currentPlayerParties.Add(syncPacket.partyGroupId, group); + } + else + group = currentPlayerParties[syncPacket.partyGroupId]; - if (session == null) - continue; + group.members = syncPacket.memberActorIds.ToList(); - Player player = session.GetActor(); - if (player == null) - continue; - player.SetParty(group); + //Add group to everyone + for (int i = 0; i < group.members.Count; i++ ) + { + uint member = group.members[i]; + Session session = Server.GetServer().GetSession(member); + + if (session == null) + continue; + + Player player = session.GetActor(); + if (player == null) + continue; + player.SetParty(group); + } } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 84afd6d5..45ec4b5a 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -17,6 +17,7 @@ using FFXIVClassic_Map_Server.packets.receive.events; using FFXIVClassic_Map_Server.packets.send.actor.inventory; using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.packets.send.group; +using FFXIVClassic_Map_Server.packets.WorldPackets.Send.Group; namespace FFXIVClassic_Map_Server.Actors { @@ -1290,8 +1291,10 @@ namespace FFXIVClassic_Map_Server.Actors return false; } - public void PartyOustPlayer() - { + public void PartyOustPlayer(string name) + { + SubPacket oustPacket = PartyModifyPacket.BuildPacket(playerSession, 1, name); + QueuePacket(oustPacket); } public void PartyLeave() @@ -1302,8 +1305,10 @@ namespace FFXIVClassic_Map_Server.Actors { } - public void PartyPromote() + public void PartyPromote(string name) { + SubPacket promotePacket = PartyModifyPacket.BuildPacket(playerSession, 0, name); + QueuePacket(promotePacket); } //A party member list packet came, set the party diff --git a/FFXIVClassic Map Server/actors/group/Party.cs b/FFXIVClassic Map Server/actors/group/Party.cs index 5d0c7f21..5747dd1a 100644 --- a/FFXIVClassic Map Server/actors/group/Party.cs +++ b/FFXIVClassic Map Server/actors/group/Party.cs @@ -20,14 +20,14 @@ namespace FFXIVClassic_Map_Server.actors.group members.Add(leaderCharaId); } - public void SetLeader(uint actorId) + public void SetLeader(uint leaderCharaId) { - partyGroupWork._globalTemp.owner = (ulong)((actorId << 32) | 0xB36F92); + partyGroupWork._globalTemp.owner = (ulong)(((ulong)leaderCharaId << 32) | 0xB36F92); } public uint GetLeader() { - return (uint)((partyGroupWork._globalTemp.owner >> 32) & 0xFFFFFFFF); + return (uint)((ulong)(partyGroupWork._globalTemp.owner >> 32) & 0xFFFFFFFF); } public bool IsInParty(uint charaId) diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs index aa397091..92d7421e 100644 --- a/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs +++ b/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs @@ -25,11 +25,10 @@ namespace FFXIVClassic_Map_Server.packets.WorldPackets.Receive try { partyGroupId = binReader.ReadUInt64(); - uint owner = binReader.ReadUInt32(); + owner = binReader.ReadUInt32(); uint numMembers = binReader.ReadUInt32(); memberActorIds = new uint[numMembers]; - - PartyGroup group = new PartyGroup(partyGroupId, owner); + for (int i = 0; i < numMembers; i++) memberActorIds[i] = binReader.ReadUInt32(); } diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/PartyModifyPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/PartyModifyPacket.cs new file mode 100644 index 00000000..6d758a61 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/Group/PartyModifyPacket.cs @@ -0,0 +1,30 @@ +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.Actors; +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 PartyModifyPacket + { + public const ushort OPCODE = 0x1020; + public const uint PACKET_SIZE = 0x48; + + public static SubPacket BuildPacket(Session session, ushort command, string name) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt16)command); + binWriter.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name)); + } + } + return new SubPacket(true, OPCODE, session.id, session.id, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroup.cs b/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroup.cs index eaeb6279..098a26b4 100644 --- a/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroup.cs +++ b/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroup.cs @@ -23,15 +23,15 @@ namespace FFXIVClassic_Map_Server.packets.send.group { using (BinaryWriter binWriter = new BinaryWriter(mem)) { - binWriter.Write((UInt64)group.groupId); - binWriter.Write((UInt32)group.groupTypeId); - binWriter.Write((Int32)group.localizedNamed); + binWriter.Write((UInt64)group.groupIndex); + binWriter.Write((UInt32)group.GetTypeId()); + binWriter.Write((Int32)group.GetGroupLocalizedName()); binWriter.Write((UInt16)0x121C); binWriter.Seek(0x20, SeekOrigin.Begin); - binWriter.Write(Encoding.ASCII.GetBytes(group.groupName), 0, Encoding.ASCII.GetByteCount(group.groupName) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(group.groupName)); + binWriter.Write(Encoding.ASCII.GetBytes(group.GetGroupName()), 0, Encoding.ASCII.GetByteCount(group.GetGroupName()) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(group.GetGroupName())); } } diff --git a/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroupMultiple.cs b/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroupMultiple.cs index 2946f653..9622e484 100644 --- a/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroupMultiple.cs +++ b/FFXIVClassic Map Server/packets/send/groups/CreateNamedGroupMultiple.cs @@ -33,15 +33,15 @@ namespace FFXIVClassic_Map_Server.packets.send.group Group group = groups[offset+i]; - binWriter.Write((UInt64)group.groupId); - binWriter.Write((UInt32)group.groupTypeId); - binWriter.Write((Int32)group.localizedNamed); + binWriter.Write((UInt64)group.groupIndex); + binWriter.Write((UInt32)group.GetTypeId()); + binWriter.Write((Int32)group.GetGroupLocalizedName()); binWriter.Write((UInt16)0x121C); binWriter.Seek(0x20, SeekOrigin.Begin); - binWriter.Write(Encoding.ASCII.GetBytes(group.groupName), 0, Encoding.ASCII.GetByteCount(group.groupName) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(group.groupName)); + binWriter.Write(Encoding.ASCII.GetBytes(group.GetGroupName()), 0, Encoding.ASCII.GetByteCount(group.GetGroupName()) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(group.GetGroupName())); } binWriter.Seek(0x200, SeekOrigin.Begin); diff --git a/FFXIVClassic Map Server/packets/send/groups/DeleteGroupPacket.cs b/FFXIVClassic Map Server/packets/send/groups/DeleteGroupPacket.cs index 5e3b0c95..8a3a559b 100644 --- a/FFXIVClassic Map Server/packets/send/groups/DeleteGroupPacket.cs +++ b/FFXIVClassic Map Server/packets/send/groups/DeleteGroupPacket.cs @@ -16,7 +16,7 @@ namespace FFXIVClassic_Map_Server.packets.send.groups public static SubPacket buildPacket(uint playerActorID, Group group) { - return buildPacket(playerActorID, group.groupId); + return buildPacket(playerActorID, group.groupIndex); } public static SubPacket buildPacket(uint playerActorID, ulong groupId) diff --git a/FFXIVClassic Map Server/packets/send/groups/GroupHeaderPacket.cs b/FFXIVClassic Map Server/packets/send/groups/GroupHeaderPacket.cs index 41de4827..ee05919c 100644 --- a/FFXIVClassic Map Server/packets/send/groups/GroupHeaderPacket.cs +++ b/FFXIVClassic Map Server/packets/send/groups/GroupHeaderPacket.cs @@ -33,17 +33,17 @@ namespace FFXIVClassic_Map_Server.packets.send.group //Write list id binWriter.Write((UInt64)3); - binWriter.Write((UInt64)group.groupId); + binWriter.Write((UInt64)group.groupIndex); binWriter.Write((UInt64)0); - binWriter.Write((UInt64)group.groupId); + binWriter.Write((UInt64)group.groupIndex); //This seems to change depending on what the list is for - binWriter.Write((UInt32)group.groupTypeId); + binWriter.Write((UInt32)group.GetTypeId()); binWriter.Seek(0x40, SeekOrigin.Begin); //This is for Linkshell - binWriter.Write((UInt32)group.localizedNamed); - binWriter.Write(Encoding.ASCII.GetBytes(group.groupName), 0, Encoding.ASCII.GetByteCount(group.groupName) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(group.groupName)); + binWriter.Write((UInt32)group.GetGroupLocalizedName()); + binWriter.Write(Encoding.ASCII.GetBytes(group.GetGroupName()), 0, Encoding.ASCII.GetByteCount(group.GetGroupName()) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(group.GetGroupName())); binWriter.Seek(0x64, SeekOrigin.Begin); @@ -52,7 +52,7 @@ namespace FFXIVClassic_Map_Server.packets.send.group binWriter.Write((UInt32)0x6D); binWriter.Write((UInt32)0x6D); - binWriter.Write((UInt32)group.members.Count); + binWriter.Write((UInt32)group.GetMemberCount()); } } diff --git a/FFXIVClassic Map Server/packets/send/groups/GroupMembersBeginPacket.cs b/FFXIVClassic Map Server/packets/send/groups/GroupMembersBeginPacket.cs index 981dd031..4c328a9a 100644 --- a/FFXIVClassic Map Server/packets/send/groups/GroupMembersBeginPacket.cs +++ b/FFXIVClassic Map Server/packets/send/groups/GroupMembersBeginPacket.cs @@ -27,8 +27,8 @@ namespace FFXIVClassic_Map_Server.packets.send.group binWriter.Write((UInt64)locationCode); binWriter.Write((UInt64)sequenceId); //Write List Info - binWriter.Write((UInt64)group.groupId); - binWriter.Write((UInt32)group.members.Count); + binWriter.Write((UInt64)group.groupIndex); + binWriter.Write((UInt32)group.GetMemberCount()); } } diff --git a/FFXIVClassic Map Server/packets/send/groups/GroupMembersEndPacket.cs b/FFXIVClassic Map Server/packets/send/groups/GroupMembersEndPacket.cs index fdf24e21..9a796008 100644 --- a/FFXIVClassic Map Server/packets/send/groups/GroupMembersEndPacket.cs +++ b/FFXIVClassic Map Server/packets/send/groups/GroupMembersEndPacket.cs @@ -27,7 +27,7 @@ namespace FFXIVClassic_Map_Server.packets.send.group binWriter.Write((UInt64)locationCode); binWriter.Write((UInt64)sequenceId); //Write List Info - binWriter.Write((UInt64)group.groupId); + binWriter.Write((UInt64)group.groupIndex); } } diff --git a/FFXIVClassic World Server/DataObjects/Group/Group.cs b/FFXIVClassic World Server/DataObjects/Group/Group.cs index 1a38df0d..5cee6529 100644 --- a/FFXIVClassic World Server/DataObjects/Group/Group.cs +++ b/FFXIVClassic World Server/DataObjects/Group/Group.cs @@ -48,6 +48,17 @@ namespace FFXIVClassic_World_Server.DataObjects.Group return new List(); } + public void SendGroupPacketsAll(List sessionIds) + { + for (int i = 0; i < sessionIds.Count; i++) + { + Session session = Server.GetServer().GetSession(sessionIds[i]); + + if (session != null) + SendGroupPackets(session); + } + } + public void SendGroupPackets(Session session) { ulong time = Utils.MilisUnixTimeStampUTC(); diff --git a/FFXIVClassic World Server/DataObjects/Group/Party.cs b/FFXIVClassic World Server/DataObjects/Group/Party.cs index 6bf4fb27..f993fde2 100644 --- a/FFXIVClassic World Server/DataObjects/Group/Party.cs +++ b/FFXIVClassic World Server/DataObjects/Group/Party.cs @@ -20,6 +20,90 @@ namespace FFXIVClassic_World_Server.DataObjects.Group members.Add(leaderCharaId); } + public void SetLeaderPlayerRequest(Session requestSession, string name) + { + if (GetLeader() != requestSession.sessionId) + { + requestSession.SendGameMessage(30428, 0x20, Server.GetServer().GetNameForId(requestSession.sessionId)); + return; + } + + uint newLeader = GetIdForName(name); + + if (newLeader == 0) + { + requestSession.SendGameMessage(30575, 0x20); + return; + } + else if (newLeader == GetLeader()) + { + requestSession.SendGameMessage(30563, 0x20, name); + return; + } + + SetLeader(newLeader); + SendLeaderWorkToAllMembers(); + + for (int i = 0; i < members.Count; i++) + { + Session session = Server.GetServer().GetSession(members[i]); + if (session == null) + continue; + session.SendGameMessage(30429, 0x20, Server.GetServer().GetNameForId(members[i])); + } + + Server.GetServer().GetWorldManager().SendPartySync(this); + } + + public void KickPlayerRequest(Session requestSession, string name) + { + if (GetLeader() != requestSession.sessionId) + { + requestSession.SendGameMessage(30428, 0x20, Server.GetServer().GetNameForId(requestSession.sessionId)); + return; + } + + uint kickedMemberId = GetIdForName(name); + + if (kickedMemberId == 0) + { + requestSession.SendGameMessage(30575, 0x20); + return; + } + + for (int i = 0; i < members.Count; i++) + { + Session session = Server.GetServer().GetSession(members[i]); + if (session == null) + continue; + + if (members[i] == kickedMemberId) + session.SendGameMessage(30410, 0x20); + else + session.SendGameMessage(30428, 0x20, (Object)name); + } + + //All good, remove + members.Remove(kickedMemberId); + SendGroupPacketsAll(members); + Server.GetServer().GetWorldManager().SendPartySync(this); + } + + public void SendLeaderWorkToAllMembers() + { + for (int i = 0; i < members.Count; i++) + { + SynchGroupWorkValuesPacket leaderUpdate = new SynchGroupWorkValuesPacket(groupIndex); + leaderUpdate.addProperty(this, "partyGroupWork._globalTemp.owner"); + leaderUpdate.setTarget("partyGroupWork/leader"); + Session session = Server.GetServer().GetSession(members[i]); + if (session == null) + continue; + else + session.clientConnection.QueuePacket(leaderUpdate.buildPacket(session.sessionId, session.sessionId), true, false); + } + } + public void SetLeader(uint actorId) { partyGroupWork._globalTemp.owner = (ulong)((actorId << 32) | 0xB36F92); @@ -30,10 +114,22 @@ namespace FFXIVClassic_World_Server.DataObjects.Group return (uint)((partyGroupWork._globalTemp.owner >> 32) & 0xFFFFFFFF); } + public uint GetIdForName(string name) + { + for (int i = 0; i < members.Count; i++) + { + if (Server.GetServer().GetNameForId(members[i]).Equals(name)) + { + return members[i]; + } + } + return 0; + } + public bool IsInParty(uint charaId) { return members.Contains(charaId); - } + } public override void SendInitWorkValues(Session session) { @@ -64,5 +160,6 @@ namespace FFXIVClassic_World_Server.DataObjects.Group return groupMembers; } + } } diff --git a/FFXIVClassic World Server/DataObjects/LuaParam.cs b/FFXIVClassic World Server/DataObjects/LuaParam.cs new file mode 100644 index 00000000..8fe6a0f7 --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/LuaParam.cs @@ -0,0 +1,16 @@ +using System; + +namespace FFXIVClassic_World_Server.DataObjects +{ + class LuaParam + { + public int typeID; + public Object value; + + public LuaParam(int type, Object value) + { + this.typeID = type; + this.value = value; + } + } +} diff --git a/FFXIVClassic World Server/DataObjects/LuaUtils.cs b/FFXIVClassic World Server/DataObjects/LuaUtils.cs new file mode 100644 index 00000000..67c18b75 --- /dev/null +++ b/FFXIVClassic World Server/DataObjects/LuaUtils.cs @@ -0,0 +1,380 @@ +using FFXIVClassic.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace FFXIVClassic_World_Server.DataObjects +{ + class LuaUtils + { + + public class Type7Param + { + public uint actorId; + public byte unknown; + public byte slot; + public byte inventoryType; + + public Type7Param(uint actorId, byte unknown, byte slot, byte inventoryType) + { + this.actorId = actorId; + this.unknown = unknown; + this.slot = slot; + this.inventoryType = inventoryType; + } + } + + public class Type9Param + { + public ulong item1; + public ulong item2; + + public Type9Param(ulong item1, ulong item2) + { + this.item1 = item1; + this.item2 = item2; + } + } + + public static List ReadLuaParams(BinaryReader reader) + { + List luaParams = new List(); + + bool isDone = false; + while (true) + { + byte code = reader.ReadByte(); + object value = null; + bool wasNil = false; + + switch (code) + { + case 0x0: //Int32 + value = Utils.SwapEndian(reader.ReadInt32()); + break; + case 0x1: //Int32 + value = Utils.SwapEndian(reader.ReadUInt32()); + break; + case 0x2: //Null Termed String + List list = new List(); + while(true){ + byte readByte = reader.ReadByte(); + if (readByte == 0) + break; + list.Add(readByte); + } + value = Encoding.ASCII.GetString(list.ToArray()); + break; + case 0x3: //Boolean True + value = true; + break; + case 0x4: //Boolean False + value = false; + break; + case 0x5: //Nil + wasNil = true; + break; + case 0x6: //Actor (By Id) + value = Utils.SwapEndian(reader.ReadUInt32()); + break; + case 0x7: //Weird one used for inventory + uint type7ActorId = Utils.SwapEndian(reader.ReadUInt32()); + byte type7Unknown = reader.ReadByte(); + byte type7Slot = reader.ReadByte(); + byte type7InventoryType = reader.ReadByte(); + value = new Type7Param(type7ActorId, type7Unknown, type7Slot, type7InventoryType); + break; + case 0x9: //Two Longs (only storing first one) + value = new Type9Param(Utils.SwapEndian(reader.ReadUInt64()), Utils.SwapEndian(reader.ReadUInt64())); + break; + case 0xC: //Byte + value = reader.ReadByte(); + break; + case 0x1B: //Short? + value = reader.ReadUInt16(); + break; + case 0xF: //End + isDone = true; + continue; + } + + if (isDone) + break; + + if (value != null) + luaParams.Add(new LuaParam(code, value)); + else if (wasNil) + luaParams.Add(new LuaParam(code, value)); + } + + return luaParams; + } + + public static void WriteLuaParams(BinaryWriter writer, List luaParams) + { + foreach (LuaParam l in luaParams) + { + if (l.typeID == 0x1) + writer.Write((Byte)0); + else + writer.Write((Byte)l.typeID); + + switch (l.typeID) + { + case 0x0: //Int32 + writer.Write((Int32)Utils.SwapEndian((Int32)l.value)); + break; + case 0x1: //Int32 + writer.Write((UInt32)Utils.SwapEndian((UInt32)l.value)); + break; + case 0x2: //Null Termed String + string sv = (string)l.value; + writer.Write(Encoding.ASCII.GetBytes(sv), 0, Encoding.ASCII.GetByteCount(sv)); + writer.Write((Byte)0); + break; + case 0x3: //Boolean True + break; + case 0x4: //Boolean False + break; + case 0x5: //Nil + break; + case 0x6: //Actor (By Id) + writer.Write((UInt32)Utils.SwapEndian((UInt32)l.value)); + break; + case 0x7: //Weird one used for inventory + Type7Param type7 = (Type7Param)l.value; + writer.Write((UInt32)Utils.SwapEndian((UInt32)type7.actorId)); + writer.Write((Byte)type7.unknown); + writer.Write((Byte)type7.slot); + writer.Write((Byte)type7.inventoryType); + break; + case 0x9: //Two Longs (only storing first one) + writer.Write((UInt64)Utils.SwapEndian(((Type9Param)l.value).item1)); + writer.Write((UInt64)Utils.SwapEndian(((Type9Param)l.value).item2)); + break; + case 0xC: //Byte + writer.Write((Byte)l.value); + break; + case 0x1B: //Short? + break; + case 0xF: //End + continue; + } + } + + writer.Write((Byte)0xF); + } + + public static List ReadLuaParams(byte[] bytesIn) + { + List luaParams = new List(); + + using (MemoryStream memStream = new MemoryStream(bytesIn)) + { + using (BinaryReader reader = new BinaryReader(memStream)) + { + bool isDone = false; + while (true) + { + byte code = reader.ReadByte(); + object value = null; + bool wasNil = false; + + switch (code) + { + case 0x0: //Int32 + value = Utils.SwapEndian(reader.ReadInt32()); + break; + case 0x1: //Int32 + value = Utils.SwapEndian(reader.ReadUInt32()); + break; + case 0x2: //Null Termed String + List list = new List(); + while (true) + { + byte readByte = reader.ReadByte(); + if (readByte == 0) + break; + list.Add(readByte); + } + value = Encoding.ASCII.GetString(list.ToArray()); + break; + case 0x3: //Boolean True + value = true; + break; + case 0x4: //Boolean False + value = false; + break; + case 0x5: //Nil + wasNil = true; + break; + case 0x6: //Actor (By Id) + value = Utils.SwapEndian(reader.ReadUInt32()); + break; + case 0x7: //Weird one used for inventory + uint type7ActorId = Utils.SwapEndian(reader.ReadUInt32()); + byte type7Unknown = reader.ReadByte(); + byte type7Slot = reader.ReadByte(); + byte type7InventoryType = reader.ReadByte(); + value = new Type7Param(type7ActorId, type7Unknown, type7Slot, type7InventoryType); + break; + case 0x9: //Two Longs (only storing first one) + value = new Type9Param(Utils.SwapEndian(reader.ReadUInt64()), Utils.SwapEndian(reader.ReadUInt64())); + break; + case 0xC: //Byte + value = reader.ReadByte(); + break; + case 0x1B: //Short? + value = reader.ReadUInt16(); + break; + case 0xF: //End + isDone = true; + continue; + } + + if (isDone) + break; + + if (value != null) + luaParams.Add(new LuaParam(code, value)); + else if (wasNil) + luaParams.Add(new LuaParam(code, value)); + } + } + } + return luaParams; + } + + public static List CreateLuaParamList(params object[] list) + { + List luaParams = new List(); + + foreach (object o in list) + { + if (o != null && o.GetType().IsArray) + { + Array arrayO = (Array)o; + foreach (object o2 in arrayO) + AddToList(o2, luaParams); + } + else + AddToList(o, luaParams); + } + + return luaParams; + } + + private static void AddToList(object o, List luaParams) + { + if (o is int) + { + luaParams.Add(new LuaParam(0x0, (int)o)); + } + else if (o is uint) + { + luaParams.Add(new LuaParam(0x1, (uint)o)); + } + else if (o is Double) + { + if (((double)o) % 1 == 0) + luaParams.Add(new LuaParam(0x0, (int)(double)o)); + } + else if (o is string) + { + luaParams.Add(new LuaParam(0x2, (string)o)); + } + else if (o is bool) + { + if (((bool)o)) + luaParams.Add(new LuaParam(0x3, null)); + else + luaParams.Add(new LuaParam(0x4, null)); + } + else if (o == null) + { + luaParams.Add(new LuaParam(0x5, null)); + } + else if (o is Type7Param) + { + luaParams.Add(new LuaParam(0x7, (Type7Param)o)); + } + else if (o is Type9Param) + { + luaParams.Add(new LuaParam(0x9, (Type9Param)o)); + } + else if (o is byte) + { + luaParams.Add(new LuaParam(0xC, (byte)o)); + } + } + + public static object[] CreateLuaParamObjectList(List luaParams) + { + object[] list = new object[luaParams.Count]; + + for (int i = 0; i < list.Length; i++) + list[i] = luaParams[i].value; + + return list; + } + + + public static string DumpParams(List lParams) + { + if (lParams == null) + return "Param list was null?"; + + string dumpString = ""; + for (int i = 0; i < lParams.Count; i++) + { + switch (lParams[i].typeID) + { + case 0x0: //Int32 + dumpString += String.Format("0x{0:X}", (int)lParams[i].value); + break; + case 0x1: //Int32 + dumpString += String.Format("0x{0:X}", (uint)lParams[i].value); + break; + case 0x2: //Null Termed String + dumpString += String.Format("\"{0}\"", (string)lParams[i].value); + break; + case 0x3: //Boolean True + dumpString += "true"; + break; + case 0x4: //Boolean False + dumpString += "false"; + break; + case 0x5: //NULL??? + dumpString += "nil"; + break; + case 0x6: //Actor (By Id) + dumpString += String.Format("0x{0:X}", (uint)lParams[i].value); + break; + case 0x7: //Weird one used for inventory + Type7Param type7Param = ((Type7Param)lParams[i].value); + dumpString += String.Format("Type7 Param: (0x{0:X}, 0x{1:X}, 0x{2:X}, 0x{3:X})", type7Param.actorId, type7Param.unknown, type7Param.slot, type7Param.inventoryType); + break; + case 0xC: //Byte + dumpString += String.Format("0x{0:X}", (byte)lParams[i].value); + break; + case 0x9: //Long (+ 8 bytes ignored) + Type9Param type9Param = ((Type9Param)lParams[i].value); + dumpString += String.Format("Type9 Param: (0x{0:X}, 0x{1:X})", type9Param.item1, type9Param.item2); + break; + case 0x1B: //Short? + dumpString += String.Format("0x{0:X}", (ushort)lParams[i].value); + break; + case 0xF: //End + break; + } + + if (i != lParams.Count - 1) + dumpString += ", "; + } + + return dumpString; + } + + } +} diff --git a/FFXIVClassic World Server/DataObjects/Session.cs b/FFXIVClassic World Server/DataObjects/Session.cs index 5a0085b9..ee94330f 100644 --- a/FFXIVClassic World Server/DataObjects/Session.cs +++ b/FFXIVClassic World Server/DataObjects/Session.cs @@ -1,4 +1,5 @@ -using System; +using FFXIVClassic_World_Server.Packets.Send.Subpackets; +using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; @@ -28,6 +29,30 @@ namespace FFXIVClassic_World_Server.DataObjects connection.owner = this; Database.LoadZoneSessionInfo(this); } - + + public void SendGameMessage( ushort textId, byte log, params object[] msgParams) + { + if (msgParams == null || msgParams.Length == 0) + clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, log), true, false); + else + clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, log, LuaUtils.CreateLuaParamList(msgParams)), true, false); + } + + public void SendGameMessage( ushort textId, byte log, string customSender, params object[] msgParams) + { + if (msgParams == null || msgParams.Length == 0) + clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, customSender, log), true, false); + else + clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, customSender, log, LuaUtils.CreateLuaParamList(msgParams)), true, false); + } + + public void SendGameMessage(ushort textId, byte log, uint displayId, params object[] msgParams) + { + if (msgParams == null || msgParams.Length == 0) + clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, displayId, log), true, false); + else + clientConnection.QueuePacket(GameMessagePacket.BuildPacket(0x5FF80001, sessionId, 0x5FF80001, textId, displayId, log, LuaUtils.CreateLuaParamList(msgParams)), true, false); + } + } } diff --git a/FFXIVClassic World Server/Database.cs b/FFXIVClassic World Server/Database.cs index a342da8b..f6b40f00 100644 --- a/FFXIVClassic World Server/Database.cs +++ b/FFXIVClassic World Server/Database.cs @@ -193,6 +193,41 @@ namespace FFXIVClassic_World_Server return members; } + public static Linkshell GetLinkshell(ulong groupIndex, string lsName) + { + 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 id, name, crestIcon, master FROM server_linkshells WHERE name = @lsName", conn); + cmd.Parameters.AddWithValue("@lsName", lsName); + using (MySqlDataReader Reader = cmd.ExecuteReader()) + { + while (Reader.Read()) + { + ulong lsId = Reader.GetUInt64("id"); + string name = Reader.GetString("name"); + ushort crest = Reader.GetUInt16("crestIcon"); + uint master = Reader.GetUInt32("master"); + + Linkshell linkshell = new Linkshell(lsId, groupIndex, name, crest, master, 0xa); + return linkshell; + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + return null; + } + 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))) diff --git a/FFXIVClassic World Server/FFXIVClassic World Server.csproj b/FFXIVClassic World Server/FFXIVClassic World Server.csproj index a7ab76de..086a7145 100644 --- a/FFXIVClassic World Server/FFXIVClassic World Server.csproj +++ b/FFXIVClassic World Server/FFXIVClassic World Server.csproj @@ -83,12 +83,15 @@ + + + @@ -105,22 +108,19 @@ + + - - - - - diff --git a/FFXIVClassic World Server/LinkshellManager.cs b/FFXIVClassic World Server/LinkshellManager.cs index 661d9d71..32b5e0fa 100644 --- a/FFXIVClassic World Server/LinkshellManager.cs +++ b/FFXIVClassic World Server/LinkshellManager.cs @@ -11,9 +11,10 @@ namespace FFXIVClassic_World_Server { private WorldManager mWorldManager; private Object mGroupLockReference; - private Dictionary mCurrentWorldGroupsReference; - private Dictionary mLinkshellList = new Dictionary(); - private Dictionary mNameToIdLookup = new Dictionary(); + private Dictionary mCurrentWorldGroupsReference; //GroupId, LS + private Dictionary mLinkshellList = new Dictionary(); //GroupId, LS + private Dictionary mLSIdToIdLookup = new Dictionary(); //Name, GroupId + private Dictionary mNameToIdLookup = new Dictionary(); //Name, GroupId public LinkshellManager(WorldManager worldManager, Object groupLock, Dictionary worldGroupList) { @@ -162,14 +163,31 @@ namespace FFXIVClassic_World_Server if (mNameToIdLookup.ContainsKey(name)) return (Linkshell)mCurrentWorldGroupsReference[mNameToIdLookup[name]]; else - return null; + { + lock (mGroupLockReference) + { + Linkshell ls = Database.GetLinkshell(mWorldManager.GetGroupIndex(), name); + ls.LoadMembers(); + + if (ls != null) + { + mLinkshellList.Add(ls.groupIndex, ls); + mNameToIdLookup.Add(ls.name, ls.groupIndex); + mCurrentWorldGroupsReference.Add(ls.groupIndex, ls); + mWorldManager.IncrementGroupIndex(); + return ls; + } + 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]; + if (mLSIdToIdLookup.ContainsKey(lsId)) + return mLSIdToIdLookup[lsId]; else { lock (mGroupLockReference) @@ -181,6 +199,7 @@ namespace FFXIVClassic_World_Server { mLinkshellList.Add(ls.groupIndex, ls); mNameToIdLookup.Add(ls.name, ls.groupIndex); + mLSIdToIdLookup.Add(ls.dbId, ls); mCurrentWorldGroupsReference.Add(ls.groupIndex, ls); mWorldManager.IncrementGroupIndex(); return ls; diff --git a/FFXIVClassic World Server/Packets/Send/Subpackets/GameMessagePacket.cs b/FFXIVClassic World Server/Packets/Send/Subpackets/GameMessagePacket.cs new file mode 100644 index 00000000..8b3041fe --- /dev/null +++ b/FFXIVClassic World Server/Packets/Send/Subpackets/GameMessagePacket.cs @@ -0,0 +1,348 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; + +namespace FFXIVClassic_World_Server.Packets.Send.Subpackets +{ + class GameMessagePacket + { + private const ushort OPCODE_GAMEMESSAGE_WITH_ACTOR1 = 0x157; + private const ushort OPCODE_GAMEMESSAGE_WITH_ACTOR2 = 0x158; + private const ushort OPCODE_GAMEMESSAGE_WITH_ACTOR3 = 0x159; + private const ushort OPCODE_GAMEMESSAGE_WITH_ACTOR4 = 0x15a; + private const ushort OPCODE_GAMEMESSAGE_WITH_ACTOR5 = 0x15b; + + private const ushort OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER1 = 0x15c; + private const ushort OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER2 = 0x15d; + private const ushort OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER3 = 0x15e; + private const ushort OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER4 = 0x15f; + private const ushort OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER5 = 0x160; + + private const ushort OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER1 = 0x161; + private const ushort OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER2 = 0x162; + private const ushort OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER3 = 0x163; + private const ushort OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER4 = 0x164; + private const ushort OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER5 = 0x165; + + private const ushort OPCODE_GAMEMESSAGE_WITHOUT_ACTOR1 = 0x166; + private const ushort OPCODE_GAMEMESSAGE_WITHOUT_ACTOR2 = 0x167; + private const ushort OPCODE_GAMEMESSAGE_WITHOUT_ACTOR3 = 0x168; + private const ushort OPCODE_GAMEMESSAGE_WITHOUT_ACTOR4 = 0x169; + private const ushort OPCODE_GAMEMESSAGE_WITHOUT_ACTOR5 = 0x16a; + + private const ushort SIZE_GAMEMESSAGE_WITH_ACTOR1 = 0x30; + private const ushort SIZE_GAMEMESSAGE_WITH_ACTOR2 = 0x38; + private const ushort SIZE_GAMEMESSAGE_WITH_ACTOR3 = 0x40; + private const ushort SIZE_GAMEMESSAGE_WITH_ACTOR4 = 0x50; + private const ushort SIZE_GAMEMESSAGE_WITH_ACTOR5 = 0x70; + + private const ushort SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER1 = 0x48; + private const ushort SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER2 = 0x58; + private const ushort SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER3 = 0x68; + private const ushort SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER4 = 0x78; + private const ushort SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER5 = 0x98; + + private const ushort SIZE_GAMEMESSAGE_WITH_DISPID_SENDER1 = 0x30; + private const ushort SIZE_GAMEMESSAGE_WITH_DISPID_SENDER2 = 0x38; + private const ushort SIZE_GAMEMESSAGE_WITH_DISPID_SENDER3 = 0x40; + private const ushort SIZE_GAMEMESSAGE_WITH_DISPID_SENDER4 = 0x50; + private const ushort SIZE_GAMEMESSAGE_WITH_DISPID_SENDER5 = 0x60; + + private const ushort SIZE_GAMEMESSAGE_WITHOUT_ACTOR1 = 0x28; + private const ushort SIZE_GAMEMESSAGE_WITHOUT_ACTOR2 = 0x38; + private const ushort SIZE_GAMEMESSAGE_WITHOUT_ACTOR3 = 0x38; + private const ushort SIZE_GAMEMESSAGE_WITHOUT_ACTOR4 = 0x48; + private const ushort SIZE_GAMEMESSAGE_WITHOUT_ACTOR5 = 0x68; + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint actorId, uint textOwnerActorId, ushort textId, byte log) + { + byte[] data = new byte[SIZE_GAMEMESSAGE_WITH_ACTOR1 - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)actorId); + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + } + } + + return new SubPacket(OPCODE_GAMEMESSAGE_WITH_ACTOR1, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint actorId, uint textOwnerActorId, ushort textId, byte log, List lParams) + { + int lParamsSize = findSizeOfParams(lParams); + byte[] data; + ushort opcode; + + if (lParamsSize <= 0x8) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_ACTOR2 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_ACTOR2; + } + else if (lParamsSize <= 0x10) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_ACTOR3 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_ACTOR3; + } + else if (lParamsSize <= 0x20) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_ACTOR4 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_ACTOR4; + } + else + { + data = new byte[SIZE_GAMEMESSAGE_WITH_ACTOR5 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_ACTOR5; + } + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)actorId); + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + LuaUtils.WriteLuaParams(binWriter, lParams); + + if (lParamsSize <= 0x14-12) + { + binWriter.Seek(0x14, SeekOrigin.Begin); + binWriter.Write((UInt32)8); + } + } + } + + return new SubPacket(opcode, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint textOwnerActorId, ushort textId, string sender, byte log) + { + byte[] data = new byte[SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER1 - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + binWriter.Write(Encoding.ASCII.GetBytes(sender), 0, Encoding.ASCII.GetByteCount(sender) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(sender)); + } + } + + return new SubPacket(OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER1, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint textOwnerActorId, ushort textId, string sender, byte log, List lParams) + { + int lParamsSize = findSizeOfParams(lParams); + byte[] data; + ushort opcode; + + if (lParamsSize <= 0x8) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER2 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER2; + } + else if (lParamsSize <= 0x10) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER3 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER3; + } + else if (lParamsSize <= 0x20) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER4 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER4; + } + else + { + data = new byte[SIZE_GAMEMESSAGE_WITH_CUSTOM_SENDER5 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_CUSTOM_SENDER5; + } + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + binWriter.Write(Encoding.ASCII.GetBytes(sender), 0, Encoding.ASCII.GetByteCount(sender) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(sender)); + LuaUtils.WriteLuaParams(binWriter, lParams); + + if (lParamsSize <= 0x14 - 12) + { + binWriter.Seek(0x30, SeekOrigin.Begin); + binWriter.Write((UInt32)8); + } + } + } + + return new SubPacket(opcode, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint textOwnerActorId, ushort textId, uint senderDisplayNameId, byte log) + { + byte[] data = new byte[SIZE_GAMEMESSAGE_WITH_DISPID_SENDER1 - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)senderDisplayNameId); + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + } + } + + return new SubPacket(OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER1, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint textOwnerActorId, ushort textId, uint senderDisplayNameId, byte log, List lParams) + { + int lParamsSize = findSizeOfParams(lParams); + byte[] data; + ushort opcode; + + if (lParamsSize <= 0x8) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_DISPID_SENDER2 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER2; + } + else if (lParamsSize <= 0x10) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_DISPID_SENDER3 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER3; + } + else if (lParamsSize <= 0x20) + { + data = new byte[SIZE_GAMEMESSAGE_WITH_DISPID_SENDER4 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER4; + } + else + { + data = new byte[SIZE_GAMEMESSAGE_WITH_DISPID_SENDER5 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITH_DISPID_SENDER5; + } + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)senderDisplayNameId); + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + LuaUtils.WriteLuaParams(binWriter, lParams); + + if (lParamsSize <= 0x14 - 12) + { + binWriter.Seek(0x14, SeekOrigin.Begin); + binWriter.Write((UInt32)8); + } + } + } + + return new SubPacket(opcode, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint textOwnerActorId, ushort textId, byte log) + { + byte[] data = new byte[SIZE_GAMEMESSAGE_WITHOUT_ACTOR1 - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + } + } + + return new SubPacket(OPCODE_GAMEMESSAGE_WITHOUT_ACTOR1, sourceId, targetId, data); + } + + public static SubPacket BuildPacket(uint sourceId, uint targetId, uint textOwnerActorId, ushort textId, byte log, List lParams) + { + int lParamsSize = findSizeOfParams(lParams); + byte[] data; + ushort opcode; + + if (lParamsSize <= 0x8) + { + data = new byte[SIZE_GAMEMESSAGE_WITHOUT_ACTOR2 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITHOUT_ACTOR2; + } + else if (lParamsSize <= 0x10) + { + data = new byte[SIZE_GAMEMESSAGE_WITHOUT_ACTOR3 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITHOUT_ACTOR3; + } + else if (lParamsSize <= 0x20) + { + data = new byte[SIZE_GAMEMESSAGE_WITHOUT_ACTOR4 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITHOUT_ACTOR4; + } + else + { + data = new byte[SIZE_GAMEMESSAGE_WITHOUT_ACTOR5 - 0x20]; + opcode = OPCODE_GAMEMESSAGE_WITHOUT_ACTOR5; + } + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)textOwnerActorId); + binWriter.Write((UInt16)textId); + binWriter.Write((UInt16)log); + LuaUtils.WriteLuaParams(binWriter, lParams); + + if (lParamsSize <= 0x8) + { + binWriter.Seek(0x10, SeekOrigin.Begin); + binWriter.Write((UInt32)8); + } + } + } + + return new SubPacket(opcode, sourceId, targetId, data); + } + + private static int findSizeOfParams(List lParams) + { + int total = 0; + foreach (LuaParam l in lParams) + { + switch (l.typeID) + { + case 0: + case 1: + case 2: + total += 1 + 0x20; + break; + case 6: + total += 5; + break; + case 3: + case 4: + case 5: + total += 1; + break; + } + } + return total + 1; + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyLeavePacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyLeavePacket.cs new file mode 100644 index 00000000..f29cb06e --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyLeavePacket.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 PartyLeavePacket + { + public bool invalidPacket = false; + + public bool isDisband; + + public PartyLeavePacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + isDisband = binReader.ReadByte() == 1; + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupMemberChangePacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyModifyPacket.cs similarity index 56% rename from FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupMemberChangePacket.cs rename to FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyModifyPacket.cs index a207e8d3..c410d962 100644 --- a/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/GroupMemberChangePacket.cs +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/Group/PartyModifyPacket.cs @@ -1,19 +1,21 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Text; namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group { - class GroupMemberChangePacket + class PartyModifyPacket { - 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) + public const ushort MODIFY_LEADER = 0; + public const ushort MODIFY_KICKPLAYER = 1; + + public ushort command; + public string name; + + public PartyModifyPacket(byte[] data) { using (MemoryStream mem = new MemoryStream(data)) { @@ -21,9 +23,8 @@ namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive.Group { try { - controlCode = binReader.ReadUInt32(); - groupId = binReader.ReadUInt64(); - memberId = binReader.ReadUInt32(); + command = binReader.ReadUInt16(); + name = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' }); } catch (Exception) { diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs deleted file mode 100644 index c949b6e1..00000000 --- a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListEndPacket.cs +++ /dev/null @@ -1,28 +0,0 @@ -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 deleted file mode 100644 index 7213f2d7..00000000 --- a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListPacket.cs +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index 06104f59..00000000 --- a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupMemberListStartPacket.cs +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 3757a9a1..00000000 --- a/FFXIVClassic World Server/Packets/WorldPackets/Send/Group/GroupWorkValuesPacket.cs +++ /dev/null @@ -1,29 +0,0 @@ -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/Server.cs b/FFXIVClassic World Server/Server.cs index de70d8b5..8911eed2 100644 --- a/FFXIVClassic World Server/Server.cs +++ b/FFXIVClassic World Server/Server.cs @@ -199,7 +199,25 @@ namespace FFXIVClassic_World_Server GetWorldManager().DoZoneServerChange(session, zoneChangePacket.destinationZoneId, "", zoneChangePacket.destinationSpawnType, zoneChangePacket.destinationX, zoneChangePacket.destinationY, zoneChangePacket.destinationZ, zoneChangePacket.destinationRot); } - break; + break; + //Change leader or kick + case 0x1020: + PartyModifyPacket partyModifyPacket = new PartyModifyPacket(subpacket.data); + + Party pt = mWorldManager.GetPartyManager().GetParty(subpacket.header.targetId); + + if (pt.GetMemberCount() <= 1) + return; + + if (partyModifyPacket.command == PartyModifyPacket.MODIFY_LEADER) + pt.SetLeaderPlayerRequest(GetSession(subpacket.header.sourceId), partyModifyPacket.name); + else if (partyModifyPacket.command == PartyModifyPacket.MODIFY_KICKPLAYER) + pt.KickPlayerRequest(GetSession(subpacket.header.sourceId), partyModifyPacket.name); + + break; + //Party Resign or Disband + case 0x1021: + break; //Linkshell create request case 0x1025: CreateLinkshellPacket createLinkshellPacket = new CreateLinkshellPacket(subpacket.data); diff --git a/FFXIVClassic World Server/WorldMaster.cs b/FFXIVClassic World Server/WorldMaster.cs index 714e6a71..42e56e87 100644 --- a/FFXIVClassic World Server/WorldMaster.cs +++ b/FFXIVClassic World Server/WorldMaster.cs @@ -212,13 +212,13 @@ namespace FFXIVClassic_World_Server //Send party, retainer, ls groups 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); + + + if (session.sessionId == 0x6c) + { + mPartyManager.AddToParty(pt.groupIndex, 156); + } + pt.SendGroupPackets(session); SendPartySync(pt); mRetainerGroupManager.GetRetainerGroup(session.sessionId).SendGroupPackets(session); @@ -238,12 +238,18 @@ namespace FFXIVClassic_World_Server } public void SendPartySync(Party party) - { + { + List alreadySent = new List(); foreach (uint member in party.members) { Session session = Server.GetServer().GetSession(member); if (session == null) continue; + + if (alreadySent.Contains(session.routing1)) + continue; + + alreadySent.Add(session.routing1); SubPacket syncPacket = PartySyncPacket.BuildPacket(session, party); session.routing1.SendPacket(syncPacket); }