More work on the world server. Modified map server to communicate with it.

This commit is contained in:
Filip Maj 2016-08-29 08:17:14 -04:00
parent bd26a71fef
commit 6bffe69b21
16 changed files with 507 additions and 577 deletions

View File

@ -295,6 +295,41 @@ namespace FFXIVClassic.Common
return packet; return packet;
} }
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public static BasePacket CreatePacket(ref int offset, byte[] buffer, int bytesRead)
{
BasePacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new BasePacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet) public static unsafe void EncryptPacket(Blowfish blowfish, BasePacket packet)
{ {
var data = packet.data; var data = packet.data;

View File

@ -153,6 +153,41 @@ namespace FFXIVClassic.Common
return outBytes; return outBytes;
} }
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public static SubPacket CreatePacket(ref int offset, byte[] buffer, int bytesRead)
{
SubPacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new SubPacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
public void DebugPrintSubPacket() public void DebugPrintSubPacket()
{ {
#if DEBUG #if DEBUG
@ -169,6 +204,9 @@ namespace FFXIVClassic.Common
logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE), logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE + GAMEMESSAGE_SIZE),
ConsoleOutputColor.DarkMagenta); ConsoleOutputColor.DarkMagenta);
} }
else
logger.ColorDebug(Utils.ByteArrayToHex(data, SUBPACKET_SIZE),
ConsoleOutputColor.DarkMagenta);
#endif #endif
} }
} }

View File

