1
0
Fork 0
This repository has been archived on 2019-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
Galactic_Colors_Control/Galactic Colors Control Server/Program.cs

306 lines
12 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Galactic_Colors_Control_Common;
using Galactic_Colors_Control_Common.Protocol;
using MyCommon;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using System.Xml;
using Console = MyCommon.ConsoleIO;
//TODO gui parties pages
namespace Galactic_Colors_Control_Server
{
internal class Server
{
private const int BUFFER_SIZE = 2048;
private static readonly Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public static bool _debug = false;
public static bool _dev = false;
public static bool _run = true;
public static bool _open = true;
private static readonly byte[] buffer = new byte[BUFFER_SIZE];
public static Dictionary<Socket, Client> clients { get; private set; } = new Dictionary<Socket, Client>();
public static object clients_lock = new object();
private static int partyID = 0;
private static object partyID_lock = new object();
public static Dictionary<int, Party> parties { get; private set; } = new Dictionary<int, Party>(); //TODO add lock
      public static int selectedParty = -1; //TODO add lock
public static Config config = new Config();
public static Logger logger = new Logger();
public static MultiLang multilang = new MultiLang();
public static Timer UpdateTimer;
public static Thread CheckConnected = new Thread(CheckConnectedLoop);
/// <summary>
/// Server Main thread
/// </summary>
private static void Main(string[] args)
{
Console.Title = "Galactic Colors Control Server";
logger.Write(Console.Title + " " + Assembly.GetEntryAssembly().GetName().Version.ToString(), Logger.logType.fatal);
config = XmlManager.Load<Config>(AppDomain.CurrentDomain.BaseDirectory + "Config.xml", XmlManager.LoadMode.ReadCreateOrReplace, XmlReader.Create("ConfigSchema.xsd"), logger);
config.PostSave();
logger.Initialise(config.logPath, config.logBackColor, config.logForeColor, config.logLevel, _debug, _dev);
multilang.Initialise(Common.dictionary);
if (args.Length > 0)
{
switch (args[0])
{
case "--debug":
_debug = true;
logger.Write("SERVER IS IN DEBUG MODE !", Logger.logType.error, Logger.logConsole.show);
break;
case "--dev":
_dev = true;
logger.Write("SERVER IS IN DEV MODE !", Logger.logType.error, Logger.logConsole.show);
break;
default:
Console.Write(new ColorStrings(new ColorString("Use"), new ColorString(" --debug", System.ConsoleColor.Red), new ColorString(" or"), new ColorString(" --dev", System.ConsoleColor.White, System.ConsoleColor.Red)));
break;
}
}
if (Type.GetType("Mono.Runtime") != null) { logger.Write("Using Mono", Logger.logType.warm, Logger.logConsole.show); }
SetupServer();
ConsoleLoop();
CloseAllSockets();
}
/// <summary>
/// Initialise server and start threads.
/// </summary>
private static void SetupServer()
{
Commands.Manager.Load();
logger.Write("Setting up server on *:" + config.port, Logger.logType.warm);
logger.Write("Size:" + config.size, Logger.logType.debug);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, config.port));
serverSocket.Listen(0);
serverSocket.BeginAccept(AcceptCallback, null);
CheckConnected.Start();
UpdateTimer = new Timer(UpdateCallback, null, 0, 200);
logger.Write("Server setup complete", Logger.logType.info);
}
/// <summary>
/// Wait console commands and execute them.
/// </summary>
private static void ConsoleLoop()
{
while (_run)
{
string ConsoleInput = Console.Read();
string[] args = Strings.SplitArgs(ConsoleInput);
Console.Write(new ColorStrings(Parser.GetResultText(new ResultData(-1, Commands.Manager.Execute(args, null, true)), config.lang, multilang)));
ConsoleInput = null;
}
}
/// <summary>
/// Close all connected client.
/// </summary>
private static void CloseAllSockets()
{
logger.Write("Stoping server", Logger.logType.warm, Logger.logConsole.show);
Utilities.Broadcast(new EventData(EventTypes.ServerKick, Strings.ArrayFromStrings("Close")));
config.PreSave();
XmlManager.Save(config, AppDomain.CurrentDomain.BaseDirectory + "Config.xml", logger);
config.PostSave();
foreach (Socket socket in clients.Keys)
{
socket.Shutdown(SocketShutdown.Both);
logger.Write("Shutdown " + Utilities.GetName(socket), Logger.logType.debug);
}
serverSocket.Close();
CheckConnected.Join(2000);
logger.Write("Server stoped", Logger.logType.info);
logger.Join();
}
/// <summary>
/// Wait a client and check if is correct
/// </summary>
private static void AcceptCallback(IAsyncResult AR)
{
Socket socket;
try
{
socket = serverSocket.EndAccept(AR);
}
catch (ObjectDisposedException)
{
return;
}
if (_open)
{
if (clients.Count < config.size)
{
AddClient(socket);
}
else
{
logger.Write("Client can't join from " + ((IPEndPoint)socket.LocalEndPoint).Address.ToString() + " no more space", Logger.logType.warm);
Utilities.Send(socket, new EventData(EventTypes.ServerKick, Strings.ArrayFromStrings("Space")));
socket.Close();
}
}
else
{
logger.Write("Client can't join from " + ((IPEndPoint)socket.LocalEndPoint).Address.ToString() + " server closed", Logger.logType.info);
Utilities.Send(socket, new EventData(EventTypes.ServerKick, Strings.ArrayFromStrings("Close")));
socket.Close();
}
serverSocket.BeginAccept(AcceptCallback, null);
}
/// <summary>
/// Add client and initialise receive
/// </summary>
private static void AddClient(Socket socket)
{
Client client = new Client();
clients.Add(socket, client);
socket.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCallback, socket);
logger.Write("Client connection from " + Utilities.GetName(socket), Logger.logType.info);
logger.Write("Size: " + clients.Count + "/" + config.size, Logger.logType.dev);
if (clients.Count >= config.size)
{
logger.Write("Server full", Logger.logType.warm, Logger.logConsole.show);
}
}
/// <summary>
/// Wait a client commands and execute them
/// </summary>
private static void ReceiveCallback(IAsyncResult AR)
{
Socket current = (Socket)AR.AsyncState;
int received;
try
{
received = current.EndReceive(AR);
}
catch (Exception e)
{
RemoveClient(current, e.GetType().Name);
return;
}
var data = new byte[received];
Array.Copy(buffer, data, received);
Data packet = Data.FromBytes(ref data);
if (packet != null)
{
switch (packet.GetType().Name)
{
case "RequestData":
RequestData req = (RequestData)packet;
Utilities.Send(current, new ResultData(req.id, Commands.Manager.Execute(req.args, current)));
break;
default:
logger.Write("Wrong packet from " + Utilities.GetName(current), Logger.logType.error);
break;
}
}
else
{
logger.Write("Wrong packet from " + Utilities.GetName(current), Logger.logType.error);
}
if (clients.ContainsKey(current)) { current.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current); }
}
private static void UpdateCallback(object state)
{
foreach (int partyId in parties.Keys.ToArray())
{
if (parties[partyId].isBuzy)
{
logger.Write("Party " + partyId + " overload", Logger.logType.error);
}
else
{
parties[partyId].isBuzy = true;
new Thread(parties[partyId].Update).Start();
}
}
}
private static void CheckConnectedLoop()
{
while (_run)
{
foreach (Socket current in clients.Keys.ToArray())
{
try
{
if ((current.Poll(10, SelectMode.SelectRead) && current.Available == 0) || !current.Connected)
{
RemoveClient(current, "NotConnected");
}
}
catch { }
Thread.Sleep(200);
}
}
}
public static void RemoveClient(Socket current, string reason = null)
{
//TODO add leave party for check empty parties
lock (clients_lock)
{
if (clients.ContainsKey(current))
{
string username = Utilities.GetName(current);
logger.Write("Client forcefully disconnected from " + username + (reason != null ? " : " + reason : ""), Logger.logType.info);
bool connected = clients[current].status != -1;
logger.Write("Size: " + clients.Count + "/" + config.size, Logger.logType.debug);
current.Close(); // Don't shutdown because the socket may be disposed and its disconnected anyway.
clients.Remove(current);
if (connected) { Utilities.Broadcast(new EventData(EventTypes.ServerLeave, Strings.ArrayFromStrings(username))); }
}
else
{
logger.Write("Client forcefully disconnected : ObjectDisposedException", Logger.logType.warm);
}
}
}
/// <summary>
/// Add new party with index
/// </summary>
/// <param name="party">Party to add</param>
public static void AddParty(Party party)
{
parties.Add(GetPartyID(), party);
}
public static int GetPartyID(bool indent = true)
{
lock (partyID_lock)
{
if (indent) { partyID++; }
return partyID;
}
}
}
}