diff --git a/FFXIVClassic Common Class Lib/BasePacket.cs b/FFXIVClassic Common Class Lib/BasePacket.cs index a6fb2a89..9f9a4c4c 100644 --- a/FFXIVClassic Common Class Lib/BasePacket.cs +++ b/FFXIVClassic Common Class Lib/BasePacket.cs @@ -295,6 +295,41 @@ namespace FFXIVClassic.Common return packet; } + /// + /// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null. + /// + /// Current offset in buffer. + /// Incoming buffer. + /// Returns either a BasePacket or null if not enough data. + public static BasePacket CreatePacket(ref int offset, byte[] buffer, int bytesRead) + { + BasePacket newPacket = null; + + //Too small to even get length + if (bytesRead <= offset) + return null; + + ushort packetSize = BitConverter.ToUInt16(buffer, offset); + + //Too small to whole packet + if (bytesRead < offset + packetSize) + return null; + + if (buffer.Length < offset + packetSize) + return null; + + try + { + newPacket = new BasePacket(buffer, ref offset); + } + catch (OverflowException) + { + return null; + } + + return newPacket; + } + public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet) { var data = packet.data; diff --git a/FFXIVClassic Common Class Lib/SubPacket.cs b/FFXIVClassic Common Class Lib/SubPacket.cs index 6cd3ff27..38394e29 100644 --- a/FFXIVClassic Common Class Lib/SubPacket.cs +++ b/FFXIVClassic Common Class Lib/SubPacket.cs @@ -153,6 +153,41 @@ namespace FFXIVClassic.Common return outBytes; } + /// + /// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null. + /// + /// Current offset in buffer. + /// Incoming buffer. + /// Returns either a BasePacket or null if not enough data. + public static SubPacket CreatePacket(ref int offset, byte[] buffer, int bytesRead) + { + SubPacket newPacket = null; + + //Too small to even get length + if (bytesRead <= offset) + return null; + + ushort packetSize = BitConverter.ToUInt16(buffer, offset); + + //Too small to whole packet + if (bytesRead < offset + packetSize) + return null; + + if (buffer.Length < offset + packetSize) + return null; + + try + { + newPacket = new SubPacket(buffer, ref offset); + } + catch (OverflowException) + { + return null; + } + + return newPacket; + } + public void DebugPrintSubPacket() { #if DEBUG @@ -169,6 +204,9 @@ namespace FFXIVClassic.Common logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE), ConsoleOutputColor.DarkMagenta); } + else + logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE), + ConsoleOutputColor.DarkMagenta); #endif } } diff --git a/FFXIVClassic Map Server/CommandProcessor.cs b/FFXIVClassic Map Server/CommandProcessor.cs index 3936a2b7..af5860ed 100644 --- a/FFXIVClassic Map Server/CommandProcessor.cs +++ b/FFXIVClassic Map Server/CommandProcessor.cs @@ -88,7 +88,7 @@ namespace FFXIVClassic_Map_Server if (cmd.Any()) { // if client isnt null, take player to be the player actor - var player = client?.GetActor(); + var player = client.GetActor(); if (cmd.Equals("help")) { diff --git a/FFXIVClassic Map Server/ConfigConstants.cs b/FFXIVClassic Map Server/ConfigConstants.cs index cf4006e6..c3ea8d54 100644 --- a/FFXIVClassic Map Server/ConfigConstants.cs +++ b/FFXIVClassic Map Server/ConfigConstants.cs @@ -30,7 +30,7 @@ namespace FFXIVClassic_Map_Server INIFile configIni = new INIFile("./map_config.ini"); ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1"); - ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54992"); + ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "1989"); ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true"); ConfigConstants.DATABASE_WORLDID = UInt32.Parse(configIni.GetValue("Database", "worldid", "0")); diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index d71ab75b..ff2afdbc 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -91,7 +91,7 @@ - + diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index a6673c93..1fb84ba4 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -26,393 +26,283 @@ namespace FFXIVClassic_Map_Server Server mServer; CommandProcessor cp; Dictionary mPlayers; - List mConnections; - public PacketProcessor(Server server, Dictionary playerList, List connectionList) + public PacketProcessor(Server server, Dictionary playerList) { mPlayers = playerList; - mConnections = connectionList; mServer = server; cp = new CommandProcessor(playerList); } - public void ProcessPacket(ClientConnection client, BasePacket packet) + public void ProcessPacket(ZoneConnection client, SubPacket subpacket) { - if (packet.header.isCompressed == 0x01) - BasePacket.DecryptPacket(client.blowfish, ref packet); + + ConnectedPlayer player = null; - List subPackets = packet.GetSubpackets(); - foreach (SubPacket subpacket in subPackets) - { - if (subpacket.header.type == 0x01) - { - packet.DebugPrintPacket(); - byte[] reply1Data = { - 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFD, 0xFF, 0xFF, - 0xE5, 0x6E, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x0 - }; + if(mPlayers.ContainsKey(subpacket.header.targetId)) + player = mPlayers[subpacket.header.targetId]; - byte[] reply2Data = { - 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x2B, 0x5F, 0x26, - 0x66, 0x00, 0x00, 0x00, 0xC8, 0xD6, 0xAF, 0x2B, 0x38, 0x2B, 0x5F, 0x26, 0xB8, 0x8D, 0xF0, 0x2B, - 0xC8, 0xFD, 0x85, 0xFE, 0xA8, 0x7C, 0x5B, 0x09, 0x38, 0x2B, 0x5F, 0x26, 0xC8, 0xD6, 0xAF, 0x2B, - 0xB8, 0x8D, 0xF0, 0x2B, 0x88, 0xAF, 0x5E, 0x26 - }; - - BasePacket reply1 = new BasePacket(reply1Data); - BasePacket reply2 = new BasePacket(reply2Data); - - //Write Timestamp into Reply1 - using (MemoryStream mem = new MemoryStream(reply1.data)) - { - using (BinaryWriter binReader = new BinaryWriter(mem)) - { - binReader.BaseStream.Seek(0x14, SeekOrigin.Begin); - binReader.Write((UInt32)Utils.UnixTimeStampUTC()); - } - } - - //Read in Actor Id that owns this connection - uint actorID = 0; - using (MemoryStream mem = new MemoryStream(packet.data)) - { - using (BinaryReader binReader = new BinaryReader(mem)) - { - try - { - byte[] readIn = new byte[12]; - binReader.BaseStream.Seek(0x14, SeekOrigin.Begin); - binReader.Read(readIn, 0, 12); - actorID = UInt32.Parse(Encoding.ASCII.GetString(readIn)); - } - catch (Exception) - { } - } - } - - //Should never happen.... unless actor id IS 0! - if (actorID == 0) - break; - - client.owner = actorID; - - //Write Actor ID into reply2 - using (MemoryStream mem = new MemoryStream(reply2.data)) - { - using (BinaryWriter binReader = new BinaryWriter(mem)) - { - binReader.BaseStream.Seek(0x10, SeekOrigin.Begin); - binReader.Write(actorID); - } - } - - ConnectedPlayer player = null; - - if (packet.header.connectionType == BasePacket.TYPE_ZONE) - { - while (mPlayers != null && !mPlayers.ContainsKey(client.owner)) - { } - player = mPlayers[client.owner]; - } - - //Create connected player if not Created - if (player == null) - { - player = new ConnectedPlayer(actorID); - mPlayers[actorID] = player; - } - - player.SetConnection(packet.header.connectionType, client); - - if (packet.header.connectionType == BasePacket.TYPE_ZONE) - Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "zone", actorID, client.GetAddress()); - else if (packet.header.connectionType == BasePacket.TYPE_CHAT) - Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "chat", actorID, client.GetAddress()); - - //Create player actor - reply1.DebugPrintPacket(); - client.QueuePacket(reply1); - client.QueuePacket(reply2); - break; - } - else if (subpacket.header.type == 0x07) - { - BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x08); - //client.QueuePacket(init); - } - else if (subpacket.header.type == 0x08) + if (player == null) { - //Response, client's current [actorID][time] - //BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x07); - //client.QueuePacket(init); - packet.DebugPrintPacket(); - } - else if (subpacket.header.type == 0x03) + player = new ConnectedPlayer(client, subpacket.header.targetId); + } + + subpacket.DebugPrintSubPacket(); + + //Normal Game Opcode + switch (subpacket.gameMessage.opcode) { - ConnectedPlayer player = null; + //Ping + case 0x0001: + //subpacket.DebugPrintSubPacket(); + PingPacket pingPacket = new PingPacket(subpacket.data); + client.QueuePacket(BasePacket.CreatePacket(PongPacket.BuildPacket(player.actorID, pingPacket.time), true, false)); + player.Ping(); + break; + //Unknown + case 0x0002: - if(mPlayers.ContainsKey(client.owner)) - player = mPlayers[client.owner]; + subpacket.DebugPrintSubPacket(); + + player = new ConnectedPlayer(client, subpacket.header.targetId); + mPlayers[subpacket.header.targetId] = player; + + client.QueuePacket(_0x2Packet.BuildPacket(player.actorID), true, false); - if (player == null || !player.IsClientConnectionsReady()) - return; + Server.GetWorldManager().DoLogin(player.GetActor()); - //Normal Game Opcode - switch (subpacket.gameMessage.opcode) - { - //Ping - case 0x0001: - //subpacket.DebugPrintSubPacket(); - PingPacket pingPacket = new PingPacket(subpacket.data); - client.QueuePacket(BasePacket.CreatePacket(PongPacket.BuildPacket(player.actorID, pingPacket.time), true, false)); - player.Ping(); - break; - //Unknown - case 0x0002: + break; + //Chat Received + case 0x0003: + ChatMessagePacket chatMessage = new ChatMessagePacket(subpacket.data); + Program.Log.Info("Got type-{5} message: {0} @ {1}, {2}, {3}, Rot: {4}", chatMessage.message, chatMessage.posX, chatMessage.posY, chatMessage.posZ, chatMessage.posRot, chatMessage.logType); + subpacket.DebugPrintSubPacket(); - subpacket.DebugPrintSubPacket(); - client.QueuePacket(_0x2Packet.BuildPacket(player.actorID), true, false); + if (chatMessage.message.StartsWith("!")) + { + if (cp.DoCommand(chatMessage.message, player)) + return; ; + } - Server.GetWorldManager().DoLogin(player.GetActor()); + player.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(player.actorID, player.actorID, chatMessage.logType, player.GetActor().customDisplayName, chatMessage.message), false); - - break; - //Chat Received - case 0x0003: - ChatMessagePacket chatMessage = new ChatMessagePacket(subpacket.data); - Program.Log.Info("Got type-{5} message: {0} @ {1}, {2}, {3}, Rot: {4}", chatMessage.message, chatMessage.posX, chatMessage.posY, chatMessage.posZ, chatMessage.posRot, chatMessage.logType); - subpacket.DebugPrintSubPacket(); - - if (chatMessage.message.StartsWith("!")) - { - if (cp.DoCommand(chatMessage.message, player)) - continue; - } - - player.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(player.actorID, player.actorID, chatMessage.logType, player.GetActor().customDisplayName, chatMessage.message), false); - - break; - //Langauge Code - case 0x0006: - LangaugeCodePacket langCode = new LangaugeCodePacket(subpacket.data); - player.languageCode = langCode.languageCode; - break; - //Unknown - Happens a lot at login, then once every time player zones - case 0x0007: - //subpacket.DebugPrintSubPacket(); - _0x07Packet unknown07 = new _0x07Packet(subpacket.data); - break; + break; + //Langauge Code + case 0x0006: + LangaugeCodePacket langCode = new LangaugeCodePacket(subpacket.data); + player.languageCode = langCode.languageCode; + break; + //Unknown - Happens a lot at login, then once every time player zones + case 0x0007: + //subpacket.DebugPrintSubPacket(); + _0x07Packet unknown07 = new _0x07Packet(subpacket.data); + break; + //Update Position + case 0x00CA: //Update Position - case 0x00CA: - //Update Position - //subpacket.DebugPrintSubPacket(); - UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); - player.UpdatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); - player.GetActor().SendInstanceUpdate(); + //subpacket.DebugPrintSubPacket(); + UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); + player.UpdatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); + player.GetActor().SendInstanceUpdate(); + + if (player.GetActor().IsInZoneChange()) + player.GetActor().SetZoneChanging(false); + + break; + //Set Target + case 0x00CD: + //subpacket.DebugPrintSubPacket(); + + SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); + player.GetActor().currentTarget = setTarget.actorID; + player.GetActor().BroadcastPacket(SetActorTargetAnimatedPacket.BuildPacket(player.actorID, player.actorID, setTarget.actorID), true); + break; + //Lock Target + case 0x00CC: + LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); + player.GetActor().currentLockedTarget = lockTarget.actorID; + break; + //Start Event + case 0x012D: + subpacket.DebugPrintSubPacket(); + EventStartPacket eventStart = new EventStartPacket(subpacket.data); + + /* + if (eventStart.error != null) + { + player.errorMessage += eventStart.error; + + if (eventStart.errorIndex == eventStart.errorNum - 1) + Program.Log.Error("\n"+player.errorMessage); - if (player.GetActor().IsInZoneChange()) - player.GetActor().SetZoneChanging(false); break; - //Set Target - case 0x00CD: - //subpacket.DebugPrintSubPacket(); + } + */ - SetTargetPacket setTarget = new SetTargetPacket(subpacket.data); - player.GetActor().currentTarget = setTarget.actorID; - player.GetActor().BroadcastPacket(SetActorTargetAnimatedPacket.BuildPacket(player.actorID, player.actorID, setTarget.actorID), true); - break; - //Lock Target - case 0x00CC: - LockTargetPacket lockTarget = new LockTargetPacket(subpacket.data); - player.GetActor().currentLockedTarget = lockTarget.actorID; - break; - //Start Event - case 0x012D: - subpacket.DebugPrintSubPacket(); - EventStartPacket eventStart = new EventStartPacket(subpacket.data); - - /* - if (eventStart.error != null) - { - player.errorMessage += eventStart.error; - - if (eventStart.errorIndex == eventStart.errorNum - 1) - Program.Log.Error("\n"+player.errorMessage); - - - break; - } - */ - - Actor ownerActor = Server.GetStaticActors(eventStart.scriptOwnerActorID); + Actor ownerActor = Server.GetStaticActors(eventStart.scriptOwnerActorID); - player.GetActor().currentEventOwner = eventStart.scriptOwnerActorID; - player.GetActor().currentEventName = eventStart.triggerName; + player.GetActor().currentEventOwner = eventStart.scriptOwnerActorID; + player.GetActor().currentEventName = eventStart.triggerName; + if (ownerActor == null) + { + //Is it a instance actor? + ownerActor = Server.GetWorldManager().GetActorInWorld(player.GetActor().currentEventOwner); if (ownerActor == null) { - //Is it a instance actor? - ownerActor = Server.GetWorldManager().GetActorInWorld(player.GetActor().currentEventOwner); - if (ownerActor == null) - { - //Is it a Director? - if (player.GetActor().currentDirector != null && player.GetActor().currentEventOwner == player.GetActor().currentDirector.actorId) - ownerActor = player.GetActor().currentDirector; - else - { - Program.Log.Debug("\n===Event START===\nCould not find actor 0x{0:X} for event started by caller: 0x{1:X}\nEvent Starter: {2}\nParams: {3}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.triggerName, LuaUtils.DumpParams(eventStart.luaParams)); - break; - } - } - } - - player.GetActor().StartEvent(ownerActor, eventStart); - - Program.Log.Debug("\n===Event START===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nEvent Starter: {4}\nParams: {5}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.val1, eventStart.val2, eventStart.triggerName, LuaUtils.DumpParams(eventStart.luaParams)); - break; - //Unknown, happens at npc spawn and cutscene play???? - case 0x00CE: - break; - //Event Result - case 0x012E: - subpacket.DebugPrintSubPacket(); - EventUpdatePacket eventUpdate = new EventUpdatePacket(subpacket.data); - Program.Log.Debug("\n===Event UPDATE===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nStep: 0x{4:X}\nParams: {5}", eventUpdate.actorID, eventUpdate.scriptOwnerActorID, eventUpdate.val1, eventUpdate.val2, eventUpdate.step, LuaUtils.DumpParams(eventUpdate.luaParams)); - /* - //Is it a static actor? If not look in the player's instance - Actor updateOwnerActor = Server.GetStaticActors(player.GetActor().currentEventOwner); - if (updateOwnerActor == null) - { - updateOwnerActor = Server.GetWorldManager().GetActorInWorld(player.GetActor().currentEventOwner); - + //Is it a Director? if (player.GetActor().currentDirector != null && player.GetActor().currentEventOwner == player.GetActor().currentDirector.actorId) - updateOwnerActor = player.GetActor().currentDirector; - - if (updateOwnerActor == null) + ownerActor = player.GetActor().currentDirector; + else + { + Program.Log.Debug("\n===Event START===\nCould not find actor 0x{0:X} for event started by caller: 0x{1:X}\nEvent Starter: {2}\nParams: {3}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.triggerName, LuaUtils.DumpParams(eventStart.luaParams)); break; - } - */ - player.GetActor().UpdateEvent(eventUpdate); + } + } + } - //LuaEngine.DoActorOnEventUpdated(player.GetActor(), updateOwnerActor, eventUpdate); + player.GetActor().StartEvent(ownerActor, eventStart); + + Program.Log.Debug("\n===Event START===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nEvent Starter: {4}\nParams: {5}", eventStart.actorID, eventStart.scriptOwnerActorID, eventStart.val1, eventStart.val2, eventStart.triggerName, LuaUtils.DumpParams(eventStart.luaParams)); + break; + //Unknown, happens at npc spawn and cutscene play???? + case 0x00CE: + break; + //Event Result + case 0x012E: + subpacket.DebugPrintSubPacket(); + EventUpdatePacket eventUpdate = new EventUpdatePacket(subpacket.data); + Program.Log.Debug("\n===Event UPDATE===\nSource Actor: 0x{0:X}\nCaller Actor: 0x{1:X}\nVal1: 0x{2:X}\nVal2: 0x{3:X}\nStep: 0x{4:X}\nParams: {5}", eventUpdate.actorID, eventUpdate.scriptOwnerActorID, eventUpdate.val1, eventUpdate.val2, eventUpdate.step, LuaUtils.DumpParams(eventUpdate.luaParams)); + /* + //Is it a static actor? If not look in the player's instance + Actor updateOwnerActor = Server.GetStaticActors(player.GetActor().currentEventOwner); + if (updateOwnerActor == null) + { + updateOwnerActor = Server.GetWorldManager().GetActorInWorld(player.GetActor().currentEventOwner); + + if (player.GetActor().currentDirector != null && player.GetActor().currentEventOwner == player.GetActor().currentDirector.actorId) + updateOwnerActor = player.GetActor().currentDirector; + + if (updateOwnerActor == null) + break; + } + */ + player.GetActor().UpdateEvent(eventUpdate); + + //LuaEngine.DoActorOnEventUpdated(player.GetActor(), updateOwnerActor, eventUpdate); - break; - case 0x012F: - //subpacket.DebugPrintSubPacket(); - ParameterDataRequestPacket paramRequest = new ParameterDataRequestPacket(subpacket.data); - if (paramRequest.paramName.Equals("charaWork/exp")) - player.GetActor().SendCharaExpInfo(); - break; - /* RECRUITMENT */ - //Start Recruiting - case 0x01C3: - StartRecruitingRequestPacket recruitRequestPacket = new StartRecruitingRequestPacket(subpacket.data); - client.QueuePacket(BasePacket.CreatePacket(StartRecruitingResponse.BuildPacket(player.actorID, true), true, false)); - break; - //End Recruiting - case 0x01C4: - client.QueuePacket(BasePacket.CreatePacket(EndRecruitmentPacket.BuildPacket(player.actorID), true, false)); - break; - //Party Window Opened, Request State - case 0x01C5: - client.QueuePacket(BasePacket.CreatePacket(RecruiterStatePacket.BuildPacket(player.actorID, true, true, 1), true, false)); - break; - //Search Recruiting - case 0x01C7: - RecruitmentSearchRequestPacket recruitSearchPacket = new RecruitmentSearchRequestPacket(subpacket.data); - break; - //Get Recruitment Details - case 0x01C8: - RecruitmentDetailsRequestPacket currentRecruitDetailsPacket = new RecruitmentDetailsRequestPacket(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; - //Accepted Recruiting - case 0x01C6: - subpacket.DebugPrintSubPacket(); - break; - /* SOCIAL STUFF */ - case 0x01C9: - AddRemoveSocialPacket addBlackList = new AddRemoveSocialPacket(subpacket.data); - client.QueuePacket(BasePacket.CreatePacket(BlacklistAddedPacket.BuildPacket(player.actorID, true, addBlackList.name), true, false)); - break; - case 0x01CA: - AddRemoveSocialPacket RemoveBlackList = new AddRemoveSocialPacket(subpacket.data); - client.QueuePacket(BasePacket.CreatePacket(BlacklistRemovedPacket.BuildPacket(player.actorID, true, RemoveBlackList.name), true, false)); - break; - case 0x01CB: - int offset1 = 0; - client.QueuePacket(BasePacket.CreatePacket(SendBlacklistPacket.BuildPacket(player.actorID, new String[] { "Test" }, ref offset1), true, false)); - break; - case 0x01CC: - AddRemoveSocialPacket addFriendList = new AddRemoveSocialPacket(subpacket.data); - 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); - client.QueuePacket(BasePacket.CreatePacket(FriendlistRemovedPacket.BuildPacket(player.actorID, true, RemoveFriendList.name), true, false)); - break; - case 0x01CE: - int offset2 = 0; - client.QueuePacket(BasePacket.CreatePacket(SendFriendlistPacket.BuildPacket(player.actorID, new Tuple[] { new Tuple(01, "Test2") }, ref offset2), true, false)); - break; - case 0x01CF: - client.QueuePacket(BasePacket.CreatePacket(FriendStatusPacket.BuildPacket(player.actorID, null), true, false)); - break; - /* SUPPORT DESK STUFF */ - //Request for FAQ/Info List - 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: - 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: - 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 if GM ticket exists - case 0x01D3: - client.QueuePacket(BasePacket.CreatePacket(StartGMTicketPacket.BuildPacket(player.actorID, false), 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)); - break; - //GM Ticket Sent - case 0x01D5: - GMSupportTicketPacket gmTicket = new GMSupportTicketPacket(subpacket.data); - Program.Log.Info("Got GM Ticket: \n" + gmTicket.ticketTitle + "\n" + gmTicket.ticketBody); - client.QueuePacket(BasePacket.CreatePacket(GMTicketSentResponsePacket.BuildPacket(player.actorID, true), true, false)); - break; - //Request to end ticket - case 0x01D6: - client.QueuePacket(BasePacket.CreatePacket(EndGMTicketPacket.BuildPacket(player.actorID), true, false)); - break; - default: - Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode); - subpacket.DebugPrintSubPacket(); - break; - } + break; + case 0x012F: + //subpacket.DebugPrintSubPacket(); + ParameterDataRequestPacket paramRequest = new ParameterDataRequestPacket(subpacket.data); + if (paramRequest.paramName.Equals("charaWork/exp")) + player.GetActor().SendCharaExpInfo(); + break; + /* RECRUITMENT */ + //Start Recruiting + case 0x01C3: + StartRecruitingRequestPacket recruitRequestPacket = new StartRecruitingRequestPacket(subpacket.data); + client.QueuePacket(BasePacket.CreatePacket(StartRecruitingResponse.BuildPacket(player.actorID, true), true, false)); + break; + //End Recruiting + case 0x01C4: + client.QueuePacket(BasePacket.CreatePacket(EndRecruitmentPacket.BuildPacket(player.actorID), true, false)); + break; + //Party Window Opened, Request State + case 0x01C5: + client.QueuePacket(BasePacket.CreatePacket(RecruiterStatePacket.BuildPacket(player.actorID, true, true, 1), true, false)); + break; + //Search Recruiting + case 0x01C7: + RecruitmentSearchRequestPacket recruitSearchPacket = new RecruitmentSearchRequestPacket(subpacket.data); + break; + //Get Recruitment Details + case 0x01C8: + RecruitmentDetailsRequestPacket currentRecruitDetailsPacket = new RecruitmentDetailsRequestPacket(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; + //Accepted Recruiting + case 0x01C6: + subpacket.DebugPrintSubPacket(); + break; + /* SOCIAL STUFF */ + case 0x01C9: + AddRemoveSocialPacket addBlackList = new AddRemoveSocialPacket(subpacket.data); + client.QueuePacket(BasePacket.CreatePacket(BlacklistAddedPacket.BuildPacket(player.actorID, true, addBlackList.name), true, false)); + break; + case 0x01CA: + AddRemoveSocialPacket RemoveBlackList = new AddRemoveSocialPacket(subpacket.data); + client.QueuePacket(BasePacket.CreatePacket(BlacklistRemovedPacket.BuildPacket(player.actorID, true, RemoveBlackList.name), true, false)); + break; + case 0x01CB: + int offset1 = 0; + client.QueuePacket(BasePacket.CreatePacket(SendBlacklistPacket.BuildPacket(player.actorID, new String[] { "Test" }, ref offset1), true, false)); + break; + case 0x01CC: + AddRemoveSocialPacket addFriendList = new AddRemoveSocialPacket(subpacket.data); + 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); + client.QueuePacket(BasePacket.CreatePacket(FriendlistRemovedPacket.BuildPacket(player.actorID, true, RemoveFriendList.name), true, false)); + break; + case 0x01CE: + int offset2 = 0; + client.QueuePacket(BasePacket.CreatePacket(SendFriendlistPacket.BuildPacket(player.actorID, new Tuple[] { new Tuple(01, "Test2") }, ref offset2), true, false)); + break; + case 0x01CF: + client.QueuePacket(BasePacket.CreatePacket(FriendStatusPacket.BuildPacket(player.actorID, null), true, false)); + break; + /* SUPPORT DESK STUFF */ + //Request for FAQ/Info List + 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: + 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: + 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 if GM ticket exists + case 0x01D3: + client.QueuePacket(BasePacket.CreatePacket(StartGMTicketPacket.BuildPacket(player.actorID, false), 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)); + break; + //GM Ticket Sent + case 0x01D5: + GMSupportTicketPacket gmTicket = new GMSupportTicketPacket(subpacket.data); + Program.Log.Info("Got GM Ticket: \n" + gmTicket.ticketTitle + "\n" + gmTicket.ticketBody); + client.QueuePacket(BasePacket.CreatePacket(GMTicketSentResponsePacket.BuildPacket(player.actorID, true), true, false)); + break; + //Request to end ticket + case 0x01D6: + client.QueuePacket(BasePacket.CreatePacket(EndGMTicketPacket.BuildPacket(player.actorID), true, false)); + break; + default: + Program.Log.Debug("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode); + subpacket.DebugPrintSubPacket(); + break; } - else - packet.DebugPrintPacket(); - } + } } diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index ca939e63..f8b4ece3 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -26,7 +26,7 @@ namespace FFXIVClassic_Map_Server private Socket mServerSocket; private Dictionary mConnectedPlayerList = new Dictionary(); - private List mConnectionList = new List(); + private ZoneConnection mWorldConnection = new ZoneConnection(); private LuaEngine mLuaEngine = new LuaEngine(); private static WorldManager mWorldManager; @@ -119,7 +119,7 @@ namespace FFXIVClassic_Map_Server Program.Log.Info("Map Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); Console.ForegroundColor = ConsoleColor.Gray; - mProcessor = new PacketProcessor(this, mConnectedPlayerList, mConnectionList); + mProcessor = new PacketProcessor(this, mConnectedPlayerList); //mGameThread = new Thread(new ThreadStart(mProcessor.update)); //mGameThread.Start(); @@ -138,21 +138,18 @@ namespace FFXIVClassic_Map_Server #region Socket Handling private void AcceptCallback(IAsyncResult result) { - ClientConnection conn = null; + ZoneConnection conn = null; Socket socket = (System.Net.Sockets.Socket)result.AsyncState; try { - conn = new ClientConnection(); + conn = new ZoneConnection(); conn.socket = socket.EndAccept(result); conn.buffer = new byte[BUFFER_SIZE]; - lock (mConnectionList) - { - mConnectionList.Add(conn); - } - + mWorldConnection = conn; + Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port); //Queue recieving of data from the connection conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn); @@ -163,11 +160,7 @@ namespace FFXIVClassic_Map_Server { if (conn != null) { - - lock (mConnectionList) - { - mConnectionList.Remove(conn); - } + mWorldConnection = null; } mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket); } @@ -175,10 +168,7 @@ namespace FFXIVClassic_Map_Server { if (conn != null) { - lock (mConnectionList) - { - mConnectionList.Remove(conn); - } + mWorldConnection = null; } mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket); } @@ -208,20 +198,13 @@ namespace FFXIVClassic_Map_Server /// private void ReceiveCallback(IAsyncResult result) { - ClientConnection conn = (ClientConnection)result.AsyncState; + ZoneConnection conn = (ZoneConnection)result.AsyncState; //Check if disconnected if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0)) { - if (mConnectedPlayerList.ContainsKey(conn.owner)) - mConnectedPlayerList.Remove(conn.owner); - lock (mConnectionList) - { - mConnectionList.Remove(conn); - } - if (conn.connType == BasePacket.TYPE_ZONE) - Program.Log.Info("{0} has disconnected.", conn.owner == 0 ? conn.GetAddress() : "User " + conn.owner); - return; + mWorldConnection = null; + Program.Log.Info("Disconnected from world server!"); } try @@ -237,13 +220,13 @@ namespace FFXIVClassic_Map_Server //Build packets until can no longer or out of data while (true) { - BasePacket basePacket = BuildPacket(ref offset, conn.buffer, bytesRead); + SubPacket subPacket = SubPacket.CreatePacket(ref offset, conn.buffer, bytesRead); //If can't build packet, break, else process another - if (basePacket == null) + if (subPacket == null) break; else - mProcessor.ProcessPacket(conn, basePacket); + mProcessor.ProcessPacket(conn, subPacket); } //Not all bytes consumed, transfer leftover to beginning @@ -264,63 +247,20 @@ namespace FFXIVClassic_Map_Server } else { - Program.Log.Info("{0} has disconnected.", conn.owner == 0 ? conn.GetAddress() : "User " + conn.owner); - - lock (mConnectionList) - { - mConnectionList.Remove(conn); - } + mWorldConnection = null; + Program.Log.Info("Disconnected from world server!"); } } catch (SocketException) { if (conn.socket != null) { - Program.Log.Info("{0} has disconnected.", conn.owner == 0 ? conn.GetAddress() : "User " + conn.owner); - - lock (mConnectionList) - { - mConnectionList.Remove(conn); - } + mWorldConnection = null; + Program.Log.Info("Disconnected from world server!"); } } } - /// - /// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null. - /// - /// Current offset in buffer. - /// Incoming buffer. - /// Returns either a BasePacket or null if not enough data. - public BasePacket BuildPacket(ref int offset, byte[] buffer, int bytesRead) - { - BasePacket newPacket = null; - - //Too small to even get length - if (bytesRead <= offset) - return null; - - ushort packetSize = BitConverter.ToUInt16(buffer, offset); - - //Too small to whole packet - if (bytesRead < offset + packetSize) - return null; - - if (buffer.Length < offset + packetSize) - return null; - - try - { - newPacket = new BasePacket(buffer, ref offset); - } - catch (OverflowException) - { - return null; - } - - return newPacket; - } - #endregion diff --git a/FFXIVClassic Map Server/ClientConnection.cs b/FFXIVClassic Map Server/ZoneConnection.cs similarity index 72% rename from FFXIVClassic Map Server/ClientConnection.cs rename to FFXIVClassic Map Server/ZoneConnection.cs index fcae92c6..4a1af12d 100644 --- a/FFXIVClassic Map Server/ClientConnection.cs +++ b/FFXIVClassic Map Server/ZoneConnection.cs @@ -7,27 +7,22 @@ using System.Net; namespace FFXIVClassic_Map_Server { - class ClientConnection + class ZoneConnection { //Connection stuff - public Blowfish blowfish; public Socket socket; public byte[] buffer; - private BlockingCollection SendPacketQueue = new BlockingCollection(1000); + private BlockingCollection SendPacketQueue = new BlockingCollection(1000); public int lastPartialSize = 0; - //Instance Stuff - public uint owner = 0; - public int connType = 0; - public void QueuePacket(BasePacket packet) { - SendPacketQueue.Add(packet); + //SendPacketQueue.Add(packet); } public void QueuePacket(SubPacket subpacket, bool isAuthed, bool isEncrypted) { - SendPacketQueue.Add(BasePacket.CreatePacket(subpacket, isAuthed, isEncrypted)); + SendPacketQueue.Add(subpacket); } public void FlushQueuedSendPackets() @@ -37,9 +32,9 @@ namespace FFXIVClassic_Map_Server while (SendPacketQueue.Count > 0) { - BasePacket packet = SendPacketQueue.Take(); + SubPacket packet = SendPacketQueue.Take(); - byte[] packetBytes = packet.GetPacketBytes(); + byte[] packetBytes = packet.GetBytes(); try { diff --git a/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs b/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs index 052bec4e..17addb4b 100644 --- a/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs +++ b/FFXIVClassic Map Server/dataobjects/ConnectedPlayer.cs @@ -20,50 +20,20 @@ namespace FFXIVClassic_Map_Server.dataobjects public uint languageCode = 1; - private ClientConnection zoneConnection; - private ClientConnection chatConnection; + private ZoneConnection zoneConnection; private uint lastPingPacket = Utils.UnixTimeStampUTC(); public string errorMessage = ""; - public ConnectedPlayer(uint actorId) + public ConnectedPlayer(ZoneConnection zc, uint actorId) { + zoneConnection = zc; this.actorID = actorId; playerActor = new Player(this, actorId); actorInstanceList.Add(playerActor); } - public void SetConnection(int type, ClientConnection conn) - { - conn.connType = type; - switch (type) - { - case BasePacket.TYPE_ZONE: - zoneConnection = conn; - break; - case BasePacket.TYPE_CHAT: - chatConnection = conn; - break; - } - } - - public bool IsClientConnectionsReady() - { - return (zoneConnection != null && chatConnection != null); - } - - public void Disconnect() - { - zoneConnection.Disconnect(); - chatConnection.Disconnect(); - } - - public bool IsDisconnected() - { - return (!zoneConnection.IsConnected() && !chatConnection.IsConnected()); - } - public void QueuePacket(BasePacket basePacket) { zoneConnection.QueuePacket(basePacket); diff --git a/FFXIVClassic World Server/ConfigConstants.cs b/FFXIVClassic World Server/ConfigConstants.cs index 028ad36a..7f8b29aa 100644 --- a/FFXIVClassic World Server/ConfigConstants.cs +++ b/FFXIVClassic World Server/ConfigConstants.cs @@ -18,15 +18,15 @@ namespace FFXIVClassic_World_Server public static bool Load() { - Program.Log.Info("Loading config.ini"); + Program.Log.Info("Loading world_config.ini"); - if (!File.Exists("./config.ini")) + if (!File.Exists("./world_config.ini")) { Program.Log.Error("FILE NOT FOUND!"); return false; } - INIFile configIni = new INIFile("./config.ini"); + INIFile configIni = new INIFile("./world_config.ini"); ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1"); ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54992"); diff --git a/FFXIVClassic World Server/DataObjects/ZoneServer.cs b/FFXIVClassic World Server/DataObjects/ZoneServer.cs index b3cf09e1..e7371402 100644 --- a/FFXIVClassic World Server/DataObjects/ZoneServer.cs +++ b/FFXIVClassic World Server/DataObjects/ZoneServer.cs @@ -16,6 +16,9 @@ namespace FFXIVClassic_World_Server.DataObjects public readonly int[] ownedZoneIds; public bool isConnected = false; public Socket zoneServerConnection; + private ClientConnection conn; + + private byte[] buffer = new byte[0xFFFF]; public ZoneServer(string ip, int port) { @@ -34,6 +37,18 @@ namespace FFXIVClassic_World_Server.DataObjects { zoneServerConnection.Connect(remoteEP); isConnected = true; + conn = new ClientConnection(); + conn.socket = zoneServerConnection; + conn.buffer = new byte[0xFFFF]; + + try + { + zoneServerConnection.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn); + } + catch (Exception e) + { + throw new ApplicationException("Error occured starting listeners, check inner exception", e); + } } catch (Exception e) { Program.Log.Error("Failed to connect"); return; } @@ -54,5 +69,71 @@ namespace FFXIVClassic_World_Server.DataObjects } } + private void ReceiveCallback(IAsyncResult result) + { + ClientConnection conn = (ClientConnection)result.AsyncState; + //Check if disconnected + if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0)) + { + conn = null; + isConnected = false; + Program.Log.Info("Zone server @ {0}:{1} disconnected!", zoneServerIp, zoneServerPort); + return; + } + + try + { + int bytesRead = conn.socket.EndReceive(result); + + bytesRead += conn.lastPartialSize; + + if (bytesRead >= 0) + { + int offset = 0; + + //Build packets until can no longer or out of data + while (true) + { + SubPacket subpacket = SubPacket.CreatePacket(ref offset, conn.buffer, bytesRead); + + //If can't build packet, break, else process another + if (subpacket == null) + break; + else + Server.GetServer().OnReceiveSubPacketFromZone(this, subpacket); + } + + //Not all bytes consumed, transfer leftover to beginning + if (offset < bytesRead) + Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset); + + conn.lastPartialSize = bytesRead - offset; + + //Build any queued subpackets into basepackets and send + conn.FlushQueuedSendPackets(); + + if (offset < bytesRead) + //Need offset since not all bytes consumed + conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(ReceiveCallback), conn); + else + //All bytes consumed, full buffer available + conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn); + } + else + { + conn = null; + isConnected = false; + Program.Log.Info("Zone server @ {0}:{1} disconnected!", zoneServerIp, zoneServerPort); + } + } + catch (SocketException) + { + conn = null; + isConnected = false; + Program.Log.Info("Zone server @ {0}:{1} disconnected!", zoneServerIp, zoneServerPort); + } + } + + } } diff --git a/FFXIVClassic World Server/NLog.config b/FFXIVClassic World Server/NLog.config index aea50410..e3e21f9c 100644 --- a/FFXIVClassic World Server/NLog.config +++ b/FFXIVClassic World Server/NLog.config @@ -53,6 +53,7 @@ +