@ -88,7 +88,7 @@ namespace FFXIVClassic_Map_Server
if (cmd.Any()) if (cmd.Any())
{ {
// if client isnt null, take player to be the player actor // if client isnt null, take player to be the player actor
var player = client?.GetActor(); var player = client.GetActor();
if (cmd.Equals("help")) if (cmd.Equals("help"))
{ {

View File

@ -30,7 +30,7 @@ namespace FFXIVClassic_Map_Server
INIFile configIni = new INIFile("./map_config.ini"); INIFile configIni = new INIFile("./map_config.ini");
ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1"); ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1");
ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54992"); ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "1989");
ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true"); ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true");
ConfigConstants.DATABASE_WORLDID = UInt32.Parse(configIni.GetValue("Database", "worldid", "0")); ConfigConstants.DATABASE_WORLDID = UInt32.Parse(configIni.GetValue("Database", "worldid", "0"));

View File

@ -91,7 +91,7 @@
<Compile Include="actors\quest\Quest.cs" /> <Compile Include="actors\quest\Quest.cs" />
<Compile Include="actors\StaticActors.cs" /> <Compile Include="actors\StaticActors.cs" />
<Compile Include="actors\world\WorldMaster.cs" /> <Compile Include="actors\world\WorldMaster.cs" />
<Compile Include="ClientConnection.cs" /> <Compile Include="ZoneConnection.cs" />
<Compile Include="CommandProcessor.cs" /> <Compile Include="CommandProcessor.cs" />
<Compile Include="ConfigConstants.cs" /> <Compile Include="ConfigConstants.cs" />
<Compile Include="Database.cs" /> <Compile Include="Database.cs" />

View File

@ -26,138 +26,28 @@ namespace FFXIVClassic_Map_Server
Server mServer; Server mServer;
CommandProcessor cp; CommandProcessor cp;
Dictionary<uint, ConnectedPlayer> mPlayers; Dictionary<uint, ConnectedPlayer> mPlayers;
List<ClientConnection> mConnections;
public PacketProcessor(Server server, Dictionary<uint, ConnectedPlayer> playerList, List<ClientConnection> connectionList) public PacketProcessor(Server server, Dictionary<uint, ConnectedPlayer> playerList)
{ {
mPlayers = playerList; mPlayers = playerList;
mConnections = connectionList;
mServer = server; mServer = server;
cp = new CommandProcessor(playerList); cp = new CommandProcessor(playerList);
} }
public void ProcessPacket(ClientConnection client, BasePacket packet) public void ProcessPacket(ZoneConnection client, SubPacket subpacket)
{ {
if (packet.header.isCompressed == 0x01)
BasePacket.DecryptPacket(client.blowfish, ref packet);
List<SubPacket> subPackets = packet.GetSubpackets();
foreach (SubPacket subpacket in subPackets)
{
if (subpacket.header.type == 0x01)
{
packet.DebugPrintPacket();
byte[] reply1Data = {
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFD, 0xFF, 0xFF,
0xE5, 0x6E, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x0
};
byte[] reply2Data = {
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x2B, 0x5F, 0x26,
0x66, 0x00, 0x00, 0x00, 0xC8, 0xD6, 0xAF, 0x2B, 0x38, 0x2B, 0x5F, 0x26, 0xB8, 0x8D, 0xF0, 0x2B,
0xC8, 0xFD, 0x85, 0xFE, 0xA8, 0x7C, 0x5B, 0x09, 0x38, 0x2B, 0x5F, 0x26, 0xC8, 0xD6, 0xAF, 0x2B,
0xB8, 0x8D, 0xF0, 0x2B, 0x88, 0xAF, 0x5E, 0x26
};
BasePacket reply1 = new BasePacket(reply1Data);
BasePacket reply2 = new BasePacket(reply2Data);
//Write Timestamp into Reply1
using (MemoryStream mem = new MemoryStream(reply1.data))
{
using (BinaryWriter binReader = new BinaryWriter(mem))
{
binReader.BaseStream.Seek(0x14, SeekOrigin.Begin);
binReader.Write((UInt32)Utils.UnixTimeStampUTC());
}
}
//Read in Actor Id that owns this connection
uint actorID = 0;
using (MemoryStream mem = new MemoryStream(packet.data))
{
using (BinaryReader binReader = new BinaryReader(mem))
{
try
{
byte[] readIn = new byte[12];
binReader.BaseStream.Seek(0x14, SeekOrigin.Begin);
binReader.Read(readIn, 0, 12);
actorID = UInt32.Parse(Encoding.ASCII.GetString(readIn));
}
catch (Exception)
{ }
}
}
//Should never happen.... unless actor id IS 0!
if (actorID == 0)
break;
client.owner = actorID;
//Write Actor ID into reply2
using (MemoryStream mem = new MemoryStream(reply2.data))
{
using (BinaryWriter binReader = new BinaryWriter(mem))
{
binReader.BaseStream.Seek(0x10, SeekOrigin.Begin);
binReader.Write(actorID);
}
}
ConnectedPlayer player = null; ConnectedPlayer player = null;
if (packet.header.connectionType == BasePacket.TYPE_ZONE) if(mPlayers.ContainsKey(subpacket.header.targetId))
{ player = mPlayers[subpacket.header.targetId];
while (mPlayers != null && !mPlayers.ContainsKey(client.owner))
{ }
player = mPlayers[client.owner];
}
//Create connected player if not Created
if (player == null) if (player == null)
{ {
player = new ConnectedPlayer(actorID); player = new ConnectedPlayer(client, subpacket.header.targetId);
mPlayers[actorID] = player;
} }
player.SetConnection(packet.header.connectionType, client); subpacket.DebugPrintSubPacket();
if (packet.header.connectionType == BasePacket.TYPE_ZONE)
Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "zone", actorID, client.GetAddress());
else if (packet.header.connectionType == BasePacket.TYPE_CHAT)
Program.Log.Info("Got {0} connection for ActorID {1} @ {2}.", "chat", actorID, client.GetAddress());
//Create player actor
reply1.DebugPrintPacket();
client.QueuePacket(reply1);
client.QueuePacket(reply2);
break;
}
else if (subpacket.header.type == 0x07)
{
BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x08);
//client.QueuePacket(init);
}
else if (subpacket.header.type == 0x08)
{
//Response, client's current [actorID][time]
//BasePacket init = Login0x7ResponsePacket.BuildPacket(BitConverter.ToUInt32(packet.data, 0x10), Utils.UnixTimeStampUTC(), 0x07);
//client.QueuePacket(init);
packet.DebugPrintPacket();
}
else if (subpacket.header.type == 0x03)
{
ConnectedPlayer player = null;
if(mPlayers.ContainsKey(client.owner))
player = mPlayers[client.owner];
if (player == null || !player.IsClientConnectionsReady())
return;
//Normal Game Opcode //Normal Game Opcode
switch (subpacket.gameMessage.opcode) switch (subpacket.gameMessage.opcode)
@ -173,11 +63,14 @@ namespace FFXIVClassic_Map_Server
case 0x0002: case 0x0002:
subpacket.DebugPrintSubPacket(); subpacket.DebugPrintSubPacket();
player = new ConnectedPlayer(client, subpacket.header.targetId);
mPlayers[subpacket.header.targetId] = player;
client.QueuePacket(_0x2Packet.BuildPacket(player.actorID), true, false); client.QueuePacket(_0x2Packet.BuildPacket(player.actorID), true, false);
Server.GetWorldManager().DoLogin(player.GetActor()); Server.GetWorldManager().DoLogin(player.GetActor());
break; break;
//Chat Received //Chat Received
case 0x0003: case 0x0003:
@ -188,7 +81,7 @@ namespace FFXIVClassic_Map_Server
if (chatMessage.message.StartsWith("!")) if (chatMessage.message.StartsWith("!"))
{ {
if (cp.DoCommand(chatMessage.message, player)) if (cp.DoCommand(chatMessage.message, player))
continue; return; ;
} }
player.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(player.actorID, player.actorID, chatMessage.logType, player.GetActor().customDisplayName, chatMessage.message), false); player.GetActor().BroadcastPacket(SendMessagePacket.BuildPacket(player.actorID, player.actorID, chatMessage.logType, player.GetActor().customDisplayName, chatMessage.message), false);
@ -409,10 +302,7 @@ namespace FFXIVClassic_Map_Server
subpacket.DebugPrintSubPacket(); subpacket.DebugPrintSubPacket();
break; break;
} }
}
else
packet.DebugPrintPacket();
}
} }
} }

