diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
index 393f317c..80e19018 100644
--- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
+++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
@@ -86,9 +86,13 @@
+
+
+
+
@@ -96,6 +100,9 @@
+
+
+
@@ -133,6 +140,8 @@
+
+
diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs
index de1346bf..73ba9557 100644
--- a/FFXIVClassic Map Server/PacketProcessor.cs
+++ b/FFXIVClassic Map Server/PacketProcessor.cs
@@ -24,6 +24,9 @@ using FFXIVClassic_Map_Server.dataobjects.chara;
using FFXIVClassic_Map_Server.packets.send.supportdesk;
using FFXIVClassic_Map_Server.packets.receive.social;
using FFXIVClassic_Map_Server.packets.send.social;
+using FFXIVClassic_Map_Server.packets.receive.supportdesk;
+using FFXIVClassic_Map_Server.packets.receive.recruitment;
+using FFXIVClassic_Map_Server.packets.send.recruitment;
namespace FFXIVClassic_Lobby_Server
{
@@ -338,6 +341,35 @@ namespace FFXIVClassic_Lobby_Server
case 0x012F:
subpacket.debugPrintSubPacket();
break;
+ /* RECRUITMENT */
+ //Start Recruiting
+ case 0x01C3:
+ StartRecruitingRequestPacket recruitRequestPacket = new StartRecruitingRequestPacket(subpacket.data);
+ client.queuePacket(BasePacket.createPacket(StartRecruitingResponse.buildPacket(player.actorID, true), true, false));
+ break;
+ case 0x01C7:
+ subpacket.debugPrintSubPacket();
+ RecruitmentSearchRequestPacket recruitSearchPacket = new RecruitmentSearchRequestPacket(subpacket.data);
+ break;
+ //Current Recruitment Details
+ case 0x01C8:
+ subpacket.debugPrintSubPacket();
+ //CurrentRecruitmentDetailsPacket currentRecruitDetailsPacket = new CurrentRecruitmentDetailsPacket(subpacket.data);
+ RecruitmentDetails details = new RecruitmentDetails();
+ details.recruiterName = "Localhost Character";
+ details.purposeId = 2;
+ details.locationId = 1;
+ details.subTaskId = 1;
+ details.comment = "This is a test details packet sent by the server. No implementation has been created yet...";
+ details.num[0] = 1;
+ client.queuePacket(BasePacket.createPacket(CurrentRecruitmentDetailsPacket.buildPacket(player.actorID, details), true, false));
+ break;
+ //Party Window Opened, Request State
+ case 0x01C5:
+ case 0x01C4:
+ case 0x01C6:
+ subpacket.debugPrintSubPacket();
+ break;
/* SOCIAL STUFF */
case 0x01C9:
AddRemoveSocialPacket addBlackList = new AddRemoveSocialPacket(subpacket.data);
@@ -349,7 +381,7 @@ namespace FFXIVClassic_Lobby_Server
break;
case 0x01CC:
AddRemoveSocialPacket addFriendList = new AddRemoveSocialPacket(subpacket.data);
- client.queuePacket(BasePacket.createPacket(FriendlistAddedPacket.buildPacket(player.actorID, true, (uint)10, true, addFriendList.name), true, false));
+ client.queuePacket(BasePacket.createPacket(FriendlistAddedPacket.buildPacket(player.actorID, true, (uint)addFriendList.name.GetHashCode(), true, addFriendList.name), true, false));
break;
case 0x01CD:
AddRemoveSocialPacket removeFriendList = new AddRemoveSocialPacket(subpacket.data);
@@ -360,29 +392,27 @@ namespace FFXIVClassic_Lobby_Server
break;
/* SUPPORT DESK STUFF */
//Request for FAQ/Info List
- case 0x01D0:
- subpacket.debugPrintSubPacket();
+ case 0x01D0:
+ FaqListRequestPacket faqRequest = new FaqListRequestPacket(subpacket.data);
client.queuePacket(BasePacket.createPacket(FaqListResponsePacket.buildPacket(player.actorID, new string[]{"Testing FAQ1", "Coded style!"}), true, false));
break;
//Request for body of a faq/info selection
case 0x01D1:
- client.queuePacket(BasePacket.createPacket(FaqBodyResponsePacket.buildPacket(player.actorID, "HERE IS A GIANT BODY. Nothing else to say!"), true, false));
- subpacket.debugPrintSubPacket();
+ FaqBodyRequestPacket faqBodyRequest = new FaqBodyRequestPacket(subpacket.data);
+ client.queuePacket(BasePacket.createPacket(FaqBodyResponsePacket.buildPacket(player.actorID, "HERE IS A GIANT BODY. Nothing else to say!"), true, false));
break;
//Request issue list
case 0x01D2:
- client.queuePacket(BasePacket.createPacket(IssueListResponsePacket.buildPacket(player.actorID, new string[] { "Test1", "Test2", "Test3", "Test4", "Test5"}), true, false));
- subpacket.debugPrintSubPacket();
+ GMTicketIssuesRequestPacket issuesRequest = new GMTicketIssuesRequestPacket(subpacket.data);
+ client.queuePacket(BasePacket.createPacket(IssueListResponsePacket.buildPacket(player.actorID, new string[] { "Test1", "Test2", "Test3", "Test4", "Test5"}), true, false));
break;
//Request for GM response message
case 0x01D4:
client.queuePacket(BasePacket.createPacket(GMTicketPacket.buildPacket(player.actorID, "This is a GM Ticket Title", "This is a GM Ticket Body."), true, false));
- subpacket.debugPrintSubPacket();
break;
//Request to end ticket
case 0x01D6:
client.queuePacket(BasePacket.createPacket(EndGMTicketPacket.buildPacket(player.actorID), true, false));
- subpacket.debugPrintSubPacket();
break;
default:
Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode));
diff --git a/FFXIVClassic Map Server/dataobjects/RecruitmentDetails.cs b/FFXIVClassic Map Server/dataobjects/RecruitmentDetails.cs
new file mode 100644
index 00000000..9acc647b
--- /dev/null
+++ b/FFXIVClassic Map Server/dataobjects/RecruitmentDetails.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.dataobjects
+{
+ class RecruitmentDetails
+ {
+ public string recruiterName;
+ public string comment;
+
+ public uint purposeId;
+ public uint locationId;
+ public uint subTaskId;
+ public uint timeSinceStart;
+
+ public uint[] discipleId = new uint[4];
+ public uint[] classjobId = new uint[4];
+ public byte[] minLvl = new byte[4];
+ public byte[] maxLvl = new byte[4];
+ public byte[] num = new byte[4];
+
+ }
+}
diff --git a/FFXIVClassic Map Server/dataobjects/SearchEntry.cs b/FFXIVClassic Map Server/dataobjects/SearchEntry.cs
new file mode 100644
index 00000000..ad94a040
--- /dev/null
+++ b/FFXIVClassic Map Server/dataobjects/SearchEntry.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.dataobjects
+{
+ class SearchEntry
+ {
+ public ushort preferredClass;
+ public ushort langauges;
+ public ushort location;
+ public ushort grandCompany;
+ public ushort status;
+ public ushort currentClass;
+ public string name;
+ public ushort[] classes = new ushort[2 * 20];
+ public ushort[] jobs = new ushort[8];
+
+ public void writeSearchEntry(BinaryWriter writer)
+ {
+ writer.Write((UInt16)preferredClass);
+ writer.Write((UInt16)langauges);
+ writer.Write((UInt16)location);
+ writer.Write((UInt16)grandCompany);
+ writer.Write((UInt16)status);
+ writer.Write((UInt16)currentClass);
+
+ writer.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name));
+
+ for (int i = 0; i < classes.Length; i++)
+ writer.Write((UInt16)classes[i]);
+ for (int i = 0; i < jobs.Length; i++)
+ writer.Write((UInt16)jobs[i]);
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/receive/recruitment/RecruitmentSearchRequestPacket.cs b/FFXIVClassic Map Server/packets/receive/recruitment/RecruitmentSearchRequestPacket.cs
new file mode 100644
index 00000000..07f55b10
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/receive/recruitment/RecruitmentSearchRequestPacket.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.receive.recruitment
+{
+ class RecruitmentSearchRequestPacket
+ {
+ public bool invalidPacket = false;
+
+ public uint purposeId;
+ public uint locationId;
+
+ public uint discipleId;
+ public uint classjobId;
+
+ public byte unknown1;
+ public byte unknown2;
+
+ public string text;
+
+ public RecruitmentSearchRequestPacket(byte[] data)
+ {
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryReader binReader = new BinaryReader(mem))
+ {
+ try{
+ purposeId = binReader.ReadUInt32();
+ locationId = binReader.ReadUInt32();
+ discipleId = binReader.ReadUInt32();
+ classjobId = binReader.ReadUInt32();
+
+ unknown1 = binReader.ReadByte();
+ unknown2 = binReader.ReadByte();
+
+ text = Encoding.ASCII.GetString(binReader.ReadBytes(0x20));
+ }
+ catch (Exception){
+ invalidPacket = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/receive/recruitment/StartRecruitingRequestPacket.cs b/FFXIVClassic Map Server/packets/receive/recruitment/StartRecruitingRequestPacket.cs
new file mode 100644
index 00000000..154bbb7e
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/receive/recruitment/StartRecruitingRequestPacket.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.receive.recruitment
+{
+ class StartRecruitingRequestPacket
+ {
+ public bool invalidPacket = false;
+
+ public uint purposeId;
+ public uint locationId;
+ public uint subTaskId;
+
+ public uint[] discipleId = new uint[4];
+ public uint[] classjobId = new uint[4];
+ public byte[] minLvl = new byte[4];
+ public byte[] maxLvl = new byte[4];
+ public byte[] num = new byte[4];
+
+ public string comment;
+
+ public StartRecruitingRequestPacket(byte[] data)
+ {
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryReader binReader = new BinaryReader(mem))
+ {
+ try{
+ purposeId = binReader.ReadUInt32();
+ locationId = binReader.ReadUInt32();
+ subTaskId = binReader.ReadUInt32();
+
+ for (int i = 0; i < 4; i++)
+ {
+ discipleId[i] = binReader.ReadUInt32();
+ classjobId[i] = binReader.ReadUInt32();
+ minLvl[i] = binReader.ReadByte();
+ maxLvl[i] = binReader.ReadByte();
+ num[i] = binReader.ReadByte();
+ binReader.ReadByte();
+ }
+
+ comment = Encoding.ASCII.GetString(binReader.ReadBytes(0x168));
+ }
+ catch (Exception){
+ invalidPacket = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/receive/supportdesk/FaqBodyRequestPacket.cs b/FFXIVClassic Map Server/packets/receive/supportdesk/FaqBodyRequestPacket.cs
new file mode 100644
index 00000000..bcf2a398
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/receive/supportdesk/FaqBodyRequestPacket.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.receive.supportdesk
+{
+ class FaqBodyRequestPacket
+ {
+ public bool invalidPacket = false;
+ public uint faqIndex;
+ public uint langCode;
+
+ public FaqBodyRequestPacket(byte[] data)
+ {
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryReader binReader = new BinaryReader(mem))
+ {
+ try
+ {
+ faqIndex = binReader.ReadUInt32();
+ langCode = binReader.ReadUInt32();
+ }
+ catch (Exception){
+ invalidPacket = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/receive/supportdesk/FaqListRequestPacket.cs b/FFXIVClassic Map Server/packets/receive/supportdesk/FaqListRequestPacket.cs
new file mode 100644
index 00000000..2bb1376a
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/receive/supportdesk/FaqListRequestPacket.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.receive.supportdesk
+{
+ class FaqListRequestPacket
+ {
+ public bool invalidPacket = false;
+ public uint langCode;
+ public uint unknown;
+
+ public FaqListRequestPacket(byte[] data)
+ {
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryReader binReader = new BinaryReader(mem))
+ {
+ try{
+ langCode = binReader.ReadUInt32();
+ unknown = binReader.ReadUInt32();
+ }
+ catch (Exception){
+ invalidPacket = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/receive/supportdesk/GMTicketIssuesRequestPacket.cs b/FFXIVClassic Map Server/packets/receive/supportdesk/GMTicketIssuesRequestPacket.cs
new file mode 100644
index 00000000..e3943c90
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/receive/supportdesk/GMTicketIssuesRequestPacket.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.receive.supportdesk
+{
+ class GMTicketIssuesRequestPacket
+ {
+ public bool invalidPacket = false;
+ public uint langCode;
+
+ public GMTicketIssuesRequestPacket(byte[] data)
+ {
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryReader binReader = new BinaryReader(mem))
+ {
+ try{
+ langCode = binReader.ReadUInt32();
+ }
+ catch (Exception){
+ invalidPacket = true;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/send/recruitment/CurrentRecruitmentDetailsPacket.cs b/FFXIVClassic Map Server/packets/send/recruitment/CurrentRecruitmentDetailsPacket.cs
new file mode 100644
index 00000000..fb2ca6cf
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/send/recruitment/CurrentRecruitmentDetailsPacket.cs
@@ -0,0 +1,56 @@
+using FFXIVClassic_Lobby_Server.packets;
+using FFXIVClassic_Map_Server.dataobjects;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.send.recruitment
+{
+ class CurrentRecruitmentDetailsPacket
+ {
+ public const ushort OPCODE = 0x01C8;
+ public const uint PACKET_SIZE = 0x218;
+
+ public static SubPacket buildPacket(uint playerActorID, RecruitmentDetails details)
+ {
+ byte[] data = new byte[PACKET_SIZE - 0x20];
+
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryWriter binWriter = new BinaryWriter(mem))
+ {
+ if (details == null)
+ {
+ return new SubPacket(OPCODE, playerActorID, playerActorID, data);
+ }
+
+ binWriter.Write((UInt32)details.purposeId);
+ binWriter.Write((UInt32)details.locationId);
+ binWriter.Write((UInt32)details.subTaskId);
+ binWriter.Write((UInt32)details.timeSinceStart);
+
+ for (int i = 0; i < 4; i++)
+ {
+ binWriter.Write((UInt32)details.discipleId[i]);
+ binWriter.Write((UInt32)details.classjobId[i]);
+ binWriter.Write((byte)details.minLvl[i]);
+ binWriter.Write((byte)details.maxLvl[i]);
+ binWriter.Write((byte)details.num[i]);
+ binWriter.Write((byte)0);
+ }
+
+ binWriter.Write(Encoding.ASCII.GetBytes(details.comment), 0, Encoding.ASCII.GetByteCount(details.comment) >= 0x168 ? 0x168 : Encoding.ASCII.GetByteCount(details.comment));
+ binWriter.Seek(0x1C0, SeekOrigin.Begin);
+ binWriter.Write(Encoding.ASCII.GetBytes(details.recruiterName), 0, Encoding.ASCII.GetByteCount(details.recruiterName) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(details.recruiterName));
+ binWriter.Seek(0x1E0, SeekOrigin.Begin);
+ binWriter.Write((byte)1);
+ }
+ }
+
+ return new SubPacket(OPCODE, playerActorID, playerActorID, data);
+ }
+ }
+}
diff --git a/FFXIVClassic Map Server/packets/send/recruitment/StartRecruitingResponse.cs b/FFXIVClassic Map Server/packets/send/recruitment/StartRecruitingResponse.cs
new file mode 100644
index 00000000..829b9ce2
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/send/recruitment/StartRecruitingResponse.cs
@@ -0,0 +1,24 @@
+using FFXIVClassic_Lobby_Server.packets;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FFXIVClassic_Map_Server.packets.send.recruitment
+{
+ class StartRecruitingResponse
+ {
+ public const ushort OPCODE = 0x01C3;
+ public const uint PACKET_SIZE = 0x28;
+
+ public static SubPacket buildPacket(uint playerActorID, bool success)
+ {
+ byte[] data = new byte[PACKET_SIZE - 0x20];
+
+ data[0] = (byte)(success ? 0x1 : 0x0);
+
+ return new SubPacket(OPCODE, playerActorID, playerActorID, data);
+ }
+ }
+}