project-meteor-server/FFXIVClassic Map Server/Server.cs
yogurt c5ce2ec771 Combat additions
Added formulas for base EXP gain and chain experience
Added basic scripts for most player abilities and effects
Added stat gains for some abilities
Changed status flags
Fixed bug with player death
Fixed bug where auto attacks didnt work when not locked on
Added traits
2018-04-18 16:06:41 -05:00

309 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic.Common;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
namespace FFXIVClassic_Map_Server
{
class Server
{
public const int FFXIV_MAP_PORT = 54992;
public const int BUFFER_SIZE = 0xFFFF; //Max basepacket size is 0xFFFF
public const int BACKLOG = 100;
public const string STATIC_ACTORS_PATH = "./staticactors.bin";
private static Server mSelf;
private Socket mServerSocket;
private Dictionary<uint, Session> mSessionList = new Dictionary<uint, Session>();
private static CommandProcessor mCommandProcessor = new CommandProcessor();
private static ZoneConnection mWorldConnection = new ZoneConnection();
private static WorldManager mWorldManager;
private static Dictionary<uint, ItemData> mGamedataItems;
private static Dictionary<uint, GuildleveData> mGamedataGuildleves;
private static StaticActors mStaticActors;
private PacketProcessor mProcessor;
public Server()
{
mSelf = this;
}
public bool StartServer()
{
mStaticActors = new StaticActors(STATIC_ACTORS_PATH);
mGamedataItems = Database.GetItemGamedata();
Program.Log.Info("Loaded {0} items.", mGamedataItems.Count);
mGamedataGuildleves = Database.GetGuildleveGamedata();
Program.Log.Info("Loaded {0} guildleves.", mGamedataGuildleves.Count);
mWorldManager = new WorldManager(this);
mWorldManager.LoadZoneList();
mWorldManager.LoadZoneEntranceList();
mWorldManager.LoadSeamlessBoundryList();
mWorldManager.LoadActorClasses();
mWorldManager.LoadSpawnLocations();
mWorldManager.LoadBattleNpcs();
mWorldManager.LoadStatusEffects();
mWorldManager.LoadBattleCommands();
mWorldManager.LoadBattleTraits();
mWorldManager.SpawnAllActors();
mWorldManager.StartZoneThread();
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), int.Parse(ConfigConstants.OPTIONS_PORT));
try
{
mServerSocket = new Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
catch (Exception e)
{
throw new ApplicationException("Could not Create socket, check to make sure not duplicating port", e);
}
try
{
mServerSocket.Bind(serverEndPoint);
mServerSocket.Listen(BACKLOG);
}
catch (Exception e)
{
throw new ApplicationException("Error occured while binding socket, check inner exception", e);
}
try
{
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception e)
{
throw new ApplicationException("Error occured starting listeners, check inner exception", e);
}
Console.ForegroundColor = ConsoleColor.White;
Program.Log.Info("Map Server has started @ {0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
Console.ForegroundColor = ConsoleColor.Gray;
mProcessor = new PacketProcessor(this);
//mGameThread = new Thread(new ThreadStart(mProcessor.update));
//mGameThread.Start();
return true;
}
#region Session Handling
public Session AddSession(uint id)
{
if (mSessionList.ContainsKey(id))
return mSessionList[id];
Session session = new Session(id);
mSessionList.Add(id, session);
return session;
}
public void RemoveSession(uint id)
{
if (mSessionList.ContainsKey(id))
{
mSessionList.Remove(id);
}
}
public Session GetSession(uint id)
{
if (mSessionList.ContainsKey(id))
return mSessionList[id];
else
return null;
}
public Session GetSession(string name)
{
foreach (Session s in mSessionList.Values)
{
if (s.GetActor().customDisplayName.ToLower().Equals(name.ToLower()))
return s;
}
return null;
}
public Dictionary<uint, Session> GetSessionList()
{
return mSessionList;
}
#endregion
#region Socket Handling
private void AcceptCallback(IAsyncResult result)
{
ZoneConnection conn = null;
Socket socket = (System.Net.Sockets.Socket)result.AsyncState;
try
{
conn = new ZoneConnection();
conn.socket = socket.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE];
mWorldConnection = conn;
Program.Log.Info("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
//Queue recieving of data from the connection
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
//Queue the accept of the next incomming connection
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (SocketException)
{
if (conn != null)
{
mWorldConnection = null;
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
catch (Exception)
{
if (conn != null)
{
mWorldConnection = null;
}
mServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), mServerSocket);
}
}
/// <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.
/// </summary>
/// <param name="result"></param>
private void ReceiveCallback(IAsyncResult result)
{
ZoneConnection conn = (ZoneConnection)result.AsyncState;
//Check if disconnected
if ((conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0))
{
mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
}
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
mProcessor.ProcessPacket(conn, 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
{
mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
}
}
catch (SocketException)
{
if (conn.socket != null)
{
mWorldConnection = null;
Program.Log.Info("Disconnected from world server!");
}
}
}
#endregion
public static ZoneConnection GetWorldConnection()
{
return mWorldConnection;
}
public static Server GetServer()
{
return mSelf;
}
public static CommandProcessor GetCommandProcessor()
{
return mCommandProcessor;
}
public static WorldManager GetWorldManager()
{
return mWorldManager;
}
public static Dictionary<uint, ItemData> GetGamedataItems()
{
return mGamedataItems;
}
public static Actor GetStaticActors(uint id)
{
return mStaticActors.GetActor(id);
}
public static Actor GetStaticActors(string name)
{
return mStaticActors.FindStaticActor(name);
}
public static ItemData GetItemGamedata(uint id)
{
if (mGamedataItems.ContainsKey(id))
return mGamedataItems[id];
else
return null;
}
public static GuildleveData GetGuildleveGamedata(uint id)
{
if (mGamedataGuildleves.ContainsKey(id))
return mGamedataGuildleves[id];
else
return null;
}
}
}