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);
}