PageRenderTime 68ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/ZeroKLobby_NET4.0/LobbyClient/Legacy/TasClient.cs

https://github.com/db81/Zero-K-Infrastructure
C# | 1656 lines | 1342 code | 262 blank | 52 comment | 204 complexity | e257e4e4f7b3addc5de95386289e0b7d MD5 | raw file
Possible License(s): GPL-3.0, MIT

Large files files are truncated, but you can click here to view the full file

  1. #region using
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.NetworkInformation;
  9. using System.Net.Sockets;
  10. using System.Text;
  11. using System.Text.RegularExpressions;
  12. using System.Threading.Tasks;
  13. using System.Timers;
  14. using ZkData;
  15. #endregion
  16. namespace LobbyClient.Legacy
  17. {
  18. public class TasClient
  19. {
  20. public const int MaxAlliances = 16;
  21. public const int MaxTeams = 16;
  22. public ProtocolExtension Extensions { get; private set; }
  23. public delegate void Invoker();
  24. public delegate void Invoker<TArg>(TArg arg);
  25. public enum SayPlace
  26. {
  27. Channel,
  28. Battle,
  29. User,
  30. BattlePrivate
  31. };
  32. StringBuilder agreementText;
  33. readonly string appName = "UnknownClient";
  34. ServerConnection con;
  35. Dictionary<int, Battle> existingBattles = new Dictionary<int, Battle>();
  36. Dictionary<string, ExistingChannel> existingChannels = new Dictionary<string, ExistingChannel>();
  37. Dictionary<string, User> existingUsers = new Dictionary<string, User>();
  38. readonly bool forcedLocalIP = false;
  39. readonly Invoker<Invoker> guiThreadInvoker;
  40. bool isChanScanning;
  41. bool isLoggedIn;
  42. Dictionary<string, Channel> joinedChannels = new Dictionary<string, Channel>();
  43. int lastSpectatorCount;
  44. int lastUdpSourcePort;
  45. int lastUserBattleStatus;
  46. int lastUserStatus;
  47. readonly string localIp;
  48. bool lockToChangeTo;
  49. int mapChecksumToChangeTo;
  50. string mapToChangeTo;
  51. readonly Timer minuteTimer;
  52. int pingInterval = 30; // how often to ping server (in seconds)
  53. readonly Timer pingTimer;
  54. readonly Random random = new Random();
  55. public string serverHost { get; private set; }
  56. int serverPort;
  57. int serverUdpHolePunchingPort;
  58. string serverVersion;
  59. bool startingAfterUdpPunch;
  60. readonly Timer udpPunchingTimer = new Timer(400);
  61. // user info
  62. string username = "";
  63. public bool ConnectionFailed { get; private set; }
  64. public Dictionary<int, Battle> ExistingBattles { get { return existingBattles; } set { existingBattles = value; } }
  65. public Dictionary<string, ExistingChannel> ExistingChannels { get { return existingChannels; } }
  66. public Dictionary<string, User> ExistingUsers { get { return existingUsers; } set { existingUsers = value; } }
  67. public bool IsConnected { get { return con != null && con.IsConnected; } }
  68. public bool IsLoggedIn { get { return isLoggedIn; } }
  69. public Dictionary<string, Channel> JoinedChannels { get { return joinedChannels; } }
  70. public int MessageID { get; private set; }
  71. public Battle MyBattle { get; protected set; }
  72. public int MyBattleID { get; private set; }
  73. public UserBattleStatus MyBattleStatus
  74. {
  75. get
  76. {
  77. if (MyBattle != null) return MyBattle.Users.SingleOrDefault(x => x.Name == username);
  78. else return null;
  79. }
  80. }
  81. public User MyUser
  82. {
  83. get
  84. {
  85. User us;
  86. ExistingUsers.TryGetValue(username, out us);
  87. return us;
  88. }
  89. }
  90. public int PingInterval
  91. {
  92. get { return pingInterval; }
  93. set
  94. {
  95. pingInterval = value;
  96. pingTimer.Interval = pingInterval*1000;
  97. }
  98. }
  99. public string ServerSpringVersion { get; private set; }
  100. public string UserName { get { return username; } }
  101. public string UserPassword;
  102. public event EventHandler<TasEventAgreementRecieved> AgreementRecieved = delegate { };
  103. public event EventHandler<EventArgs<BotBattleStatus>> BattleBotAdded = delegate { };
  104. public event EventHandler<EventArgs<BotBattleStatus>> BattleBotRemoved = delegate { };
  105. public event EventHandler<EventArgs<BotBattleStatus>> BattleBotUpdated = delegate { };
  106. public event EventHandler<EventArgs<Battle>> BattleClosed = delegate { };
  107. public event EventHandler<TasEventArgs> BattleDetailsChanged = delegate { };
  108. public event EventHandler<TasEventArgs> BattleDisabledUnitsChanged = delegate { };
  109. public event EventHandler<EventArgs<Battle>> BattleEnded = delegate { }; // raised just after the battle is removed from the battle list
  110. public event EventHandler<EventArgs<Battle>> BattleEnding = delegate { }; // raised just before the battle is removed from the battle list
  111. public event EventHandler BattleForceQuit = delegate { }; // i was kicked from a battle (sent after LEFTBATTLE)
  112. public event EventHandler<EventArgs<Battle>> BattleFound = delegate { };
  113. public event EventHandler<BattleInfoEventArgs> BattleInfoChanged = delegate { };
  114. public event EventHandler<EventArgs<Battle>> BattleJoined = delegate { };
  115. public event EventHandler<BattleInfoEventArgs> BattleLockChanged = delegate { };
  116. public event EventHandler<BattleInfoEventArgs> BattleMapChanged = delegate { };
  117. public event EventHandler<TasEventArgs> BattleMyUserStatusChanged = delegate { };
  118. public event EventHandler<TasEventArgs> BattleOpenFailed = delegate { };
  119. public event EventHandler<TasEventArgs> BattleOpened = delegate { };
  120. public event EventHandler<EventArgs<User>> BattleStarted = delegate { };
  121. public event EventHandler<TasEventArgs> BattleUserIpRecieved = delegate { };
  122. public event EventHandler<BattleUserEventArgs> BattleUserJoined = delegate { };
  123. public event EventHandler<BattleUserEventArgs> BattleUserLeft = delegate { };
  124. public event EventHandler<TasEventArgs> BattleUserStatusChanged = delegate { };
  125. public event EventHandler<TasEventArgs> ChannelForceLeave = delegate { }; // i was kicked from a channel
  126. public event EventHandler<TasEventArgs> ChannelJoinFailed = delegate { };
  127. public event EventHandler<TasEventArgs> ChannelJoined = delegate { };
  128. public event EventHandler<CancelEventArgs<string>> ChannelLeaving = delegate { }; // raised before attempting to leave a channel
  129. public event EventHandler<TasEventArgs> ChannelLeft = delegate { };
  130. public event EventHandler<TasEventArgs> ChannelListDone = delegate { };
  131. public event EventHandler<TasEventArgs> ChannelTopicChanged = delegate { };
  132. public event EventHandler<TasEventArgs> ChannelUserAdded = delegate { };
  133. public event EventHandler<TasEventArgs> ChannelUserRemoved = delegate { };
  134. public event EventHandler<TasEventArgs> ChannelUsersAdded = delegate { }; // raised after a group of clients is recieved in a batch
  135. public event EventHandler<TasEventArgs> Connected = delegate { };
  136. public event EventHandler<TasEventArgs> ConnectionLost = delegate { };
  137. public event EventHandler<TasEventArgs> Failure = delegate { }; //this event is fired whenever any failure events fire
  138. public event EventHandler<CancelEventArgs<string>> FilterBattleByMod;
  139. public event EventHandler<EventArgs> HourChime = delegate { };
  140. public event EventHandler<TasInputArgs> Input = delegate { };
  141. public event EventHandler<TasEventArgs> JoinBattleFailed = delegate { };
  142. public event EventHandler<KickedFromServerEventArgs> KickedFromServer = delegate { };
  143. public event EventHandler<TasEventArgs> LoginAccepted = delegate { };
  144. public event EventHandler<TasEventArgs> LoginDenied = delegate { };
  145. public event EventHandler<EventArgs<Battle>> MyBattleEnded = delegate { }; // raised just after the battle is removed from the battle list
  146. public event EventHandler<TasEventArgs> MyBattleHostExited = delegate { };
  147. public event EventHandler<BattleInfoEventArgs> MyBattleMapChanged = delegate { };
  148. public event EventHandler<TasEventArgs> MyBattleStarted = delegate { };
  149. public event EventHandler<EventArgs<KeyValuePair<string, object[]>>> Output = delegate { }; // outgoing command and arguments
  150. public event EventHandler<CancelEventArgs<TasEventArgs>> PreviewChannelJoined = delegate { };
  151. public event EventHandler<CancelEventArgs<TasSayEventArgs>> PreviewSaid = delegate { };
  152. public event EventHandler<EventArgs<string>> Rang = delegate { };
  153. public event EventHandler<TasEventArgs> RegistrationAccepted = delegate { };
  154. public event EventHandler<TasEventArgs> RegistrationDenied = delegate { };
  155. public event EventHandler RequestBattleStatus = delegate { };
  156. public event EventHandler<TasSayEventArgs> Said = delegate { }; // this is fired when any kind of say message is recieved
  157. public event EventHandler<SayingEventArgs> Saying = delegate { }; // this client is trying to say somethign
  158. public event EventHandler<TasEventArgs> StartRectAdded = delegate { };
  159. public event EventHandler<TasEventArgs> StartRectRemoved = delegate { };
  160. public event EventHandler<TasEventArgs> TestLoginAccepted = delegate { };
  161. public event EventHandler<TasEventArgs> TestLoginDenied = delegate { };
  162. public event EventHandler<EventArgs<User>> UserAdded = delegate { };
  163. public event EventHandler<TasEventArgs> UserRemoved = delegate { };
  164. public event EventHandler<TasEventArgs> UserStatusChanged = delegate { };
  165. public event EventHandler<EventArgs<User>> UserExtensionsChanged = delegate { };
  166. public event EventHandler<EventArgs<User>> MyExtensionsChanged = delegate { };
  167. public event EventHandler<UserLobbyVersionEventArgs> UserLobbyVersionRecieved = delegate { };
  168. public event EventHandler<UserIPEventArgs> UserIPRecieved = delegate { };
  169. public event EventHandler<UserIDEventArgs> UserIDRecieved = delegate { };
  170. public TasClient(Invoker<Invoker> guiThreadInvoker, string appName, int cpu, bool invokeUserStatusChangedOnExtensions = false, string ipOverride = null)
  171. {
  172. this.cpu = cpu;
  173. this.appName = appName;
  174. this.guiThreadInvoker = guiThreadInvoker;
  175. con = new ServerConnection();
  176. con.ConnectionClosed += OnConnectionClosed;
  177. con.CommandRecieved += OnCommandRecieved;
  178. con.CommandSent += (s, e) => Output(this, e);
  179. if (!string.IsNullOrEmpty(ipOverride))
  180. {
  181. localIp = ipOverride;
  182. forcedLocalIP = true;
  183. }
  184. else
  185. {
  186. var addresses = Dns.GetHostAddresses(Dns.GetHostName());
  187. localIp = addresses[0].ToString();
  188. foreach (var adr in addresses)
  189. {
  190. if (adr.AddressFamily == AddressFamily.InterNetwork)
  191. {
  192. localIp = adr.ToString();
  193. break;
  194. }
  195. }
  196. }
  197. pingTimer = new Timer(pingInterval*1000) { AutoReset = true };
  198. pingTimer.Elapsed += OnPingTimer;
  199. pingTimer.Start();
  200. minuteTimer = new Timer(60000) { AutoReset = true };
  201. minuteTimer.Elapsed += (s, e) =>
  202. {
  203. if (DateTime.Now.Minute == 0)
  204. if (guiThreadInvoker != null) guiThreadInvoker(() => HourChime(this, new EventArgs()));
  205. else HourChime(this, new EventArgs());
  206. };
  207. minuteTimer.Start();
  208. udpPunchingTimer.Elapsed += udpPunchingTimer_Elapsed;
  209. udpPunchingTimer.AutoReset = true;
  210. ConnectionLost += RaiseFailure;
  211. LoginDenied += RaiseFailure;
  212. ChannelJoinFailed += RaiseFailure;
  213. BattleOpenFailed += RaiseFailure;
  214. }
  215. public void AcceptAgreement()
  216. {
  217. con.SendCommand("CONFIRMAGREEMENT");
  218. }
  219. public void AddBattleRectangle(int allyno, BattleRect rect)
  220. {
  221. {
  222. if (allyno < Spring.MaxAllies && allyno >= 0)
  223. {
  224. RemoveBattleRectangle(allyno);
  225. MyBattle.Rectangles.Add(allyno, rect);
  226. con.SendCommand("ADDSTARTRECT", allyno, rect.Left, rect.Top, rect.Right, rect.Bottom);
  227. }
  228. }
  229. }
  230. public void AddBot(string name, UserBattleStatus status, int teamColor, string aiDll)
  231. {
  232. if (name.Contains(" ")) throw new TasClientException("Bot name must not contain spaces. " + name);
  233. con.SendCommand("ADDBOT", name, status.ToInt(), teamColor, aiDll);
  234. }
  235. public void ChangeLock(bool lck)
  236. {
  237. if (lck != lockToChangeTo) UpdateBattleInfo(lck, mapToChangeTo, mapChecksumToChangeTo);
  238. }
  239. public void ChangeMap(string name, int checksum)
  240. {
  241. {
  242. mapToChangeTo = name;
  243. mapChecksumToChangeTo = checksum;
  244. UpdateBattleInfo(lockToChangeTo, name, checksum);
  245. }
  246. }
  247. public void ChangeMyBattleStatus(bool? spectate = null,
  248. bool? ready = null,
  249. SyncStatuses? syncStatus = null,
  250. int? side = null,
  251. int? ally = null,
  252. int? team = null)
  253. {
  254. var ubs = MyBattleStatus;
  255. if (ubs != null)
  256. {
  257. var clone = (UserBattleStatus)ubs.Clone();
  258. clone.SetFrom(lastUserBattleStatus, ubs.TeamColor);
  259. if (spectate.HasValue) clone.IsSpectator = spectate.Value;
  260. if (ready.HasValue) clone.IsReady = ready.Value;
  261. if (syncStatus.HasValue) clone.SyncStatus = syncStatus.Value;
  262. if (side.HasValue) clone.Side = side.Value;
  263. if (ally.HasValue) clone.AllyNumber = ally.Value;
  264. if (team.HasValue) clone.TeamNumber = team.Value;
  265. if (clone.ToInt() != lastUserBattleStatus)
  266. {
  267. SendMyBattleStatus(clone.ToInt(), clone.TeamColor);
  268. lastUserBattleStatus = clone.ToInt();
  269. }
  270. }
  271. }
  272. public void ChangeMyUserStatus(bool? isAway = null, bool? isInGame = null)
  273. {
  274. User u;
  275. if (MyUser != null) u = MyUser.Clone();
  276. else u = new User();
  277. u.FromInt(lastUserStatus);
  278. if (isAway != null) u.IsAway = isAway.Value;
  279. if (isInGame != null) u.IsInGame = isInGame.Value;
  280. if (MyUser == null || lastUserStatus != u.ToInt())
  281. {
  282. con.SendCommand("MYSTATUS", u.ToInt());
  283. lastUserStatus = u.ToInt();
  284. }
  285. }
  286. public void Connect(string host, int port)
  287. {
  288. serverHost = host;
  289. serverPort = port;
  290. MyBattle = null;
  291. MyBattleID = 0;
  292. ConnectionFailed = false;
  293. ExistingUsers = new Dictionary<string, User>();
  294. existingChannels = new Dictionary<string, ExistingChannel>();
  295. joinedChannels = new Dictionary<string, Channel>();
  296. existingBattles = new Dictionary<int, Battle>();
  297. isChanScanning = false;
  298. isLoggedIn = false;
  299. username = "";
  300. if (con.IsConnected) con.RequestClose();
  301. con.Connect(host, port, forcedLocalIP ? localIp : null);
  302. }
  303. public static DateTime ConvertMilisecondTime(string arg)
  304. {
  305. return (new DateTime(1970, 1, 1, 0, 0, 0)).AddMilliseconds(double.Parse(arg, System.Globalization.CultureInfo.InvariantCulture));
  306. }
  307. public void DisableUnits(params string[] units)
  308. {
  309. {
  310. var temp = new List<string>(units);
  311. foreach (var s in temp) if (!MyBattle.DisabledUnits.Contains(s)) MyBattle.DisabledUnits.Add(s);
  312. if (MyBattle.DisabledUnits.Count > 0)
  313. {
  314. con.SendCommand("DISABLEUNITS", MyBattle.DisabledUnits.ToArray());
  315. BattleDisabledUnitsChanged(this, new TasEventArgs(MyBattle.DisabledUnits.ToArray()));
  316. }
  317. }
  318. }
  319. public void RequestDisconnect()
  320. {
  321. con.RequestClose();
  322. }
  323. void OnDisconnected()
  324. {
  325. ExistingUsers = new Dictionary<string, User>();
  326. existingChannels = new Dictionary<string, ExistingChannel>();
  327. joinedChannels = new Dictionary<string, Channel>();
  328. existingBattles = new Dictionary<int, Battle>();
  329. MyBattle = null;
  330. MyBattleID = 0;
  331. username = "";
  332. isLoggedIn = false;
  333. isChanScanning = false;
  334. if (guiThreadInvoker != null) guiThreadInvoker(() => ConnectionLost(this, new TasEventArgs("Connection was closed")));
  335. else ConnectionLost(this, new TasEventArgs("Connection was closed"));
  336. }
  337. public void EnableAllUnits()
  338. {
  339. MyBattle.DisabledUnits.Clear();
  340. con.SendCommand("ENABLEALLUNITS");
  341. BattleDisabledUnitsChanged(this, new TasEventArgs(MyBattle.DisabledUnits.ToArray()));
  342. }
  343. public void ForceAlly(string username, int ally)
  344. {
  345. con.SendCommand("FORCEALLYNO", username, ally);
  346. }
  347. public void ForceColor(string username, int color)
  348. {
  349. con.SendCommand("FORCETEAMCOLOR", username, color);
  350. }
  351. public void ForceSpectator(string username)
  352. {
  353. con.SendCommand("FORCESPECTATORMODE", username);
  354. }
  355. public void ForceTeam(string username, int team)
  356. {
  357. con.SendCommand("FORCETEAMNO", username, team);
  358. }
  359. public void GameSaid(string username, string text)
  360. {
  361. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player, TasSayEventArgs.Places.Game, "", username, text, false));
  362. }
  363. public Dictionary<string, ExistingChannel> GetExistingChannels()
  364. {
  365. if (isChanScanning) throw new TasClientException("Channel scan operation in progress");
  366. return new Dictionary<string, ExistingChannel>(ExistingChannels);
  367. }
  368. public bool GetExistingUser(string name, out User u)
  369. {
  370. return ExistingUsers.TryGetValue(name, out u);
  371. }
  372. public User GetUserCaseInsensitive(string userName)
  373. {
  374. return ExistingUsers.Values.FirstOrDefault(u => String.Equals(u.Name, userName, StringComparison.InvariantCultureIgnoreCase));
  375. }
  376. public void JoinBattle(int battleID, string password = "*")
  377. {
  378. if (string.IsNullOrEmpty(password)) password = "*";
  379. con.SendCommand("JOINBATTLE", battleID, password, random.Next());
  380. }
  381. public void JoinChannel(string channelName, string key=null)
  382. {
  383. if (con == null)
  384. {
  385. System.Diagnostics.Trace.TraceError("ERROR TasClient/JoinChannel: No server connection yet");
  386. return;
  387. }
  388. if (!String.IsNullOrEmpty(key)) con.SendCommand("JOIN", channelName, key);
  389. else con.SendCommand("JOIN", channelName);
  390. }
  391. public void Kick(string username)
  392. {
  393. con.SendCommand("KICKFROMBATTLE", username);
  394. }
  395. public void AdminKickFromLobby(string username,string reason)
  396. {
  397. con.SendCommand("KICKUSER", username,reason);
  398. }
  399. public void AdminSetTopic(string channel, string topic) {
  400. Say(SayPlace.User, "ChanServ", string.Format("!topic #{0} {1}", channel, topic.Replace("\n","\\n")),false);
  401. }
  402. public void AdminSetChannelPassword(string channel, string password) {
  403. if (string.IsNullOrEmpty(password) || password=="*") {
  404. Say(SayPlace.User, "ChanServ",string.Format("!lock #{0} {1}", channel, password),false);
  405. } else {
  406. Say(SayPlace.User, "ChanServ", string.Format("!unlock #{0}", channel), false);
  407. }
  408. }
  409. public void AdminBan(string username, double duration, string reason)
  410. {
  411. con.SendCommand("BAN", username, duration, reason);
  412. }
  413. public void AdminUnban(string username)
  414. {
  415. con.SendCommand("UNBAN", username);
  416. }
  417. public void AdminBanIP(string ip, double duration, string reason)
  418. {
  419. con.SendCommand("BANIP", ip, duration, reason);
  420. }
  421. public void AdminUnbanIP(string ip)
  422. {
  423. con.SendCommand("UNBANIP", ip);
  424. }
  425. public void LeaveBattle()
  426. {
  427. if (MyBattle != null)
  428. {
  429. con.SendCommand("LEAVEBATTLE");
  430. var bat = MyBattle;
  431. bat.ScriptTags.Clear();
  432. MyBattle = null;
  433. MyBattleID = 0;
  434. BattleClosed(this, new EventArgs<Battle>(bat));
  435. }
  436. }
  437. public void LeaveChannel(string channelName)
  438. {
  439. var args = new CancelEventArgs<string>(channelName);
  440. ChannelLeaving(this, args);
  441. if (args.Cancel) return;
  442. con.SendCommand("LEAVE", channelName);
  443. JoinedChannels.Remove(channelName);
  444. ChannelLeft(this, new TasEventArgs(channelName));
  445. }
  446. public void ListChannels()
  447. {
  448. isChanScanning = true;
  449. ExistingChannels.Clear();
  450. con.SendCommand("CHANNELS");
  451. }
  452. public static string GetMyUserID() {
  453. var nics = NetworkInterface.GetAllNetworkInterfaces().Where(x=> !String.IsNullOrWhiteSpace(x.GetPhysicalAddress().ToString())
  454. && x.NetworkInterfaceType != NetworkInterfaceType.Loopback && x.NetworkInterfaceType != NetworkInterfaceType.Tunnel);
  455. var wantedNic = nics.FirstOrDefault();
  456. if (wantedNic != null)
  457. {
  458. return Crc.Crc32(wantedNic.GetPhysicalAddress().GetAddressBytes()).ToString();
  459. }
  460. return "0";
  461. }
  462. public void Login(string userName, string password)
  463. {
  464. if (con == null) throw new TasClientException("Not connected");
  465. UserPassword = password;
  466. con.SendCommand("LOGIN", userName, Utils.HashLobbyPassword(password), cpu, localIp, appName, "\t" + GetMyUserID(), "\ta sp m cl p");
  467. }
  468. int cpu = Environment.OSVersion.Platform == PlatformID.Unix ? GlobalConst.ZkLobbyUserCpuLinux : GlobalConst.ZkLobbyUserCpu;
  469. public void OpenBattle(Battle nbattle)
  470. {
  471. LeaveBattle(); // leave current battle
  472. MyBattleID = -1;
  473. MyBattle = nbattle;
  474. var objList = new List<object>
  475. {
  476. 0,
  477. // type normal
  478. (int)MyBattle.Nat,
  479. MyBattle.Password,
  480. MyBattle.HostPort,
  481. MyBattle.MaxPlayers,
  482. MyBattle.ModHash,
  483. MyBattle.Rank,
  484. MyBattle.MapHash,
  485. MyBattle.EngineName,
  486. '\t' +MyBattle.EngineVersion,
  487. '\t' +MyBattle.MapName,
  488. '\t' + MyBattle.Title,
  489. '\t' + MyBattle.ModName
  490. };
  491. MyBattle.Founder = ExistingUsers[username];
  492. MyBattle.Ip = localIp;
  493. //battle.Details.AddToParamList(objList);
  494. mapToChangeTo = MyBattle.MapName;
  495. mapChecksumToChangeTo = MyBattle.MapHash ?? 0;
  496. lockToChangeTo = false;
  497. con.SendCommand("OPENBATTLE", objList.ToArray());
  498. lastSpectatorCount = 0;
  499. // send predefined starting rectangles
  500. foreach (var v in MyBattle.Rectangles) con.SendCommand("ADDSTARTRECT", v.Key, v.Value.Left, v.Value.Top, v.Value.Right, v.Value.Bottom);
  501. }
  502. public void Register(string username, string password)
  503. {
  504. con.SendCommand("REGISTER", username, Utils.HashLobbyPassword(password));
  505. }
  506. public void RemoveBattleRectangle(int allyno)
  507. {
  508. if (MyBattle.Rectangles.ContainsKey(allyno))
  509. {
  510. MyBattle.Rectangles.Remove(allyno);
  511. con.SendCommand("REMOVESTARTRECT", allyno);
  512. }
  513. }
  514. public void RemoveBot(string name)
  515. {
  516. con.SendCommand("REMOVEBOT", name);
  517. }
  518. public void RenameAccount(string newName)
  519. {
  520. con.SendCommand("RENAMEACCOUNT", newName);
  521. }
  522. public void Ring(string username)
  523. {
  524. con.SendCommand("RING", username);
  525. }
  526. public void ForceJoinBattle(string name, string battleHostName, string password = null) {
  527. var battle = ExistingBattles.Values.FirstOrDefault(x => x.Founder.Name == battleHostName);
  528. if (battle != null) ForceJoinBattle(name, battle.BattleID, password);
  529. }
  530. public void ForceJoinBattle(string name, int battleID, string password = null) {
  531. User user;
  532. Battle battle;
  533. existingUsers.TryGetValue(name, out user);
  534. existingBattles.TryGetValue(battleID, out battle);
  535. if (user != null && battle != null) con.SendCommand("FORCEJOINBATTLE", name, battleID, password);
  536. }
  537. public void ForceJoinChannel(string user, string channel, string password= null) {
  538. con.SendCommand(string.Format("FORCEJOIN {0} {1} {2}", user, channel, password));
  539. }
  540. public void ForceLeaveChannel(string user, string channel, string reason = null)
  541. {
  542. con.SendCommand(string.Format("FORCELEAVECHANNEL {0} {1} {2}", channel, user, reason));
  543. }
  544. /// <summary>
  545. /// Say something through chat system
  546. /// </summary>
  547. /// <param Name="place">Pick user (private message) channel or battle</param>
  548. /// <param Name="channel">Channel or User Name</param>
  549. /// <param Name="inputtext">chat text</param>
  550. /// <param Name="isEmote">is message emote? (channel or battle only)</param>
  551. /// <param Name="linePrefix">text to be inserted in front of each line (example: "!pm xyz")</param>
  552. public async Task Say(SayPlace place, string channel, string inputtext, bool isEmote, string linePrefix = "")
  553. {
  554. if (String.IsNullOrEmpty(inputtext)) return;
  555. var lines = inputtext.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
  556. foreach (var text in lines)
  557. {
  558. if (String.IsNullOrEmpty(text)) continue;
  559. var sentText = linePrefix + text;
  560. var args = new SayingEventArgs(place, channel, sentText, isEmote);
  561. Saying(this, args);
  562. if (args.Cancel) continue;
  563. switch (place)
  564. {
  565. case SayPlace.Channel:
  566. if (!JoinedChannels.ContainsKey(args.Channel)) JoinChannel(args.Channel);
  567. if (args.IsEmote) await con.SendCommand("SAYEX", args.Channel, args.Text);
  568. else await con.SendCommand("SAY", args.Channel, args.Text);
  569. break;
  570. case SayPlace.User:
  571. await con.SendCommand("SAYPRIVATE", args.Channel, args.Text);
  572. break;
  573. case SayPlace.Battle:
  574. if (args.IsEmote) await con.SendCommand("SAYBATTLEEX", args.Text);
  575. else await con.SendCommand("SAYBATTLE", args.Text);
  576. break;
  577. case SayPlace.BattlePrivate:
  578. if (args.IsEmote) await con.SendCommand("SAYBATTLEPRIVATEEX", channel, args.Text);
  579. else await con.SendCommand("SAYBATTLEPRIVATE", channel, args.Text);
  580. break;
  581. }
  582. }
  583. }
  584. public void SendMyBattleStatus(UserBattleStatus status)
  585. {
  586. con.SendCommand("MYBATTLESTATUS", status.ToInt(), status.TeamColor);
  587. }
  588. public void SendMyBattleStatus(int battleStatus, int color)
  589. {
  590. con.SendCommand("MYBATTLESTATUS", battleStatus, color);
  591. }
  592. public void SendRaw(string text)
  593. {
  594. con.SendCommand("", text);
  595. }
  596. public void SetHideCountry(string name, bool state) {
  597. con.SendCommand("HIDECOUNTRYSET", name, state ? "1" : "0");
  598. }
  599. public void SetScriptTag(string data)
  600. {
  601. con.SendCommand("SETSCRIPTTAGS", data);
  602. }
  603. public void SetBotMode(string name, bool botMode) {
  604. con.SendCommand("SETBOTMODE",name, botMode?"1":"0");
  605. }
  606. public void RequestLobbyVersion(string name) {
  607. con.SendCommand("GETLOBBYVERSION", name);
  608. }
  609. public void RequestUserIP(string name) {
  610. con.SendCommand("GETIP",name);
  611. }
  612. public void RequestUserID(string name)
  613. {
  614. con.SendCommand("GETUSERID", name);
  615. }
  616. /// <summary>
  617. /// Starts game and automatically does hole punching if necessary
  618. /// </summary>
  619. public void StartGame()
  620. {
  621. if (MyBattle.Nat == Battle.NatMode.HolePunching)
  622. {
  623. startingAfterUdpPunch = true;
  624. SendUdpPacket(0, serverHost, serverUdpHolePunchingPort);
  625. udpPunchingTimer.Start();
  626. }
  627. else ChangeMyUserStatus(false, true);
  628. }
  629. public void UpdateBattleDetails(BattleDetails bd)
  630. {
  631. var objList = new List<object>();
  632. con.SendCommand("SETSCRIPTTAGS", bd.GetParamList());
  633. }
  634. public void UpdateBot(string name, UserBattleStatus battleStatus, int teamColor)
  635. {
  636. con.SendCommand("UPDATEBOT", name, battleStatus.ToInt(), teamColor);
  637. }
  638. void DispatchServerCommand(string command, string[] args)
  639. {
  640. if (guiThreadInvoker != null) guiThreadInvoker(() => DispatchServerCommandOnGuiThread(command, args));
  641. else DispatchServerCommandOnGuiThread(command, args);
  642. }
  643. // FIXME: ugh
  644. private void HandleSpecialServerMessages(string[] args) {
  645. var text = Utils.Glue(args, 0);
  646. var match = Regex.Match(text, "<([^>]+)> is using (.+)");
  647. if (match.Success)
  648. {
  649. var name = match.Groups[1].Value;
  650. var version = match.Groups[2].Value.Trim();
  651. UserLobbyVersionRecieved(this, new UserLobbyVersionEventArgs() { Name = name, LobbyVersion = version});
  652. }
  653. else
  654. {
  655. match = Regex.Match(text, "<([^>]+)> is currently bound to (.+)");
  656. if (match.Success) {
  657. var name = match.Groups[1].Value;
  658. var ip = match.Groups[2].Value.Trim();
  659. UserIPRecieved(this, new UserIPEventArgs() { Name = name, IP = ip});
  660. }
  661. else
  662. {
  663. match = Regex.Match(text, "<([^>]+)> is (.+)");
  664. if (match.Success)
  665. {
  666. var name = match.Groups[1].Value;
  667. long id;
  668. if (long.TryParse(match.Groups[2].Value.Trim(), out id)) UserIDRecieved(this, new UserIDEventArgs() { Name = name, ID = id });
  669. }
  670. /*
  671. else
  672. {
  673. match = Regex.Match(text, "You've been kicked from server by <([^>]+)> (.+)");
  674. if (match.Success)
  675. {
  676. Trace.TraceWarning(String.Format("User {0} kicked (we are {1})"), args[1], MyUser.Name);
  677. string name = match.Groups[1].Value;
  678. string reason = match.Groups.Count > 2 ? string.Join(" ", match.Groups, 2) : null;
  679. KickedFromServer(this, new KickedFromServerEventArgs(name, ""));
  680. }
  681. }
  682. */
  683. }
  684. }
  685. }
  686. /// <summary>
  687. /// Primary method - processes commands from server
  688. /// </summary>
  689. /// <param Name="command">command Name</param>
  690. /// <param Name="args">command arguments</param>
  691. void DispatchServerCommandOnGuiThread(string command, string[] args)
  692. {
  693. // is this really needed for the whole thread? it screws with date formatting
  694. // Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
  695. try
  696. {
  697. if (command.StartsWith("#"))
  698. {
  699. MessageID = int.Parse(command.Substring(1));
  700. command = args[0];
  701. args = Utils.ShiftArray(args, -1);
  702. }
  703. Input(this, new TasInputArgs(command, args));
  704. switch (command)
  705. {
  706. case "TASServer": // happens after connecting to server
  707. OnTasServer(args);
  708. break;
  709. case "ACCEPTED": // Login accepted
  710. OnAccepted(args);
  711. break;
  712. case "DENIED": // login denied
  713. OnDenied(args);
  714. break;
  715. case "JOIN": // channel joined
  716. OnJoin(args);
  717. break;
  718. case "JOINFAILED": // channel join failed
  719. ChannelJoinFailed(this, new TasEventArgs(Utils.Glue(args)));
  720. break;
  721. case "CHANNEL": // iterating channels
  722. OnChannel(args);
  723. break;
  724. case "ENDOFCHANNELS": // end of channel list iteration
  725. OnEndOfChannels();
  726. break;
  727. case "ADDUSER": // new user joined ta server
  728. OnAddUser(args);
  729. break;
  730. case "FORCELEAVECHANNEL":
  731. OnForceLeaveChannel(args);
  732. break;
  733. case "FORCEQUITBATTLE":
  734. BattleForceQuit(this, EventArgs.Empty);
  735. break;
  736. case "KICKUSER":
  737. OnKickUser(args);
  738. break;
  739. case "REMOVEUSER": // user left ta server
  740. OnRemoveUser(args);
  741. break;
  742. case "MOTD": // server motd
  743. OnMotd(args);
  744. break;
  745. case "SERVERMSG": // server message
  746. OnServerMsg(args);
  747. break;
  748. case "SERVERMSGBOX": // server messagebox
  749. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Server,
  750. TasSayEventArgs.Places.MessageBox,
  751. "",
  752. "",
  753. Utils.Glue(args, 0),
  754. false));
  755. break;
  756. case "CHANNELMESSAGE": // server broadcast to channel
  757. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Server,
  758. TasSayEventArgs.Places.Channel,
  759. args[0],
  760. "",
  761. Utils.Glue(args, 1),
  762. false));
  763. break;
  764. case "SAID": // someone said something in channel
  765. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player,
  766. TasSayEventArgs.Places.Channel,
  767. args[0],
  768. args[1],
  769. Utils.Glue(args, 2),
  770. false));
  771. break;
  772. case "RING":
  773. Rang(this, new EventArgs<string>(args[0]));
  774. break;
  775. case "SAIDEX": // someone said something with emote in channel
  776. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player,
  777. TasSayEventArgs.Places.Channel,
  778. args[0],
  779. args[1],
  780. Utils.Glue(args, 2),
  781. true));
  782. break;
  783. case "SAYPRIVATE": // sent back from sever when user sends private message
  784. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player,
  785. TasSayEventArgs.Places.Normal,
  786. args[0],
  787. username,
  788. Utils.Glue(args, 1),
  789. false));
  790. break;
  791. case "SAIDPRIVATE": // someone said something to me
  792. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player,
  793. TasSayEventArgs.Places.Normal,
  794. args[0],
  795. args[0],
  796. Utils.Glue(args, 1),
  797. false));
  798. break;
  799. case "SAIDBATTLE": // someone said something in battle
  800. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player,
  801. TasSayEventArgs.Places.Battle,
  802. "",
  803. args[0],
  804. Utils.Glue(args, 1),
  805. false));
  806. break;
  807. case "SAIDBATTLEEX": // someone said in battle with emote
  808. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Player,
  809. TasSayEventArgs.Places.Battle,
  810. "",
  811. args[0],
  812. Utils.Glue(args, 1),
  813. true));
  814. break;
  815. case "BROADCAST": // server sends urgent broadcast
  816. InvokeSaid(new TasSayEventArgs(TasSayEventArgs.Origins.Server,
  817. TasSayEventArgs.Places.Broadcast,
  818. "",
  819. "",
  820. Utils.Glue(args, 0),
  821. false));
  822. break;
  823. case "REDIRECT": // server sends backup IP
  824. OnRedirect(args);
  825. break;
  826. case "CLIENTSTATUS": // client's status changed
  827. OnClientStatus(args);
  828. break;
  829. case "CLIENTS": // client list sent after channel join
  830. OnClients(args);
  831. break;
  832. case "JOINED": // user joined one of my channels
  833. OnJoined(args);
  834. break;
  835. case "LEFT": // user left one of my channels
  836. OnLeft(args);
  837. break;
  838. case "CHANNELTOPIC": // channel topic update (after joining a channel)
  839. OnChannelTopic(args);
  840. break;
  841. case "OPENBATTLEFAILED": // opening new battle has failed
  842. BattleOpenFailed(this, new TasEventArgs(Utils.Glue(args)));
  843. break;
  844. case "OPENBATTLE": // openbattle ok
  845. OnOpenBattle(args);
  846. break;
  847. case "REQUESTBATTLESTATUS": // ask for status at the beginning of the battle
  848. RequestBattleStatus(this, new EventArgs());
  849. break;
  850. case "JOINBATTLE": // we joined the battle
  851. OnJoinBattle(args);
  852. break;
  853. case "FORCEJOINBATTLE":
  854. OnForceJoinBattle(args);
  855. break;
  856. case "JOINEDBATTLE": // user joined the battle
  857. OnJoinedBattle(args);
  858. break;
  859. case "JOINBATTLEFAILED": // user failed to join battle
  860. JoinBattleFailed(this, new TasEventArgs(args));
  861. break;
  862. case "ADDBOT": // bot added to battle
  863. OnAddBot(args);
  864. break;
  865. case "REMOVEBOT": // bot removed from battle
  866. OnRemoveBot(args);
  867. break;
  868. case "UPDATEBOT": // bot data changed
  869. OnUpdateBot(args);
  870. break;
  871. case "LEFTBATTLE": // user left the battle
  872. OnLeftBattle(args);
  873. break;
  874. case "CLIENTBATTLESTATUS": // player battle status has changed
  875. OnClientBattleStatus(args);
  876. break;
  877. case "UPDATEBATTLEINFO": // update external battle info (lock and map)
  878. OnUpdateBattleInfo(args);
  879. break;
  880. case "BATTLEOPENED":
  881. OnBattleOpened(args);
  882. break;
  883. case "BATTLECLOSED":
  884. OnBattleClosed(args);
  885. break;
  886. case "CLIENTIPPORT":
  887. OnClientIpPort(args);
  888. break;
  889. case "SETSCRIPTTAGS": // updates internal battle details
  890. OnSetScriptTags(args);
  891. break;
  892. case "UDPSOURCEPORT":
  893. OnUdpSourcePort();
  894. break;
  895. case "AGREEMENT":
  896. OnAgreement(args);
  897. break;
  898. case "PONG":
  899. lastPong = DateTime.UtcNow;
  900. break;
  901. case "AGREEMENTEND":
  902. OnAgreementEnd();
  903. break;
  904. case "REGISTRATIONDENIED":
  905. RegistrationDenied(this, new TasEventArgs(Utils.Glue(args)));
  906. break;
  907. case "REGISTRATIONACCEPTED":
  908. RegistrationAccepted(this, new TasEventArgs(args));
  909. break;
  910. case "ADDSTARTRECT":
  911. OnAddStartRect(args);
  912. break;
  913. case "REMOVESTARTRECT":
  914. OnRemoveStartRect(args);
  915. break;
  916. case "TESTLOGINACCEPT":
  917. TestLoginAccepted(this, new TasEventArgs(args));
  918. break;
  919. case "TESTLOGINDENY":
  920. TestLoginDenied(this, new TasEventArgs(args));
  921. break;
  922. }
  923. }
  924. catch (Exception e)
  925. {
  926. //not throwing "ApplicationException" because we can explicitly say its application fault here:
  927. Trace.TraceError("TASC error: Error was thrown while processing chat command {0} \"{1}\" (check if chat event trigger faulty code in application): {2}", command, Utils.Glue(args), e);
  928. }
  929. }
  930. void OnRemoveStartRect(string[] args)
  931. {
  932. var allyNo = int.Parse(args[0]);
  933. MyBattle.Rectangles.Remove(allyNo);
  934. StartRectRemoved(this, new TasEventArgs(args));
  935. }
  936. void OnAddStartRect(string[] args)
  937. {
  938. var allyNo = int.Parse(args[0]);
  939. var left = int.Parse(args[1]);
  940. var top = int.Parse(args[2]);
  941. var right = int.Parse(args[3]);
  942. var bottom = int.Parse(args[4]);
  943. var rect = new BattleRect(left, top, right, bottom);
  944. MyBattle.Rectangles[allyNo] = rect;
  945. StartRectAdded(this, new TasEventArgs(args));
  946. }
  947. void OnAgreementEnd()
  948. {
  949. AgreementRecieved(this, new TasEventAgreementRecieved(agreementText));
  950. agreementText = null;
  951. }
  952. void OnAgreement(string[] args)
  953. {
  954. if (agreementText == null) agreementText = new StringBuilder();
  955. agreementText.AppendLine(Utils.Glue(args));
  956. }
  957. void OnUdpSourcePort()
  958. {
  959. udpPunchingTimer.Stop();
  960. if (startingAfterUdpPunch) {
  961. startingAfterUdpPunch = false;
  962. // send UDP packets to client (2x to be sure)
  963. foreach (var ubs in MyBattle.Users) if (ubs.ip != IPAddress.None && ubs.port != 0) SendUdpPacket(lastUdpSourcePort, ubs.ip.ToString(), ubs.port);
  964. foreach (var ubs in MyBattle.Users) if (ubs.ip != IPAddress.None && ubs.port != 0) SendUdpPacket(lastUdpSourcePort, ubs.ip.ToString(), ubs.port);
  965. MyBattle.HostPort = lastUdpSourcePort; // update source port for hosting and start it
  966. ChangeMyUserStatus(false, true);
  967. }
  968. }
  969. void OnSetScriptTags(string[] args)
  970. {
  971. var bd = new BattleDetails();
  972. bd.Parse(Utils.Glue(args), MyBattle.ModOptions);
  973. MyBattle.Details = bd;
  974. MyBattle.ScriptTags.AddRange(args);
  975. BattleDetailsChanged(this, new TasEventArgs(args));
  976. }
  977. void OnClientIpPort(string[] args)
  978. {
  979. var idx = MyBattle.GetUserIndex(args[0]);
  980. if (idx != -1) {
  981. var bs = MyBattle.Users[idx];
  982. bs.ip = IPAddress.Parse(args[1]);
  983. bs.port = int.Parse(args[2]);
  984. MyBattle.Users[idx] = bs;
  985. BattleUserIpRecieved(this, new TasEventArgs(args));
  986. }
  987. }
  988. void OnBattleClosed(string[] args)
  989. {
  990. var battleID = Int32.Parse(args[0]);
  991. Battle battle;
  992. if (!existingBattles.TryGetValue(battleID, out battle)) return;
  993. foreach (var u in battle.Users) {
  994. User user;
  995. if (ExistingUsers.TryGetValue(u.Name, out user)) user.IsInBattleRoom = false;
  996. }
  997. if (battle == MyBattle) {
  998. battle.ScriptTags.Clear();
  999. battle.Users.Clear();
  1000. BattleClosed(this, new EventArgs<Battle>(battle));
  1001. MyBattleEnded(this, new EventArgs<Battle>(battle));
  1002. }
  1003. BattleEnding(this, new EventArgs<Battle>(battle));
  1004. existingBattles.Remove(battleID);
  1005. BattleEnded(this, new EventArgs<Battle>(battle));
  1006. }
  1007. void OnBattleOpened(string[] args)
  1008. {
  1009. var mapHash = args[9];
  1010. var rest = Utils.Glue(args, 10).Split('\t');
  1011. var mapName = rest[2];
  1012. var modName = rest[4];
  1013. if (!IsBattleVisible(modName)) return;
  1014. var newBattle = new Battle {
  1015. BattleID = Int32.Parse(args[0]),
  1016. IsReplay = args[1] != "0",
  1017. Founder = ExistingUsers[args[3]],
  1018. Ip = args[4],
  1019. HostPort = Int32.Parse(args[5]),
  1020. MaxPlayers = Int32.Parse(args[6]),
  1021. Password = args[7] != "1" ? "*" : "apassword",
  1022. Rank = Int32.Parse(args[8]),
  1023. EngineVersion = rest[1],
  1024. Engine

Large files files are truncated, but you can click here to view the full file