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