View File

@ -26,7 +26,7 @@ namespace FFXIVClassic_Map_Server
private Socket mServerSocket; private Socket mServerSocket;
private Dictionary<uint, ConnectedPlayer> mConnectedPlayerList = new Dictionary<uint, ConnectedPlayer>(); private Dictionary<uint, ConnectedPlayer> mConnectedPlayerList = new Dictionary<uint, ConnectedPlayer>();
private List<ClientConnection> mConnectionList = new List<ClientConnection>(); private ZoneConnection mWorldConnection = new ZoneConnection();
private LuaEngine mLuaEngine = new LuaEngine(); private LuaEngine mLuaEngine = new LuaEngine();
private static WorldManager mWorldManager; private static WorldManager mWorldManager;
@ -119,7 +119,7 @@ namespace FFXIVClassic_Map_Server
Program.Log.Info("Map Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port); Program.Log.Info("Map Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
Console.ForegroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Gray;
mProcessor = new PacketProcessor(this, mConnectedPlayerList, mConnectionList); mProcessor = new PacketProcessor(this, mConnectedPlayerList);
//mGameThread = new Thread(new ThreadStart(mProcessor.update)); //mGameThread = new Thread(new ThreadStart(mProcessor.update));
//mGameThread.Start(); //mGameThread.Start();
@ -138,20 +138,17 @@ namespace FFXIVClassic_Map_Server
#region Socket Handling #region Socket Handling
private void AcceptCallback(IAsyncResult result) private void AcceptCallback(IAsyncResult result)
{ {
ClientConnection conn = null; ZoneConnection conn = null;
Socket socket = (System.Net.Sockets.Socket)result.AsyncState; Socket socket = (System.Net.Sockets.Socket)result.AsyncState;
try try
{ {
conn = new ClientConnection(); conn = new ZoneConnection();
conn.socket = socket.EndAccept(result); conn.socket = socket.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE]; conn.buffer = new byte[BUFFER_SIZE];
lock (mConnectionList) mWorldConnection = conn;
{
mConnectionList.Add(conn);
}
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port); Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
//Queue recieving of data from the connection //Queue recieving of data from the connection
@ -163,11 +160,7 @@ namespace FFXIVClassic_Map_Server
{ {
if (conn != null) if (conn != null)
{ {
mWorldConnection = null;
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
} }
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket); mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
} }
@ -175,10 +168,7 @@ namespace FFXIVClassic_Map_Server
{ {
if (conn != null) if (conn != null)
{ {
lock (mConnectionList) mWorldConnection = null;
{
mConnectionList.Remove(conn);
}
} }
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket); mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
} }
@ -208,20 +198,13 @@ namespace FFXIVClassic_Map_Server
/// <param name="result"></param> /// <param name="result"></param>
private void ReceiveCallback(IAsyncResult result) private void ReceiveCallback(IAsyncResult result)
{ {
ClientConnection conn = (ClientConnection)result.AsyncState; ZoneConnection conn = (ZoneConnection)result.AsyncState;
//Check if disconnected //Check if disconnected
if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0)) if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0))
{ {
if (mConnectedPlayerList.ContainsKey(conn.owner)) mWorldConnection = null;
mConnectedPlayerList.Remove(conn.owner); Program.Log.Info("Disconnected from world server!");
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
if (conn.connType == BasePacket.TYPE_ZONE)
Program.Log.Info("{0} has disconnected.", conn.owner == 0 ? conn.GetAddress() : "User " + conn.owner);
return;
} }
try try
@ -237,13 +220,13 @@ namespace FFXIVClassic_Map_Server
//Build packets until can no longer or out of data //Build packets until can no longer or out of data
while (true) while (true)
{ {
BasePacket basePacket = BuildPacket(ref offset, conn.buffer, bytesRead); SubPacket subPacket = SubPacket.CreatePacket(ref offset, conn.buffer, bytesRead);
//If can't build packet, break, else process another //If can't build packet, break, else process another
if (basePacket == null) if (subPacket == null)
break; break;
else else
mProcessor.ProcessPacket(conn, basePacket); mProcessor.ProcessPacket(conn, subPacket);
} }
//Not all bytes consumed, transfer leftover to beginning //Not all bytes consumed, transfer leftover to beginning
@ -264,62 +247,19 @@ namespace FFXIVClassic_Map_Server
} }
else else
{ {
Program.Log.Info("{0} has disconnected.", conn.owner == 0 ? conn.GetAddress() : "User " + conn.owner); mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
} }
} }
catch (SocketException) catch (SocketException)
{ {
if (conn.socket != null) if (conn.socket != null)
{ {
Program.Log.Info("{0} has disconnected.", conn.owner == 0 ? conn.GetAddress() : "User " + conn.owner); mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
lock (mConnectionList)
{
mConnectionList.Remove(conn);
} }
} }
} }
}
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public BasePacket BuildPacket(ref int offset, byte[] buffer, int bytesRead)
{
BasePacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new BasePacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
#endregion #endregion

View File

@ -7,27 +7,22 @@ using System.Net;
namespace FFXIVClassic_Map_Server namespace FFXIVClassic_Map_Server
{ {
class ClientConnection class ZoneConnection
{ {
//Connection stuff //Connection stuff
public Blowfish blowfish;
public Socket socket; public Socket socket;
public byte[] buffer; public byte[] buffer;
private BlockingCollection<BasePacket> SendPacketQueue = new BlockingCollection<BasePacket>(1000); private BlockingCollection<SubPacket> SendPacketQueue = new BlockingCollection<SubPacket>(1000);
public int lastPartialSize = 0; public int lastPartialSize = 0;
//Instance Stuff
public uint owner = 0;
public int connType = 0;
public void QueuePacket(BasePacket packet) public void QueuePacket(BasePacket packet)
{ {
SendPacketQueue.Add(packet); //SendPacketQueue.Add(packet);
} }
public void QueuePacket(SubPacket subpacket, bool isAuthed, bool isEncrypted) public void QueuePacket(SubPacket subpacket, bool isAuthed, bool isEncrypted)
{ {
SendPacketQueue.Add(BasePacket.CreatePacket(subpacket, isAuthed, isEncrypted)); SendPacketQueue.Add(subpacket);
} }
public void FlushQueuedSendPackets() public void FlushQueuedSendPackets()
@ -37,9 +32,9 @@ namespace FFXIVClassic_Map_Server
while (SendPacketQueue.Count > 0) while (SendPacketQueue.Count > 0)
{ {
BasePacket packet = SendPacketQueue.Take(); SubPacket packet = SendPacketQueue.Take();
byte[] packetBytes = packet.GetPacketBytes(); byte[] packetBytes = packet.GetBytes();
try try
{ {

View File

@ -20,50 +20,20 @@ namespace FFXIVClassic_Map_Server.dataobjects
public uint languageCode = 1; public uint languageCode = 1;
private ClientConnection zoneConnection; private ZoneConnection zoneConnection;
private ClientConnection chatConnection;
private uint lastPingPacket = Utils.UnixTimeStampUTC(); private uint lastPingPacket = Utils.UnixTimeStampUTC();
public string errorMessage = ""; public string errorMessage = "";
public ConnectedPlayer(uint actorId) public ConnectedPlayer(ZoneConnection zc, uint actorId)
{ {
zoneConnection = zc;
this.actorID = actorId; this.actorID = actorId;
playerActor = new Player(this, actorId); playerActor = new Player(this, actorId);
actorInstanceList.Add(playerActor); actorInstanceList.Add(playerActor);
} }
public void SetConnection(int type, ClientConnection conn)
{
conn.connType = type;
switch (type)
{
case BasePacket.TYPE_ZONE:
zoneConnection = conn;
break;
case BasePacket.TYPE_CHAT:
chatConnection = conn;
break;
}
}
public bool IsClientConnectionsReady()
{
return (zoneConnection != null && chatConnection != null);
}
public void Disconnect()
{
zoneConnection.Disconnect();
chatConnection.Disconnect();
}
public bool IsDisconnected()
{
return (!zoneConnection.IsConnected() && !chatConnection.IsConnected());
}
public void QueuePacket(BasePacket basePacket) public void QueuePacket(BasePacket basePacket)
{ {
zoneConnection.QueuePacket(basePacket); zoneConnection.QueuePacket(basePacket);

View File

@ -18,15 +18,15 @@ namespace FFXIVClassic_World_Server
public static bool Load() public static bool Load()
{ {
Program.Log.Info("Loading config.ini"); Program.Log.Info("Loading world_config.ini");
if (!File.Exists("./config.ini")) if (!File.Exists("./world_config.ini"))
{ {
Program.Log.Error("FILE NOT FOUND!"); Program.Log.Error("FILE NOT FOUND!");
return false; return false;
} }
INIFile configIni = new INIFile("./config.ini"); INIFile configIni = new INIFile("./world_config.ini");
ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1"); ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1");
ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54992"); ConfigConstants.OPTIONS_PORT = configIni.GetValue("General", "server_port", "54992");

View File

@ -16,6 +16,9 @@ namespace FFXIVClassic_World_Server.DataObjects
public readonly int[] ownedZoneIds; public readonly int[] ownedZoneIds;
public bool isConnected = false; public bool isConnected = false;
public Socket zoneServerConnection; public Socket zoneServerConnection;
private ClientConnection conn;
private byte[] buffer = new byte[0xFFFF];
public ZoneServer(string ip, int port) public ZoneServer(string ip, int port)
{ {
@ -34,6 +37,18 @@ namespace FFXIVClassic_World_Server.DataObjects
{ {
zoneServerConnection.Connect(remoteEP); zoneServerConnection.Connect(remoteEP);
isConnected = true; isConnected = true;
conn = new ClientConnection();
conn.socket = zoneServerConnection;
conn.buffer = new byte[0xFFFF];
try
{
zoneServerConnection.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
}
catch (Exception e)
{
throw new ApplicationException("Error occured starting listeners, check inner exception", e);
}
} }
catch (Exception e) catch (Exception e)
{ Program.Log.Error("Failed to connect"); return; } { Program.Log.Error("Failed to connect"); return; }
@ -54,5 +69,71 @@ namespace FFXIVClassic_World_Server.DataObjects
} }
} }
private void ReceiveCallback(IAsyncResult result)
{
ClientConnection conn = (ClientConnection)result.AsyncState;
//Check if disconnected
if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0))
{
conn = null;
isConnected = false;
Program.Log.Info("Zone server @ {0}:{1} disconnected!", zoneServerIp, zoneServerPort);
return;
}
try
{
int bytesRead = conn.socket.EndReceive(result);
bytesRead += conn.lastPartialSize;
if (bytesRead >= 0)
{
int offset = 0;
//Build packets until can no longer or out of data
while (true)
{
SubPacket subpacket = SubPacket.CreatePacket(ref offset, conn.buffer, bytesRead);
//If can't build packet, break, else process another
if (subpacket == null)
break;
else
Server.GetServer().OnReceiveSubPacketFromZone(this, subpacket);
}
//Not all bytes consumed, transfer leftover to beginning
if (offset < bytesRead)
Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset);
conn.lastPartialSize = bytesRead - offset;
//Build any queued subpackets into basepackets and send
conn.FlushQueuedSendPackets();
if (offset < bytesRead)
//Need offset since not all bytes consumed
conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
else
//All bytes consumed, full buffer available
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
}
else
{
conn = null;
isConnected = false;
Program.Log.Info("Zone server @ {0}:{1} disconnected!", zoneServerIp, zoneServerPort);
}
}
catch (SocketException)
{
conn = null;
isConnected = false;
Program.Log.Info("Zone server @ {0}:{1} disconnected!", zoneServerIp, zoneServerPort);
}
}
} }
} }

View File

@ -53,6 +53,7 @@
<!-- add your logging rules here --> <!-- add your logging rules here -->
<logger name='*' minlevel='Trace' writeTo='file' /> <logger name='*' minlevel='Trace' writeTo='file' />
<logger name='FFXIVClassic_World_Server.Program' minlevel='Trace' writeTo='console' /> <logger name='FFXIVClassic_World_Server.Program' minlevel='Trace' writeTo='console' />
<logger name='FFXIVClassic.Common.*' minlevel='Debug' writeTo='packets' />
<!-- <!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f" Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
<logger name="*" minlevel="Debug" writeTo="f" /> <logger name="*" minlevel="Debug" writeTo="f" />

View File

@ -75,6 +75,7 @@ namespace FFXIVClassic_World_Server
{ {
//Send to the correct zone server //Send to the correct zone server
uint targetSession = subpacket.header.targetId; uint targetSession = subpacket.header.targetId;
mServer.GetSession(targetSession).routing1 = Server.GetServer().GetWorldManager().mZoneServerList["127.0.0.1:1989"];
if (mServer.GetSession(targetSession).routing1 != null) if (mServer.GetSession(targetSession).routing1 != null)
mServer.GetSession(targetSession).routing1.SendPacket(subpacket); mServer.GetSession(targetSession).routing1.SendPacket(subpacket);

View File

@ -31,14 +31,18 @@ namespace FFXIVClassic_World_Server.Packets.Send
} }
byte[] reply2Data = { byte[] reply2Data = {
0x66, 0x00, 0x00, 0x00, 0xC8, 0xD6, 0xAF, 0x2B, 0x38, 0x2B, 0x5F, 0x26, 0xB8, 0x8D, 0xF0, 0x2B, 0x6c, 0x00, 0x00, 0x00, 0xC8, 0xD6, 0xAF, 0x2B, 0x38, 0x2B, 0x5F, 0x26, 0xB8, 0x8D, 0xF0, 0x2B,
0xC8, 0xFD, 0x85, 0xFE, 0xA8, 0x7C, 0x5B, 0x09, 0x38, 0x2B, 0x5F, 0x26, 0xC8, 0xD6, 0xAF, 0x2B, 0xC8, 0xFD, 0x85, 0xFE, 0xA8, 0x7C, 0x5B, 0x09, 0x38, 0x2B, 0x5F, 0x26, 0xC8, 0xD6, 0xAF, 0x2B,
0xB8, 0x8D, 0xF0, 0x2B, 0x88, 0xAF, 0x5E, 0x26 0xB8, 0x8D, 0xF0, 0x2B, 0x88, 0xAF, 0x5E, 0x26
}; };
data = reply2Data; /*
0x6c, 0x00, 0x00, 0x00, 0xC8, 0xD6, 0xAF, 0x2B, 0x38, 0x2B, 0x5F, 0x26, 0xB8, 0x8D, 0xF0, 0x2B,
0xC8, 0xFD, 0x85, 0xFE, 0xA8, 0x7C, 0x5B, 0x09, 0x38, 0x2B, 0x5F, 0x26, 0xC8, 0xD6, 0xAF, 0x2B,
0xB8, 0x8D, 0xF0, 0x2B, 0x88, 0xAF, 0x5E, 0x26
*/
return new SubPacket(false, OPCODE, 0, 0, data); return new SubPacket(false, OPCODE, 0, 0, reply2Data);
} }
} }
} }

