diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index d55dc078..d6eab493 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -78,7 +78,12 @@ namespace FFXIVClassic_Map_Server client.QueuePacket(SessionEndConfirmPacket.BuildPacket(session, endSessionPacket.destinationZoneId), true, false); client.FlushQueuedSendPackets(); - break; + break; + //World Server - Party Synch + case 0x1020: + PartySyncPacket partySyncPacket = new PartySyncPacket(subpacket.data); + Server.GetWorldManager().PartyMemberListRecieved(partySyncPacket); + break; //Ping case 0x0001: //subpacket.DebugPrintSubPacket(); diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 0d8b717b..a8a5d597 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -16,6 +16,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using FFXIVClassic_Map_Server.actors.group; +using FFXIVClassic_Map_Server.packets.send.group; +using FFXIVClassic_Map_Server.packets.WorldPackets.Receive; namespace FFXIVClassic_Map_Server { @@ -27,6 +30,7 @@ namespace FFXIVClassic_Map_Server private Dictionary> seamlessBoundryList; private Dictionary zoneEntranceList; private Dictionary actorClasses = new Dictionary(); + private Dictionary currentPlayerParties = new Dictionary(); //GroupId, Party object private Server mServer; @@ -659,6 +663,58 @@ namespace FFXIVClassic_Map_Server zc.RequestZoneChange(player.playerSession.id, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation); } + //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) + { + PartyGroup group; + List members = new List(); + + //Build member list for players on this server + foreach (uint actorId in syncPacket.memberActorIds) + { + Player p = GetPCInWorld(actorId); + + if (p == null) + continue; + + GroupMember member = new GroupMember(actorId, -1, 0, false, true, p.customDisplayName); + members.Add(member); + } + + //If no members on this server, get out or clean + if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId) && members.Count == 0) + return; + else if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId) && members.Count == 0) + NoMembersInParty(currentPlayerParties[syncPacket.partyGroupId]); + + //Get or create group + if (!currentPlayerParties.ContainsKey(syncPacket.partyGroupId)) + { + group = new PartyGroup(syncPacket.partyGroupId, syncPacket.owner); + currentPlayerParties.Add(syncPacket.partyGroupId, group); + } + else + group = currentPlayerParties[syncPacket.partyGroupId]; + + currentPlayerParties[syncPacket.partyGroupId].members = members; + + //Add group to everyone + foreach (GroupMember member in members) + { + Player player = GetPCInWorld(member.actorId); + if (player == null) + continue; + player.SetParty(group); + } + } + + //Player was removed from the party either due to leaving it or leaving the server. Remove if empty. + public void NoMembersInParty(PartyGroup party) + { + if (currentPlayerParties.ContainsKey(party.groupId)) + currentPlayerParties.Remove(party.groupId); + } + public Player GetPCInWorld(string name) { foreach (Zone zone in zoneList.Values) @@ -745,7 +801,7 @@ namespace FFXIVClassic_Map_Server return actorClasses[id]; else return null; - } + } } diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 03a95331..34d8850c 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -1,5 +1,6 @@  using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.group; using FFXIVClassic_Map_Server.Actors.Chara; using FFXIVClassic_Map_Server.packets.send.actor; @@ -50,7 +51,9 @@ namespace FFXIVClassic_Map_Server.Actors public Work work = new Work(); public CharaWork charaWork = new CharaWork(); - + + public Group currentParty = null; + public Character(uint actorID) : base(actorID) { //Init timer array to "notimer" diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index 8763eb00..5ecdac30 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -474,5 +474,14 @@ namespace FFXIVClassic_Map_Server.Actors else return; } + + //A party member list packet came, set the party + /* public void SetParty(MonsterPartyGroup group) + { + if (group is MonsterPartyGroup) + currentParty = group; + } + */ + } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 69062109..56ca1d47 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -16,6 +16,7 @@ using MoonSharp.Interpreter; 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; namespace FFXIVClassic_Map_Server.Actors { @@ -646,6 +647,9 @@ namespace FFXIVClassic_Map_Server.Actors this.destinationZone = 0; this.destinationSpawnType = 0; + //Clean up parties + RemoveFromCurrentPartyAndCleanup(); + //Save Player Database.SavePlayerPlayTime(this); Database.SavePlayerPosition(this); @@ -658,6 +662,9 @@ namespace FFXIVClassic_Map_Server.Actors //Remove actor from zone and main server list zone.RemoveActorFromZone(this); + //Clean up parties + RemoveFromCurrentPartyAndCleanup(); + //Set destination this.destinationZone = destinationZone; this.destinationSpawnType = spawnType; @@ -1256,7 +1263,6 @@ namespace FFXIVClassic_Map_Server.Actors public void SendInstanceUpdate() { - Server.GetWorldManager().SeamlessCheck(this); //Update Instance @@ -1266,7 +1272,37 @@ namespace FFXIVClassic_Map_Server.Actors if (zone2 != null) aroundMe.AddRange(zone2.GetActorsAroundActor(this, 50)); playerSession.UpdateInstance(aroundMe); + } + //A party member list packet came, set the party + public void SetParty(PartyGroup group) + { + if (group is PartyGroup) + { + RemoveFromCurrentPartyAndCleanup(); + currentParty = group; + } + } + + //Removes the player from the party and cleans it up if needed + public void RemoveFromCurrentPartyAndCleanup() + { + if (currentParty == null) + return; + + for (int i = 0; i < currentParty.members.Count; i++) + { + if (currentParty.members[i].actorId == actorId) + { + currentParty.members.RemoveAt(i); + break; + } + } + + //currentParty.members.Remove(this); + if (currentParty.members.Count == 0) + Server.GetWorldManager().NoMembersInParty((PartyGroup)currentParty); + currentParty = null; } } diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs new file mode 100644 index 00000000..aa397091 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Receive/PartySyncPacket.cs @@ -0,0 +1,45 @@ +using FFXIVClassic_Map_Server.actors.group; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Receive +{ + class PartySyncPacket + { + public ulong partyGroupId; + public uint owner; + public uint[] memberActorIds; + + public bool invalidPacket = false; + + public PartySyncPacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + partyGroupId = binReader.ReadUInt64(); + uint 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(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + + } + } +}