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.

This commit is contained in:
Filip Maj 2016-12-03 12:19:59 -05:00
parent 58fda93b45
commit e30831fdc5
27 changed files with 674 additions and 113 deletions

View file

@ -14,17 +14,25 @@ namespace FFXIVClassic_World_Server.DataObjects
{
public readonly string zoneServerIp;
public readonly int zoneServerPort;
public readonly int[] ownedZoneIds;
public readonly List<uint> 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<uint>();
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));
}
}

View file

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

View file

@ -65,6 +65,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ConfigConstants.cs" />
<Compile Include="Database.cs" />
<Compile Include="DataObjects\ClientConnection.cs" />
<Compile Include="DataObjects\ZoneServer.cs" />
<Compile Include="DataObjects\Session.cs" />
@ -73,8 +74,10 @@
<Compile Include="Packets\Send\_0x2Packet.cs" />
<Compile Include="Packets\Send\_0x7Packet.cs" />
<Compile Include="Packets\Send\_0x8PingPacket.cs" />
<Compile Include="Packets\WorldPackets\Receive\SessionBeginConfirmPacket.cs" />
<Compile Include="Packets\WorldPackets\Receive\WorldRequestZoneChangePacket.cs" />
<Compile Include="Packets\WorldPackets\Receive\SessionEndConfirmPacket.cs" />
<Compile Include="Packets\WorldPackets\Send\SessionBeginPacket.cs" />
<Compile Include="Packets\WorldPackets\Send\SessionEndZonePacket.cs" />
<Compile Include="Packets\WorldPackets\Send\SessionEndPacket.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -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<SubPacket> 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();
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;

View file

@ -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