PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/other/Springie/Springie/Main.cs

http://zero-k.googlecode.com/
C# | 309 lines | 264 code | 30 blank | 15 comment | 51 complexity | b76714c51b708fc7b54ec5415cd0cb3c MD5 | raw file
Possible License(s): GPL-2.0, MIT, BSD-3-Clause, GPL-3.0, LGPL-2.1
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Timers;
  9. using System.Xml.Serialization;
  10. using LobbyClient;
  11. using PlasmaShared;
  12. using PlasmaShared.SpringieInterfaceReference;
  13. using Springie.autohost;
  14. namespace Springie
  15. {
  16. /// <summary>
  17. /// Holds and handles autohost instances
  18. /// </summary>
  19. ///
  20. public class Main
  21. {
  22. public const string ConfigMain = "main.xml";
  23. const int ConfigUpdatePeriod = 60;
  24. const int JugglePeriod = 61;
  25. const int MinJuggleDelay = 5;
  26. readonly List<AutoHost> autoHosts = new List<AutoHost>();
  27. List<AutoHost> deletionCandidate = new List<AutoHost>();
  28. DateTime lastConfigUpdate = DateTime.Now;
  29. DateTime lastJuggle = DateTime.Now;
  30. readonly Timer timer;
  31. public MainConfig Config;
  32. public PlasmaDownloader.PlasmaDownloader Downloader;
  33. public MetaDataCache MetaCache;
  34. public string RootWorkPath { get; private set; }
  35. public readonly SpringPaths paths;
  36. private bool forceJuggleNext;
  37. public Main(string path)
  38. {
  39. RootWorkPath = path;
  40. LoadConfig();
  41. Config.RestartCounter++;
  42. if (Config.RestartCounter > 3) Config.RestartCounter = 0;
  43. SaveConfig();
  44. paths = new SpringPaths(Path.GetDirectoryName(Config.ExecutableName), Config.SpringVersion, Config.DataDir);
  45. if (!string.IsNullOrEmpty(Config.ExecutableName)) paths.OverrideDedicatedServer(Config.ExecutableName);
  46. paths.MakeFolders();
  47. MetaCache = new MetaDataCache(paths, null);
  48. timer = new Timer(30000);
  49. timer.Elapsed += timer_Elapsed;
  50. timer.AutoReset = true;
  51. timer.Start();
  52. Downloader = new PlasmaDownloader.PlasmaDownloader(Config, null, paths);
  53. }
  54. public int GetFreeHostingPort()
  55. {
  56. lock (autoHosts)
  57. {
  58. var usedPorts = autoHosts.ToDictionary(x => x.hostingPort);
  59. var freePort =
  60. Enumerable.Range(Config.HostingPortStart + Config.RestartCounter * 100, Config.MaxInstances).FirstOrDefault(x => !usedPorts.ContainsKey(x) && VerifyUdpSocket(x));
  61. return freePort;
  62. }
  63. }
  64. public string JuggleNow()
  65. {
  66. try
  67. {
  68. lastJuggle = DateTime.Now;
  69. forceJuggleNext = false;
  70. using (var serv = new SpringieService())
  71. {
  72. serv.Timeout = 8000;
  73. JugglerAutohost[] data;
  74. lock (autoHosts)
  75. {
  76. data =
  77. autoHosts.Where(x => x.tas.MyBattle != null && x.SpawnConfig == null && x.config.Mode != AutohostMode.None).Select(
  78. x =>
  79. new JugglerAutohost()
  80. {
  81. LobbyContext = x.tas.MyBattle.GetContext(),
  82. RunningGameStartContext = (x.spring.IsRunning && !x.spring.IsBattleOver) ? x.spring.StartContext : null
  83. }).ToArray();
  84. }
  85. var ret = serv.JugglePlayers(data);
  86. if (ret != null)
  87. {
  88. if (ret.PlayerMoves != null)
  89. {
  90. /*
  91. foreach (var playermove in ret.PlayerMoves)
  92. {
  93. var ah =
  94. autoHosts.FirstOrDefault(x => x.tas.MyBattle != null && x.tas.MyBattle.Users.Any(y => y.Name == playermove.Name));
  95. if (ah != null) ah.ComMove(TasSayEventArgs.Default, new[] { playermove.Name, playermove.TargetAutohost });
  96. }
  97. */
  98. }
  99. if (ret.AutohostsToClose != null)
  100. {
  101. foreach (var ahToKill in ret.AutohostsToClose)
  102. {
  103. var ah = autoHosts.FirstOrDefault(x => x.tas.UserName == ahToKill);
  104. if (ah != null) StopAutohost(ah);
  105. }
  106. }
  107. return ret.Message;
  108. }
  109. }
  110. }
  111. catch (Exception ex)
  112. {
  113. Trace.TraceError("Error juggling: {0}", ex);
  114. return ex.ToString();
  115. }
  116. return null;
  117. }
  118. public void LoadConfig()
  119. {
  120. Config = new MainConfig();
  121. if (File.Exists(RootWorkPath + '/' + ConfigMain))
  122. {
  123. var s = new XmlSerializer(Config.GetType());
  124. var r = File.OpenText(RootWorkPath + '/' + ConfigMain);
  125. Config = (MainConfig)s.Deserialize(r);
  126. r.Close();
  127. }
  128. }
  129. public void PeriodicCheck()
  130. {
  131. if (DateTime.Now.Subtract(lastConfigUpdate).TotalSeconds > ConfigUpdatePeriod)
  132. {
  133. UpdateAll();
  134. lastConfigUpdate = DateTime.Now;
  135. }
  136. if (DateTime.Now.Subtract(lastJuggle).TotalSeconds > JugglePeriod || forceJuggleNext)
  137. {
  138. JuggleNow();
  139. }
  140. }
  141. public void RequestJuggle()
  142. {
  143. lastJuggle = DateTime.MinValue;
  144. forceJuggleNext = true;
  145. }
  146. public void SaveConfig()
  147. {
  148. var s = new XmlSerializer(Config.GetType());
  149. var f = File.OpenWrite(RootWorkPath + '/' + ConfigMain);
  150. f.SetLength(0);
  151. s.Serialize(f, Config);
  152. f.Close();
  153. }
  154. public void SpawnAutoHost(AhConfig config, SpawnConfig spawnData)
  155. {
  156. lock (autoHosts)
  157. {
  158. var ah = new AutoHost(MetaCache, config, GetFreeHostingPort(), spawnData);
  159. autoHosts.Add(ah);
  160. }
  161. }
  162. public void StopAll()
  163. {
  164. lock (autoHosts)
  165. {
  166. foreach (var ah in autoHosts) ah.Dispose();
  167. autoHosts.Clear();
  168. }
  169. }
  170. public void StopAutohost(AutoHost ah)
  171. {
  172. ah.Dispose();
  173. lock (autoHosts)
  174. {
  175. autoHosts.Remove(ah);
  176. }
  177. }
  178. public void UpdateAll()
  179. {
  180. try
  181. {
  182. using (var serv = new SpringieService())
  183. {
  184. serv.Timeout = 5000;
  185. var configs = serv.GetClusterConfigs(Config.ClusterNode);
  186. lock (autoHosts)
  187. {
  188. foreach (var conf in configs)
  189. {
  190. if (!autoHosts.Any(x => x.config.Login == conf.Login)) SpawnAutoHost(conf, null);
  191. else foreach (var ah in autoHosts.Where(x => x.config.Login == conf.Login && x.SpawnConfig == null)) ah.config = conf;
  192. }
  193. var todel = autoHosts.Where(x => !configs.Any(y => y.Login == x.config.Login)).ToList();
  194. foreach (var ah in todel) StopAutohost(ah);
  195. }
  196. }
  197. }
  198. catch (Exception ex) {
  199. Trace.TraceError("Error in periodic updates: {0}",ex);
  200. }
  201. }
  202. public static bool VerifyUdpSocket(int port)
  203. {
  204. try
  205. {
  206. using (var sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
  207. {
  208. var endpoint = new IPEndPoint(IPAddress.Loopback, port);
  209. sock.ExclusiveAddressUse = true;
  210. sock.Bind(endpoint);
  211. }
  212. }
  213. catch
  214. {
  215. return false;
  216. }
  217. return true;
  218. }
  219. void timer_Elapsed(object sender, ElapsedEventArgs e)
  220. {
  221. try
  222. {
  223. timer.Stop(); ;
  224. lock (autoHosts)
  225. {
  226. // spawned autohosts
  227. var spawnedToDel =
  228. autoHosts.Where(
  229. x => x.SpawnConfig != null && !x.spring.IsRunning && (x.tas.MyBattle == null || x.tas.MyBattle.Users.Count <= 1)).ToList();
  230. foreach (var ah in spawnedToDel.Where(x => deletionCandidate.Contains(x))) StopAutohost(ah); // delete those who are empty during 2 checks
  231. deletionCandidate = spawnedToDel;
  232. // autohosts which have clones
  233. var keys = autoHosts.Where(x => x.config.AutoSpawnClones).Select(x => x.config.Login).Distinct().ToList();
  234. foreach (var key in keys)
  235. {
  236. // 0-1 players = empty
  237. var empty =
  238. autoHosts.Where(
  239. x =>
  240. x.SpawnConfig == null && x.config.Login == key && !x.spring.IsRunning &&
  241. (x.tas.MyBattle == null || (x.tas.MyBattle.Users.Count <= 1 && !x.tas.MyUser.IsInGame))).ToList();
  242. if (empty.Count == 1) continue;
  243. else if (empty.Count == 0)
  244. {
  245. var existing = autoHosts.Where(x => x.config.Login == key).First();
  246. SpawnAutoHost(existing.config, null);
  247. }
  248. else // more than 1 empty running, stop all but 1
  249. {
  250. var minNumber = empty.Min(y => y.CloneNumber);
  251. foreach (var ah in empty.Where(x => x.CloneNumber != minNumber && x.SpawnConfig == null)) StopAutohost(ah);
  252. }
  253. }
  254. }
  255. }
  256. catch (Exception ex)
  257. {
  258. ErrorHandling.HandleException(ex, "While checking autohosts");
  259. }
  260. finally {
  261. timer.Start();
  262. }
  263. }
  264. }
  265. public class SpawnConfig
  266. {
  267. public string Mod;
  268. public string Owner;
  269. public string Password;
  270. public string Title;
  271. public SpawnConfig(string owner, Dictionary<string, string> config)
  272. {
  273. Owner = owner;
  274. config.TryGetValue("password", out Password);
  275. config.TryGetValue("mod", out Mod);
  276. config.TryGetValue("title", out Title);
  277. }
  278. }
  279. }