View File

@ -77,54 +77,6 @@ namespace FFXIVClassic_World_Server
return true; return true;
} }
#region Socket Handling
private void AcceptCallback(IAsyncResult result)
{
ClientConnection conn = null;
Socket socket = (System.Net.Sockets.Socket)result.AsyncState;
try
{
conn = new ClientConnection();
conn.socket = socket.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE];
lock (mConnectionList)
{
mConnectionList.Add(conn);
}
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
//Queue recieving of data from the connection
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
//Queue the accept of the next incomming connection
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (SocketException)
{
if (conn != null)
{
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception)
{
if (conn != null)
{
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
}
public void AddSession(ClientConnection connection, Session.Channel type, uint id) public void AddSession(ClientConnection connection, Session.Channel type, uint id)
{ {
Session session = new Session(id, connection, type); Session session = new Session(id, connection, type);
@ -182,6 +134,70 @@ namespace FFXIVClassic_World_Server
return null; return null;
} }
public void OnReceiveSubPacketFromZone(ZoneServer zoneServer, SubPacket subpacket)
{
uint sessionId = subpacket.header.targetId;
if (mZoneSessionList.ContainsKey(sessionId))
{
ClientConnection conn = mZoneSessionList[sessionId].clientConnection;
conn.QueuePacket(subpacket, true, false);
}
}
public WorldManager GetWorldManager()
{
return mWorldManager;
}
#region Socket Handling
private void AcceptCallback(IAsyncResult result)
{
ClientConnection conn = null;
Socket socket = (System.Net.Sockets.Socket)result.AsyncState;
try
{
conn = new ClientConnection();
conn.socket = socket.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE];
lock (mConnectionList)
{
mConnectionList.Add(conn);
}
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
//Queue recieving of data from the connection
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
//Queue the accept of the next incomming connection
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (SocketException)
{
if (conn != null)
{
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception)
{
if (conn != null)
{
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
}
/// <summary> /// <summary>
/// Receive Callback. Reads in incoming data, converting them to base packets. Base packets are sent to be parsed. If not enough data at the end to build a basepacket, move to the beginning and prepend. /// Receive Callback. Reads in incoming data, converting them to base packets. Base packets are sent to be parsed. If not enough data at the end to build a basepacket, move to the beginning and prepend.
/// </summary> /// </summary>
@ -214,7 +230,7 @@ namespace FFXIVClassic_World_Server
//Build packets until can no longer or out of data //Build packets until can no longer or out of data
while (true) while (true)
{ {
BasePacket basePacket = BuildPacket(ref offset, conn.buffer, bytesRead); BasePacket basePacket = BasePacket.CreatePacket(ref offset, conn.buffer, bytesRead);
//If can't build packet, break, else process another //If can't build packet, break, else process another
if (basePacket == null) if (basePacket == null)
@ -264,48 +280,7 @@ namespace FFXIVClassic_World_Server
} }
} }
/// <summary>
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
/// </summary>
/// <param name="offset">Current offset in buffer.</param>
/// <param name="buffer">Incoming buffer.</param>
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
public BasePacket BuildPacket(ref int offset, byte[] buffer, int bytesRead)
{
BasePacket newPacket = null;
//Too small to even get length
if (bytesRead <= offset)
return null;
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
//Too small to whole packet
if (bytesRead < offset + packetSize)
return null;
if (buffer.Length < offset + packetSize)
return null;
try
{
newPacket = new BasePacket(buffer, ref offset);
}
catch (OverflowException)
{
return null;
}
return newPacket;
}
#endregion #endregion
public WorldManager GetWorldManager()
{
return mWorldManager;
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace FFXIVClassic_World_Server
class WorldManager class WorldManager
{ {
private Server mServer; private Server mServer;
private Dictionary<string, ZoneServer> mZoneServerList; public Dictionary<string, ZoneServer> mZoneServerList;
public WorldManager(Server server) public WorldManager(Server server)
{ {