From e30831fdc5e671ddbf00495e272dd3b47c36056a Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sat, 3 Dec 2016 12:19:59 -0500 Subject: [PATCH] Built subpackets to let the zone servers talk to the world server. Implemented cross-server zoning but the E2 packet or something isn't being sent. --- FFXIVClassic Map Server/Database.cs | 18 +++++- .../FFXIVClassic Map Server.csproj | 5 ++ FFXIVClassic Map Server/PacketProcessor.cs | 48 +++++++++------ FFXIVClassic Map Server/Server.cs | 18 +++--- FFXIVClassic Map Server/WorldManager.cs | 27 ++++++--- FFXIVClassic Map Server/actors/Actor.cs | 6 +- .../actors/chara/npc/Npc.cs | 2 +- .../actors/chara/player/Player.cs | 49 ++++++++++++--- .../dataobjects/Session.cs | 13 ++++ .../dataobjects/ZoneConnection.cs | 7 +++ .../Receive/SessionBeginPacket.cs | 19 ++++++ .../WorldPackets/Receive/SessionEndPacket.cs | 45 ++++++++++++++ .../Send/SessionBeginConfirmPacket.cs | 27 +++++++++ .../Send/SessionEndConfirmPacket.cs | 28 +++++++++ .../Send/WorldRequestZoneChangePacket.cs | 37 ++++++++++++ .../send/Actor/SetActorPositionPacket.cs | 2 +- .../DataObjects/ZoneServer.cs | 23 ++++++-- FFXIVClassic World Server/Database.cs | 53 +++++++++++++++++ .../FFXIVClassic World Server.csproj | 5 +- FFXIVClassic World Server/PacketProcessor.cs | 59 ++++++++++++++++++- .../Receive/SessionBeginConfirmPacket.cs | 38 ++++++++++++ .../Receive/SessionEndConfirmPacket.cs | 39 ++++++++++++ .../Receive/WorldRequestZoneChangePacket.cs | 49 +++++++++++++++ .../WorldPackets/Send/SessionEndPacket.cs | 41 ++++++++++++- .../WorldPackets/Send/SessionEndZonePacket.cs | 44 -------------- FFXIVClassic World Server/Server.cs | 54 ++++++++++++++++- FFXIVClassic World Server/WorldMaster.cs | 31 ++++++---- 27 files changed, 674 insertions(+), 113 deletions(-) create mode 100644 FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionBeginPacket.cs create mode 100644 FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionEndPacket.cs create mode 100644 FFXIVClassic Map Server/packets/WorldPackets/Send/SessionBeginConfirmPacket.cs create mode 100644 FFXIVClassic Map Server/packets/WorldPackets/Send/SessionEndConfirmPacket.cs create mode 100644 FFXIVClassic Map Server/packets/WorldPackets/Send/WorldRequestZoneChangePacket.cs create mode 100644 FFXIVClassic World Server/Database.cs create mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionBeginConfirmPacket.cs create mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionEndConfirmPacket.cs create mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Receive/WorldRequestZoneChangePacket.cs delete mode 100644 FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndZonePacket.cs diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index cb8f72a4..0bb01eb3 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -258,6 +258,8 @@ namespace FFXIVClassic_Map_Server positionY = @y, positionZ = @z, rotation = @rot, + destinationZoneId = @destZone, + destinationSpawnType = @destSpawn, currentZoneId = @zoneId WHERE id = @charaId "; @@ -269,6 +271,8 @@ namespace FFXIVClassic_Map_Server cmd.Parameters.AddWithValue("@z", player.positionZ); cmd.Parameters.AddWithValue("@rot", player.rotation); cmd.Parameters.AddWithValue("@zoneId", player.zoneId); + cmd.Parameters.AddWithValue("@destZone", player.destinationZone); + cmd.Parameters.AddWithValue("@destSpawn", player.destinationSpawnType); cmd.ExecuteNonQuery(); } @@ -402,7 +406,9 @@ namespace FFXIVClassic_Map_Server tribe, restBonus, achievementPoints, - playTime + playTime, + destinationZoneId, + destinationSpawnType FROM characters WHERE id = @charId"; cmd = new MySqlCommand(query, conn); @@ -419,8 +425,7 @@ namespace FFXIVClassic_Map_Server player.oldRotation = player.rotation = reader.GetFloat(4); player.currentMainState = reader.GetUInt16(5); player.zoneId = reader.GetUInt32(6); - player.isZoning = true; - player.zone = Server.GetWorldManager().GetZone(player.zoneId); + player.isZoning = true; player.gcCurrent = reader.GetByte(7); player.gcRankLimsa = reader.GetByte(8); player.gcRankGridania = reader.GetByte(9); @@ -434,6 +439,13 @@ namespace FFXIVClassic_Map_Server player.playerWork.restBonusExpRate = reader.GetInt32(17); player.achievementPoints = reader.GetUInt32(18); player.playTime = reader.GetUInt32(19); + player.destinationZone = reader.GetUInt32("destinationZoneId"); + player.destinationSpawnType = reader.GetByte("destinationSpawnType"); + + if (player.destinationZone != 0) + player.zoneId = player.destinationZone; + + player.zone = Server.GetWorldManager().GetZone(player.zoneId); } } diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 77879f8c..fd7c1e34 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -256,6 +256,10 @@ + + + + @@ -280,6 +284,7 @@ Designer + diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 6958c0bf..7607e2e8 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -17,7 +17,9 @@ using FFXIVClassic_Map_Server.packets.receive.recruitment; using FFXIVClassic_Map_Server.packets.send.recruitment; using FFXIVClassic_Map_Server.packets.receive.events; using FFXIVClassic_Map_Server.lua; -using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.packets.WorldPackets.Send; +using FFXIVClassic_Map_Server.packets.WorldPackets.Receive; namespace FFXIVClassic_Map_Server { @@ -32,23 +34,36 @@ namespace FFXIVClassic_Map_Server public void ProcessPacket(ZoneConnection client, SubPacket subpacket) { - Session session = mServer.GetSession(subpacket.header.targetId); - + Session session = mServer.GetSession(subpacket.header.targetId); + + if (session == null && subpacket.gameMessage.opcode != 0x1000) + return; + //Normal Game Opcode switch (subpacket.gameMessage.opcode) { - //World Server - End Session + //World Server - Session Begin case 0x1000: - session.GetActor().CleanupAndSave(); - break; - //World Server - End Session and Zone - case 0x1001: + subpacket.DebugPrintSubPacket(); + session = mServer.AddSession(subpacket.header.targetId); - session.GetActor().CleanupAndSave(); + if (session.GetActor().destinationZone != 0) + Server.GetWorldManager().DoZoneIn(session.GetActor(), false, session.GetActor().destinationSpawnType); + + client.FlushQueuedSendPackets(); break; - //World Server - Begin Session - case 0x1002: - break; + //World Server - Session End + case 0x1001: + SessionEndPacket endSessionPacket = new SessionEndPacket(subpacket.data); + + if (endSessionPacket.destinationZoneId == 0) + session.GetActor().CleanupAndSave(); + else + session.GetActor().CleanupAndSave(endSessionPacket.destinationZoneId, endSessionPacket.destinationSpawnType, endSessionPacket.destinationX, endSessionPacket.destinationY, endSessionPacket.destinationZ, endSessionPacket.destinationRot); + + client.QueuePacket(SessionEndConfirmPacket.BuildPacket(session, endSessionPacket.destinationZoneId), true, false); + client.FlushQueuedSendPackets(); + break; //Ping case 0x0001: //subpacket.DebugPrintSubPacket(); @@ -60,12 +75,12 @@ namespace FFXIVClassic_Map_Server case 0x0002: subpacket.DebugPrintSubPacket(); - session = mServer.AddSession(subpacket.header.targetId); + client.QueuePacket(_0x2Packet.BuildPacket(session.id), true, false); - client.QueuePacket(_0x2Packet.BuildPacket(session.id), true, false); - - Server.GetWorldManager().DoLogin(session.GetActor()); + LuaEngine.OnBeginLogin(session.GetActor()); + Server.GetWorldManager().DoZoneIn(session.GetActor(), true, 0x1); + LuaEngine.OnLogin(session.GetActor()); client.FlushQueuedSendPackets(); @@ -98,7 +113,6 @@ namespace FFXIVClassic_Map_Server //Update Position case 0x00CA: //Update Position - subpacket.DebugPrintSubPacket(); UpdatePlayerPositionPacket posUpdate = new UpdatePlayerPositionPacket(subpacket.data); session.UpdatePlayerActorPosition(posUpdate.x, posUpdate.y, posUpdate.z, posUpdate.rot, posUpdate.moveState); session.GetActor().SendInstanceUpdate(); diff --git a/FFXIVClassic Map Server/Server.cs b/FFXIVClassic Map Server/Server.cs index 8bf0c2a2..cd718ea5 100644 --- a/FFXIVClassic Map Server/Server.cs +++ b/FFXIVClassic Map Server/Server.cs @@ -95,6 +95,9 @@ namespace FFXIVClassic_Map_Server public Session AddSession(uint id) { + if (mSessionList.ContainsKey(id)) + return mSessionList[id]; + Session session = new Session(id); mSessionList.Add(id, session); return session; @@ -104,8 +107,7 @@ namespace FFXIVClassic_Map_Server { if (mSessionList.ContainsKey(id)) { - mSessionList[id].GetActor().CleanupAndSave(); - mSessionList.Remove(id); + mSessionList.Remove(id); } } @@ -244,6 +246,11 @@ namespace FFXIVClassic_Map_Server #endregion + public static ZoneConnection GetWorldConnection() + { + return mWorldConnection; + } + public static Server GetServer() { return mSelf; @@ -252,12 +259,7 @@ namespace FFXIVClassic_Map_Server public static CommandProcessor GetCommandProcessor() { return mCommandProcessor; - } - - public static ZoneConnection GetWorldConnection() - { - return mWorldConnection; - } + } public static WorldManager GetWorldManager() { diff --git a/FFXIVClassic Map Server/WorldManager.cs b/FFXIVClassic Map Server/WorldManager.cs index 7f912169..c330d99c 100644 --- a/FFXIVClassic Map Server/WorldManager.cs +++ b/FFXIVClassic Map Server/WorldManager.cs @@ -384,9 +384,8 @@ namespace FFXIVClassic_Map_Server oldZone.AddActorToZone(player); } - var message = "WorldManager.DoZoneChange: unable to change areas, new area is not valid."; - player.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM, "[Debug]", message); - Program.Log.Debug(message); + Program.Log.Debug("Request to change to zone not on this server by: {0}.", player.customDisplayName); + RequestWorldServerZoneChange(player, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation); return; } @@ -450,8 +449,8 @@ namespace FFXIVClassic_Map_Server } } - //Login Zone In - public void DoLogin(Player player) + //Session started, zone into world + public void DoZoneIn(Player player, bool isLogin, ushort spawnType) { //Add player to new zone and update Zone zone = GetZone(player.zoneId); @@ -462,15 +461,19 @@ namespace FFXIVClassic_Map_Server //Set the current zone and add player player.zone = zone; - - LuaEngine.OnBeginLogin(player); zone.AddActorToZone(player); //Send packets - player.SendZoneInPackets(this, 0x1); + if (!isLogin) + { + player.playerSession.QueuePacket(DeleteAllActorsPacket.BuildPacket(player.actorId), true, false); + player.playerSession.QueuePacket(_0xE2Packet.BuildPacket(player.actorId, 0x0), true, false); + player.playerSession.QueuePacket(_0xE2Packet.BuildPacket(player.actorId, 0x0), true, false); + } - LuaEngine.OnLogin(player); + player.SendZoneInPackets(this, spawnType); + LuaEngine.OnZoneIn(player); } @@ -485,6 +488,12 @@ namespace FFXIVClassic_Map_Server } + private void RequestWorldServerZoneChange(Player player, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) + { + ZoneConnection zc = Server.GetWorldConnection(); + zc.RequestZoneChange(player.playerSession.id, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation); + } + public Player GetPCInWorld(string name) { foreach (Zone zone in zoneList.Values) diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index dc26a885..43691cb1 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -70,7 +70,7 @@ namespace FFXIVClassic_Map_Server.Actors return SetActorSpeedPacket.BuildPacket(actorId, playerActorId); } - public SubPacket CreateSpawnPositonPacket(uint playerActorId, uint spawnType) + public SubPacket CreateSpawnPositonPacket(uint playerActorId, ushort spawnType) { SubPacket spawnPacket; if (!spawnedFirstTime && playerActorId == actorId) @@ -91,7 +91,7 @@ namespace FFXIVClassic_Map_Server.Actors return spawnPacket; } - public SubPacket CreateSpawnTeleportPacket(uint playerActorId, uint spawnType) + public SubPacket CreateSpawnTeleportPacket(uint playerActorId, ushort spawnType) { SubPacket spawnPacket; @@ -223,7 +223,7 @@ namespace FFXIVClassic_Map_Server.Actors return GetSpawnPackets(playerActorId, 0x1); } - public virtual BasePacket GetSpawnPackets(uint playerActorId, uint spawnType) + public virtual BasePacket GetSpawnPackets(uint playerActorId, ushort spawnType) { List subpackets = new List(); subpackets.Add(CreateAddActorPacket(playerActorId, 8)); diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index c371f40a..adb887d5 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -116,7 +116,7 @@ namespace FFXIVClassic_Map_Server.Actors return ActorInstantiatePacket.BuildPacket(actorId, playerActorId, actorName, className, lParams); } - public override BasePacket GetSpawnPackets(uint playerActorId, uint spawnType) + public override BasePacket GetSpawnPackets(uint playerActorId, ushort spawnType) { List subpackets = new List(); subpackets.Add(CreateAddActorPacket(playerActorId)); diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index a02987a2..72e23282 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -83,7 +83,9 @@ namespace FFXIVClassic_Map_Server.Actors public Coroutine currentEventRunning; - //Player Info + //Player Info + public uint destinationZone; + public ushort destinationSpawnType; public uint[] timers = new uint[20]; public ushort currentJob; public uint currentTitle; @@ -257,9 +259,9 @@ namespace FFXIVClassic_Map_Server.Actors else lParams = LuaUtils.CreateLuaParamList("/Chara/Player/Player_work", false, false, false, false, false, true); return ActorInstantiatePacket.BuildPacket(actorId, playerActorId, actorName, className, lParams); - } - - public override BasePacket GetSpawnPackets(uint playerActorId, uint spawnType) + } + + public override BasePacket GetSpawnPackets(uint playerActorId, ushort spawnType) { List subpackets = new List(); subpackets.Add(CreateAddActorPacket(playerActorId, 8)); @@ -646,14 +648,47 @@ namespace FFXIVClassic_Map_Server.Actors } public void CleanupAndSave() - { + { + playerSession.LockUpdates(true); + //Remove actor from zone and main server list zone.RemoveActorFromZone(this); + //Set Destination to 0 + this.destinationZone = 0; + this.destinationSpawnType = 0; + //Save Player Database.SavePlayerPlayTime(this); - Database.SavePlayerPosition(this); - Program.Log.Info("{0} has been logged out and saved.", this.customDisplayName); + Database.SavePlayerPosition(this); + + Server.GetServer().RemoveSession(playerSession.id); + + Program.Log.Info("{0} has been removed from the session list.", this.customDisplayName); + } + + public void CleanupAndSave(uint destinationZone, ushort spawnType, float destinationX, float destinationY, float destinationZ, float destinationRot) + { + playerSession.LockUpdates(true); + + //Remove actor from zone and main server list + zone.RemoveActorFromZone(this); + + //Set destination + this.destinationZone = destinationZone; + this.destinationSpawnType = spawnType; + this.positionX = destinationX; + this.positionY = destinationY; + this.positionZ = destinationZ; + this.rotation = destinationRot; + + //Save Player + Database.SavePlayerPlayTime(this); + Database.SavePlayerPosition(this); + + Server.GetServer().RemoveSession(playerSession.id); + + Program.Log.Info("{0} has been removed from the session list.", this.customDisplayName); } public Area GetZone() diff --git a/FFXIVClassic Map Server/dataobjects/Session.cs b/FFXIVClassic Map Server/dataobjects/Session.cs index 0dd8a88c..2e0c3ebb 100644 --- a/FFXIVClassic Map Server/dataobjects/Session.cs +++ b/FFXIVClassic Map Server/dataobjects/Session.cs @@ -20,6 +20,8 @@ namespace FFXIVClassic_Map_Server.dataobjects public uint languageCode = 1; private uint lastPingPacket = Utils.UnixTimeStampUTC(); + public bool isUpdatesLocked = false; + public string errorMessage = ""; public Session(uint sessionId) @@ -63,6 +65,9 @@ namespace FFXIVClassic_Map_Server.dataobjects public void UpdatePlayerActorPosition(float x, float y, float z, float rot, ushort moveState) { + if (isUpdatesLocked) + return; + playerActor.oldPositionX = playerActor.positionX; playerActor.oldPositionY = playerActor.positionY; playerActor.oldPositionZ = playerActor.positionZ; @@ -80,6 +85,9 @@ namespace FFXIVClassic_Map_Server.dataobjects public void UpdateInstance(List list) { + if (isUpdatesLocked) + return; + List basePackets = new List(); List RemoveActorSubpackets = new List(); List posUpdateSubpackets = new List(); @@ -132,5 +140,10 @@ namespace FFXIVClassic_Map_Server.dataobjects actorInstanceList.Clear(); } + + public void LockUpdates(bool f) + { + isUpdatesLocked = f; + } } } diff --git a/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs b/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs index 96197dd1..a8e957d1 100644 --- a/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs +++ b/FFXIVClassic Map Server/dataobjects/ZoneConnection.cs @@ -5,6 +5,7 @@ using FFXIVClassic.Common; using System.Collections.Concurrent; using System.Net; using System.Collections.Generic; +using FFXIVClassic_Map_Server.packets.WorldPackets.Send; namespace FFXIVClassic_Map_Server.dataobjects { @@ -63,5 +64,11 @@ namespace FFXIVClassic_Map_Server.dataobjects if (socket.Connected) socket.Disconnect(false); } + + public void RequestZoneChange(uint sessionId, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) + { + WorldRequestZoneChangePacket.BuildPacket(sessionId, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation).DebugPrintSubPacket(); + QueuePacket(WorldRequestZoneChangePacket.BuildPacket(sessionId, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation), true, false); + } } } diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionBeginPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionBeginPacket.cs new file mode 100644 index 00000000..3baf9b98 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionBeginPacket.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Receive +{ + class SessionBeginPacket + { + public bool invalidPacket = false; + + public SessionBeginPacket(byte[] data) + { + + } + } +} diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionEndPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionEndPacket.cs new file mode 100644 index 00000000..77dd6395 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Receive/SessionEndPacket.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Receive +{ + class SessionEndPacket + { + public uint destinationZoneId; + public ushort destinationSpawnType; + public float destinationX; + public float destinationY; + public float destinationZ; + public float destinationRot; + + public bool invalidPacket = false; + + public SessionEndPacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + destinationZoneId = binReader.ReadUInt32(); + destinationSpawnType = binReader.ReadUInt16(); + destinationX = binReader.ReadSingle(); + destinationY = binReader.ReadSingle(); + destinationZ = binReader.ReadSingle(); + destinationRot = binReader.ReadSingle(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + + } + } +} diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/SessionBeginConfirmPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/SessionBeginConfirmPacket.cs new file mode 100644 index 00000000..b56439b5 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/SessionBeginConfirmPacket.cs @@ -0,0 +1,27 @@ +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.dataobjects; +using System; +using System.IO; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send +{ + class SessionBeginConfirmPacket + { + public const ushort OPCODE = 0x1000; + public const uint PACKET_SIZE = 0x28; + + public static SubPacket BuildPacket(Session session, ushort errorCode = 0) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)session.id); + binWriter.Write((UInt16)errorCode); + } + } + return new SubPacket(true, OPCODE, 0, session.id, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/SessionEndConfirmPacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/SessionEndConfirmPacket.cs new file mode 100644 index 00000000..9e220947 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/SessionEndConfirmPacket.cs @@ -0,0 +1,28 @@ +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.dataobjects; +using System; +using System.IO; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send +{ + class SessionEndConfirmPacket + { + public const ushort OPCODE = 0x1001; + public const uint PACKET_SIZE = 0x30; + + public static SubPacket BuildPacket(Session session, uint destinationZone, ushort errorCode = 0) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)session.id); + binWriter.Write((UInt16)errorCode); + binWriter.Write((UInt32)destinationZone); + } + } + return new SubPacket(true, OPCODE, 0, session.id, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/WorldPackets/Send/WorldRequestZoneChangePacket.cs b/FFXIVClassic Map Server/packets/WorldPackets/Send/WorldRequestZoneChangePacket.cs new file mode 100644 index 00000000..259d5631 --- /dev/null +++ b/FFXIVClassic Map Server/packets/WorldPackets/Send/WorldRequestZoneChangePacket.cs @@ -0,0 +1,37 @@ +using FFXIVClassic.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_Map_Server.packets.WorldPackets.Send +{ + class WorldRequestZoneChangePacket + { + public const ushort OPCODE = 0x1002; + public const uint PACKET_SIZE = 0x048; + + public static SubPacket BuildPacket(uint sessionId, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + binWriter.Write((UInt32)sessionId); + binWriter.Write((UInt32)destinationZoneId); + binWriter.Write((UInt16)spawnType); + binWriter.Write((Single)spawnX); + binWriter.Write((Single)spawnY); + binWriter.Write((Single)spawnZ); + binWriter.Write((Single)spawnRotation); + } + } + + return new SubPacket(OPCODE, sessionId, sessionId, data); + } + } +} diff --git a/FFXIVClassic Map Server/packets/send/Actor/SetActorPositionPacket.cs b/FFXIVClassic Map Server/packets/send/Actor/SetActorPositionPacket.cs index cfb9cf05..0df2192a 100644 --- a/FFXIVClassic Map Server/packets/send/Actor/SetActorPositionPacket.cs +++ b/FFXIVClassic Map Server/packets/send/Actor/SetActorPositionPacket.cs @@ -24,7 +24,7 @@ namespace FFXIVClassic_Map_Server.packets.send.actor public const float INNPOS_Z = 165.050003f; public const float INNPOS_ROT = -1.530000f; - public static SubPacket BuildPacket(uint sourceActorID, uint targetActorID, uint actorId, float x, float y, float z, float rotation, uint spawnType, bool isZoningPlayer) + public static SubPacket BuildPacket(uint sourceActorID, uint targetActorID, uint actorId, float x, float y, float z, float rotation, ushort spawnType, bool isZoningPlayer) { byte[] data = new byte[PACKET_SIZE-0x20]; diff --git a/FFXIVClassic World Server/DataObjects/ZoneServer.cs b/FFXIVClassic World Server/DataObjects/ZoneServer.cs index 00896f2f..f066ba7b 100644 --- a/FFXIVClassic World Server/DataObjects/ZoneServer.cs +++ b/FFXIVClassic World Server/DataObjects/ZoneServer.cs @@ -14,17 +14,25 @@ namespace FFXIVClassic_World_Server.DataObjects { public readonly string zoneServerIp; public readonly int zoneServerPort; - public readonly int[] ownedZoneIds; + public readonly List ownedZoneIds; public bool isConnected = false; public Socket zoneServerConnection; - private ClientConnection conn; + private ClientConnection conn; private byte[] buffer = new byte[0xFFFF]; - public ZoneServer(string ip, int port) + public ZoneServer(string ip, int port, uint firstId) { zoneServerIp = ip; zoneServerPort = port; + + ownedZoneIds = new List(); + ownedZoneIds.Add(firstId); + } + + public void AddLoadedZone(uint id) + { + ownedZoneIds.Add(id); } public void Connect() @@ -135,14 +143,19 @@ namespace FFXIVClassic_World_Server.DataObjects } } - public void SendGoodbye(Session session) + public void SendSessionStart(Session session) + { + SendPacket(SessionBeginPacket.BuildPacket(session)); + } + + public void SendSessionEnd(Session session) { SendPacket(SessionEndPacket.BuildPacket(session)); } public void SendSessionEnd(Session session, uint destinationZoneId, string destinationPrivateArea, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) { - SendPacket(SessionEndAndZonePacket.BuildPacket(session, destinationZoneId, destinationPrivateArea, spawnType, spawnX, spawnY, spawnZ, spawnRotation)); + SendPacket(SessionEndPacket.BuildPacket(session, destinationZoneId, destinationPrivateArea, spawnType, spawnX, spawnY, spawnZ, spawnRotation)); } } diff --git a/FFXIVClassic World Server/Database.cs b/FFXIVClassic World Server/Database.cs new file mode 100644 index 00000000..e3a6412a --- /dev/null +++ b/FFXIVClassic World Server/Database.cs @@ -0,0 +1,53 @@ +using MySql.Data.MySqlClient; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server +{ + class Database + { + public static uint GetCurrentZoneForSession(uint charId) + { + uint currentZone = 0; + uint destinationZone = 0; + + 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 currentZoneId, destinationZoneId FROM characters WHERE id = @charaId", conn); + cmd.Parameters.AddWithValue("@charaId", charId); + using (MySqlDataReader Reader = cmd.ExecuteReader()) + { + while (Reader.Read()) + { + currentZone = Reader.GetUInt32("currentZoneId"); + destinationZone = Reader.GetUInt32("destinationZoneId"); + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + + if (currentZone == 0 && destinationZone != 0) + return destinationZone; + if (currentZone != 0 && destinationZone == 0) + return currentZone; + else + { + return 0; + } + } + } +} diff --git a/FFXIVClassic World Server/FFXIVClassic World Server.csproj b/FFXIVClassic World Server/FFXIVClassic World Server.csproj index 7d4825ee..bb0381e4 100644 --- a/FFXIVClassic World Server/FFXIVClassic World Server.csproj +++ b/FFXIVClassic World Server/FFXIVClassic World Server.csproj @@ -65,6 +65,7 @@ + @@ -73,8 +74,10 @@ + + + - diff --git a/FFXIVClassic World Server/PacketProcessor.cs b/FFXIVClassic World Server/PacketProcessor.cs index 3909cf02..34793e38 100644 --- a/FFXIVClassic World Server/PacketProcessor.cs +++ b/FFXIVClassic World Server/PacketProcessor.cs @@ -3,6 +3,8 @@ using FFXIVClassic_World_Server.DataObjects; using FFXIVClassic_World_Server.Packets.Receive; using FFXIVClassic_World_Server.Packets.Send; using FFXIVClassic_World_Server.Packets.Send.Login; +using FFXIVClassic_World_Server.Packets.WorldPackets.Receive; +using FFXIVClassic_World_Server.Packets.WorldPackets.Send; using System; using System.Collections.Generic; using System.IO; @@ -37,7 +39,7 @@ namespace FFXIVClassic_World_Server { if (packet.header.isCompressed == 0x01) BasePacket.DecompressPacket(ref packet); - + List subPackets = packet.GetSubpackets(); foreach (SubPacket subpacket in subPackets) { @@ -47,7 +49,10 @@ namespace FFXIVClassic_World_Server HelloPacket hello = new HelloPacket(packet.data); if (packet.header.connectionType == BasePacket.TYPE_ZONE) + { mServer.AddSession(client, Session.Channel.ZONE, hello.sessionId); + mServer.GetWorldManager().DoLogin(mServer.GetSession(hello.sessionId)); + } else if (packet.header.connectionType == BasePacket.TYPE_CHAT) mServer.AddSession(client, Session.Channel.CHAT, hello.sessionId); @@ -73,7 +78,6 @@ namespace FFXIVClassic_World_Server { //Send to the correct zone server uint targetSession = subpacket.header.targetId; - mServer.GetSession(targetSession).routing1 = Server.GetServer().GetWorldManager().mZoneServerList["127.0.0.1:1989"]; if (mServer.GetSession(targetSession).routing1 != null) mServer.GetSession(targetSession).routing1.SendPacket(subpacket); @@ -81,6 +85,57 @@ namespace FFXIVClassic_World_Server if (mServer.GetSession(targetSession).routing2 != null) mServer.GetSession(targetSession).routing2.SendPacket(subpacket); } + //World Server Type + else if (subpacket.header.type >= 0x1000) + { + uint targetSession = subpacket.header.targetId; + Session session = mServer.GetSession(targetSession); + + switch (subpacket.header.type) + { + //Session Begin Confirm + case 0x1000: + SessionBeginConfirmPacket beginConfirmPacket = new SessionBeginConfirmPacket(packet.data); + + if (beginConfirmPacket.invalidPacket || beginConfirmPacket.errorCode == 0) + Program.Log.Error("Session {0} had a error beginning session.", beginConfirmPacket.sessionId); + + break; + //Session End Confirm + case 0x1001: + SessionEndConfirmPacket endConfirmPacket = new SessionEndConfirmPacket(packet.data); + + if (!endConfirmPacket.invalidPacket && endConfirmPacket.errorCode != 0) + { + //Check destination, if != 0, update route and start new session + if (endConfirmPacket.destinationZone != 0) + { + session.routing1 = Server.GetServer().GetWorldManager().GetZoneServer(endConfirmPacket.destinationZone); + session.routing1.SendSessionStart(session); + } + else + { + mServer.RemoveSession(Session.Channel.ZONE, endConfirmPacket.sessionId); + mServer.RemoveSession(Session.Channel.CHAT, endConfirmPacket.sessionId); + } + } + else + Program.Log.Error("Session {0} had an error ending session.", endConfirmPacket.sessionId); + + break; + //Zone Change Request + case 0x1002: + WorldRequestZoneChangePacket zoneChangePacket = new WorldRequestZoneChangePacket(packet.data); + + if (!zoneChangePacket.invalidPacket) + { + mServer.GetWorldManager().DoZoneServerChange(session, zoneChangePacket.destinationZoneId, "", zoneChangePacket.destinationSpawnType, zoneChangePacket.destinationX, zoneChangePacket.destinationY, zoneChangePacket.destinationZ, zoneChangePacket.destinationRot); + } + + break; + } + + } else packet.DebugPrintPacket(); } diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionBeginConfirmPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionBeginConfirmPacket.cs new file mode 100644 index 00000000..189865c6 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionBeginConfirmPacket.cs @@ -0,0 +1,38 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive +{ + class SessionBeginConfirmPacket + { + public bool invalidPacket = false; + public uint sessionId; + public ushort errorCode; + + public SessionBeginConfirmPacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + sessionId = binReader.ReadUInt32(); + errorCode = binReader.ReadUInt16(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionEndConfirmPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionEndConfirmPacket.cs new file mode 100644 index 00000000..b0e22aec --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/SessionEndConfirmPacket.cs @@ -0,0 +1,39 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive +{ + class SessionEndConfirmPacket + { + public bool invalidPacket = false; + public uint sessionId; + public ushort errorCode; + public uint destinationZone; + + public SessionEndConfirmPacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + sessionId = binReader.ReadUInt32(); + errorCode = binReader.ReadUInt16(); + destinationZone = binReader.ReadUInt32(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Receive/WorldRequestZoneChangePacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Receive/WorldRequestZoneChangePacket.cs new file mode 100644 index 00000000..faa2e4b4 --- /dev/null +++ b/FFXIVClassic World Server/Packets/WorldPackets/Receive/WorldRequestZoneChangePacket.cs @@ -0,0 +1,49 @@ +using FFXIVClassic.Common; +using FFXIVClassic_World_Server.DataObjects; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic_World_Server.Packets.WorldPackets.Receive +{ + class WorldRequestZoneChangePacket + { + public uint sessionId; + public uint destinationZoneId; + public byte destinationSpawnType; + public float destinationX; + public float destinationY; + public float destinationZ; + public float destinationRot; + + public bool invalidPacket = false; + + public WorldRequestZoneChangePacket(byte[] data) + { + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryReader binReader = new BinaryReader(mem)) + { + try + { + sessionId = binReader.ReadUInt32(); + destinationZoneId = binReader.ReadUInt32(); + destinationSpawnType = (byte)binReader.ReadUInt16(); + destinationX = binReader.ReadSingle(); + destinationY = binReader.ReadSingle(); + destinationZ = binReader.ReadSingle(); + destinationRot = binReader.ReadSingle(); + } + catch (Exception) + { + invalidPacket = true; + } + } + } + + } + } +} diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndPacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndPacket.cs index fb384d8c..762ba91d 100644 --- a/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndPacket.cs +++ b/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndPacket.cs @@ -12,12 +12,51 @@ namespace FFXIVClassic_World_Server.Packets.WorldPackets.Send class SessionEndPacket { public const ushort OPCODE = 0x1001; - public const uint PACKET_SIZE = 0x24; + public const uint PACKET_SIZE = 0x38; public static SubPacket BuildPacket(Session session) { byte[] data = new byte[PACKET_SIZE - 0x20]; + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + try + { + binWriter.Write((UInt32)0); + } + catch (Exception) + { } + } + } + + return new SubPacket(true, OPCODE, 0, session.sessionId, data); + } + + public static SubPacket BuildPacket(Session session, uint destinationZoneId, string destinationPrivateArea, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) + { + byte[] data = new byte[PACKET_SIZE - 0x20]; + + using (MemoryStream mem = new MemoryStream(data)) + { + using (BinaryWriter binWriter = new BinaryWriter(mem)) + { + try + { + binWriter.Write((UInt32)destinationZoneId); + binWriter.Write((UInt32)spawnType); + binWriter.Write((Single)spawnX); + binWriter.Write((Single)spawnY); + binWriter.Write((Single)spawnZ); + binWriter.Write((Single)spawnRotation); + + } + catch (Exception) + { } + } + } + return new SubPacket(true, OPCODE, 0, session.sessionId, data); } } diff --git a/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndZonePacket.cs b/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndZonePacket.cs deleted file mode 100644 index 453737c1..00000000 --- a/FFXIVClassic World Server/Packets/WorldPackets/Send/SessionEndZonePacket.cs +++ /dev/null @@ -1,44 +0,0 @@ -using FFXIVClassic.Common; -using FFXIVClassic_World_Server.DataObjects; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace FFXIVClassic_World_Server.Packets.WorldPackets.Send -{ - class SessionEndAndZonePacket - { - public const ushort OPCODE = 0x1002; - public const uint PACKET_SIZE = 0x48; - - public static SubPacket BuildPacket(Session session, uint destinationZoneId, string destinationPrivateArea, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation) - { - byte[] data = new byte[PACKET_SIZE - 0x20]; - - using (MemoryStream mem = new MemoryStream(data)) - { - using (BinaryWriter binWriter = new BinaryWriter(mem)) - { - try - { - binWriter.Write((UInt32)destinationZoneId); - binWriter.Write((UInt32)destinationZoneId); - binWriter.Write((UInt32)spawnType); - binWriter.Write((Single)spawnX); - binWriter.Write((Single)spawnY); - binWriter.Write((Single)spawnZ); - binWriter.Write((Single)spawnRotation); - - } - catch (Exception) - { } - } - } - - return new SubPacket(true, OPCODE, 0, session.sessionId, data); - } - } -} diff --git a/FFXIVClassic World Server/Server.cs b/FFXIVClassic World Server/Server.cs index b88ab3d7..c010faef 100644 --- a/FFXIVClassic World Server/Server.cs +++ b/FFXIVClassic World Server/Server.cs @@ -1,5 +1,6 @@ using FFXIVClassic.Common; using FFXIVClassic_World_Server.DataObjects; +using FFXIVClassic_World_Server.Packets.WorldPackets.Receive; using System; using System.Collections.Generic; using System.Net; @@ -138,7 +139,58 @@ namespace FFXIVClassic_World_Server public void OnReceiveSubPacketFromZone(ZoneServer zoneServer, SubPacket subpacket) { uint sessionId = subpacket.header.targetId; - + + if (subpacket.gameMessage.opcode >= 0x1000) + { + subpacket.DebugPrintSubPacket(); + uint targetSession = subpacket.header.targetId; + Session session = GetSession(targetSession); + + switch (subpacket.gameMessage.opcode) + { + //Session Begin Confirm + case 0x1000: + SessionBeginConfirmPacket beginConfirmPacket = new SessionBeginConfirmPacket(subpacket.data); + + if (beginConfirmPacket.invalidPacket || beginConfirmPacket.errorCode == 0) + Program.Log.Error("Session {0} had a error beginning session.", beginConfirmPacket.sessionId); + + break; + //Session End Confirm + case 0x1001: + SessionEndConfirmPacket endConfirmPacket = new SessionEndConfirmPacket(subpacket.data); + + if (!endConfirmPacket.invalidPacket && endConfirmPacket.errorCode == 0) + { + //Check destination, if != 0, update route and start new session + if (endConfirmPacket.destinationZone != 0) + { + session.routing1 = Server.GetServer().GetWorldManager().GetZoneServer(endConfirmPacket.destinationZone); + session.routing1.SendSessionStart(session); + } + else + { + RemoveSession(Session.Channel.ZONE, endConfirmPacket.sessionId); + RemoveSession(Session.Channel.CHAT, endConfirmPacket.sessionId); + } + } + else + Program.Log.Error("Session {0} had an error ending session.", endConfirmPacket.sessionId); + + break; + //Zone Change Request + case 0x1002: + WorldRequestZoneChangePacket zoneChangePacket = new WorldRequestZoneChangePacket(subpacket.data); + + if (!zoneChangePacket.invalidPacket) + { + GetWorldManager().DoZoneServerChange(session, zoneChangePacket.destinationZoneId, "", zoneChangePacket.destinationSpawnType, zoneChangePacket.destinationX, zoneChangePacket.destinationY, zoneChangePacket.destinationZ, zoneChangePacket.destinationRot); + } + + break; + } + } + if (mZoneSessionList.ContainsKey(sessionId)) { ClientConnection conn = mZoneSessionList[sessionId].clientConnection; diff --git a/FFXIVClassic World Server/WorldMaster.cs b/FFXIVClassic World Server/WorldMaster.cs index 797f043a..0f490c25 100644 --- a/FFXIVClassic World Server/WorldMaster.cs +++ b/FFXIVClassic World Server/WorldMaster.cs @@ -34,6 +34,7 @@ namespace FFXIVClassic_World_Server string query = @" SELECT + id, serverIp, serverPort FROM server_zones @@ -45,15 +46,18 @@ namespace FFXIVClassic_World_Server { while (reader.Read()) { - string ip = reader.GetString(0); - int port = reader.GetInt32(1); + uint id = reader.GetUInt32(0); + string ip = reader.GetString(1); + int port = reader.GetInt32(2); string address = ip + ":" + port; if (!mZoneServerList.ContainsKey(address)) { - ZoneServer zone = new ZoneServer(ip, port); + ZoneServer zone = new ZoneServer(ip, port, id); mZoneServerList.Add(address, zone); } + else + mZoneServerList[address].AddLoadedZone(id); } } } @@ -130,6 +134,17 @@ namespace FFXIVClassic_World_Server } } + public ZoneServer GetZoneServer(uint zoneId) + { + foreach (ZoneServer zs in mZoneServerList.Values) + { + if (zs.ownedZoneIds.Contains(zoneId)) + return zs; + } + + return null; + } + //Moves the actor to the new zone if exists. No packets are sent nor position changed. public void DoSeamlessZoneServerChange(Session session, uint destinationZoneId) { @@ -157,13 +172,9 @@ namespace FFXIVClassic_World_Server //Login Zone In public void DoLogin(Session session) - { - /* - ->Update routing - ->Tell new server to load session info and add - */ - - + { + session.routing1 = GetZoneServer(Database.GetCurrentZoneForSession(session.sessionId)); + session.routing1.SendSessionStart(session); } public class ZoneEntrance