!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / Tools / VisualStudioExtensions / CryEngineMonoDebugger / source / CryEngine.Debugger.Mono / RemoteConsole.cs
blob7ab7c0bb422cc2422bc0990f89318770b60fcfbd
1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
3 using System;
4 using System.Collections.Generic;
5 using System.Net.Sockets;
6 using System.Threading;
8 namespace CryEngine.Debugger.Mono
10 static class Constants
12 public const int DefaultPort = 4600;
13 public const int DefaultBuffer = 4096;
14 public const int ReconnectionTime = 3000;
15 public const int ConnectionAttempts = 25;
18 public enum MessageType
20 Message = 0,
21 Warning,
22 Error,
25 public class LogMessage
27 public MessageType Type { get; set; }
28 public string Message { get; set; }
30 public LogMessage(MessageType type, string message)
32 Type = type;
33 Message = message;
37 interface IRemoteConsoleClientListener
39 void OnLogMessage(LogMessage message);
40 void OnAutoCompleteDone(List<string> autoCompleteList);
41 void OnConnected();
42 void OnDisconnected();
45 interface IRemoteConsoleClient
47 void Start();
48 void Stop();
49 void SetServer(string ip);
50 void ExecuteConsoleCommand(string command);
51 void ExecuteGameplayCommand(string command);
52 void SetListener(IRemoteConsoleClientListener listener);
53 void PumpEvents();
56 class RemoteConsoleClientListenerState
58 public IRemoteConsoleClientListener Listener { get; set; } = null;
59 public bool Connected { get; set; } = false;
60 public bool AutoCompleteSent { get; set; } = false;
62 public void SetListener(IRemoteConsoleClientListener listener)
64 Listener = listener;
65 Reset();
68 public void Reset()
70 AutoCompleteSent = false;
74 class RemoteConsole : IRemoteConsoleClient, IDisposable
76 private enum ConsoleEventType
78 Noop = 0,
79 Req,
80 LogMessage,
81 LogWarning,
82 LogError,
83 ConsoleCommand,
84 AutoCompleteList,
85 AutoCompleteListDone,
87 Strobo_GetThreads,
88 Strobo_ThreadAdd,
89 Strobo_ThreadDone,
91 Strobo_GetResult,
92 Strobo_ResultStart,
93 Strobo_ResultDone,
95 Strobo_StatStart,
96 Strobo_StatAdd,
97 Strobo_IPStart,
98 Strobo_IPAdd,
99 Strobo_SymStart,
100 Strobo_SymAdd,
101 Strobo_CallstackStart,
102 Strobo_CallstackAdd,
104 GameplayEvent,
107 private class CommandEvent
109 public ConsoleEventType Type { get; set; }
110 public string Command { get; set; }
112 public CommandEvent(ConsoleEventType type, string command)
114 Type = type;
115 Command = command;
119 private Thread _consoleThread = null;
120 private TcpClient _clientSocket = null;
121 private byte[] _inStream = new byte[Constants.DefaultBuffer];
123 private string _server = "";
124 private Queue<CommandEvent> _commands = new Queue<CommandEvent>();
125 private Queue<string> _autoComplete = new Queue<string>();
126 private Queue<LogMessage> _messages = new Queue<LogMessage>();
128 private RemoteConsoleClientListenerState _listener = new RemoteConsoleClientListenerState();
130 private object _locker = new object();
131 private object _clientLocker = new object();
133 private volatile bool _running = false;
134 private volatile bool _stopping = false;
135 private volatile bool _isConnected = false;
136 private volatile bool _autoCompleteIsDone = false;
137 private volatile bool _resetConnection = false;
138 private int _ticks = 0;
139 private int _connectionAttempts = 0;
141 public int Port { get; private set; }
143 public RemoteConsole()
145 _clientSocket = null;
146 Port = Constants.DefaultPort;
149 public RemoteConsole(int port)
151 _clientSocket = null;
152 Port = port;
155 public bool SetPort(string portText)
157 int port = -1;
158 if((!_isConnected || _resetConnection) && int.TryParse(portText, out port))
160 Port = port;
161 return true;
163 return false;
166 public void Start()
168 if (!_running)
170 _running = true;
171 _consoleThread = new Thread(RemoteConsoleLoop);
172 _consoleThread.Name = nameof(RemoteConsoleLoop);
173 _consoleThread.Start();
177 public void Stop()
179 if (_running)
181 _running = false;
182 _stopping = true;
183 lock (_clientLocker)
185 if (_clientSocket != null)
187 _clientSocket.Close();
190 while (_stopping && _consoleThread.IsAlive)
192 Thread.Sleep(10);
194 _consoleThread = null;
198 public void SetServer(string ip)
200 if (_listener.Listener != null)
202 _listener.Listener.OnDisconnected();
203 _listener.Connected = false;
205 lock (_locker)
207 _server = ip;
208 _ticks = 0;
209 _autoCompleteIsDone = false;
210 _listener.Reset();
211 _resetConnection = true;
212 _connectionAttempts = 0;
216 public void ExecuteConsoleCommand(string command)
218 lock (_locker)
220 _commands.Enqueue(new CommandEvent(ConsoleEventType.ConsoleCommand, command));
224 public void ExecuteGameplayCommand(string command)
226 lock (_locker)
228 _commands.Enqueue(new CommandEvent(ConsoleEventType.GameplayEvent, command));
232 public void SetListener(IRemoteConsoleClientListener listener)
234 lock (_locker)
236 _listener.SetListener(listener);
240 public void PumpEvents()
242 List<LogMessage> messages = null;
243 List<string> autoComplete = null;
244 bool sendConn = false;
245 bool isConn = false;
246 IRemoteConsoleClientListener l = null;
247 lock (_locker)
249 l = _listener.Listener;
250 messages = new List<LogMessage>(_messages);
251 _messages.Clear();
252 if (_autoCompleteIsDone && !_listener.AutoCompleteSent)
254 autoComplete = new List<string>(_autoComplete);
255 _autoComplete.Clear();
256 _listener.AutoCompleteSent = true;
258 if (_isConnected != _listener.Connected && !_resetConnection)
260 _listener.Connected = _isConnected;
261 sendConn = true;
262 isConn = _isConnected;
265 if (l != null)
267 if (sendConn)
269 if (isConn)
271 l.OnConnected();
273 else
275 l.OnDisconnected();
278 if (messages != null)
280 foreach (var message in messages)
282 l.OnLogMessage(message);
285 if (autoComplete != null)
287 l.OnAutoCompleteDone(autoComplete);
292 private void RemoteConsoleLoop()
294 while (_running)
296 if(!_resetConnection && !_isConnected && _connectionAttempts >= Constants.ConnectionAttempts)
298 AddLogMessage(MessageType.Message, string.Format("Unable to connect after {0} connection attemtps.", _connectionAttempts));
299 break;
302 if (!_isConnected || _resetConnection)
304 ClearCommands();
305 if (_ticks++ % Constants.ReconnectionTime == 0)
307 _connectionAttempts++;
308 lock (_clientLocker)
310 if (_clientSocket != null && _clientSocket.Connected)
312 _clientSocket.GetStream().Close();
313 _clientSocket.Close();
314 _clientSocket.Dispose();
316 _clientSocket = new TcpClient();
320 _clientSocket.Connect(_server, Port);
321 NetworkStream stream = _clientSocket.GetStream();
322 stream.ReadTimeout = 3000;
323 stream.WriteTimeout = 3000;
324 _ticks = 0;
326 #if DEBUG
327 catch (Exception ex)
329 // Prevent log-spam if connecting failed but there are still connection attempts left.
330 if(!(ex is SocketException) && _connectionAttempts < Constants.ConnectionAttempts)
332 AddLogMessage(MessageType.Message, ex.Message);
335 #else
336 catch (System.Exception){ }
337 #endif
340 _isConnected = _clientSocket.Client != null ? _clientSocket.Connected : false;
341 if (!_isConnected)
343 _autoCompleteIsDone = false;
345 else
347 // Reset the connection attempts if reconnecting was succesful.
348 _connectionAttempts = 0;
351 if (_resetConnection)
353 _resetConnection = false;
356 else
360 if(_clientSocket.Available > 0)
362 _isConnected = ProcessClient();
365 // Pump the events immediately since they will be logged thread-safe later on.
366 PumpEvents();
368 catch(Exception ex)
370 string message = string.Format("Disconnecting the remote console because it ran into the following exception: {0}{1}{2}", ex.Message, Environment.NewLine, ex.StackTrace);
371 AddLogMessage(MessageType.Error, message);
372 break;
377 _stopping = false;
380 private bool ReadData(ref ConsoleEventType id, ref string data)
384 NetworkStream stream = _clientSocket.GetStream();
385 int ret = 0;
386 while (true)
388 ret += stream.Read(_inStream, ret, Constants.DefaultBuffer - ret);
389 if(ret == 0)
391 return false;
394 if (_inStream[ret - 1] == '\0')
396 break;
399 string returndata = System.Text.Encoding.ASCII.GetString(_inStream);
400 id = (ConsoleEventType)(returndata[0] - '0');
401 int index = returndata.IndexOf('\0');
402 data = returndata.Substring(1, index - 1);
404 catch (Exception)
406 return false;
408 return true;
411 private bool SendData(ConsoleEventType id, string data = "")
413 char cid = (char)((char)id + '0');
414 string msg = "";
415 msg += cid;
416 msg += data;
417 msg += "\0";
420 byte[] outStream = System.Text.Encoding.ASCII.GetBytes(msg);
421 NetworkStream stream = _clientSocket.GetStream();
422 stream.Write(outStream, 0, outStream.Length);
423 stream.Flush();
425 catch (Exception)
427 return false;
429 return true;
432 private void AddLogMessage(MessageType type, string message)
434 lock (_locker)
436 _messages.Enqueue(new LogMessage(type, message));
440 private void AddAutoCompleteItem(string item)
442 lock (_locker)
444 _autoComplete.Enqueue(item);
448 private bool GetCommand(ref CommandEvent command)
450 bool res = false;
451 lock (_locker)
453 if (_commands.Count > 0)
455 command = _commands.Dequeue();
456 res = true;
459 return res;
462 private void AutoCompleteDone()
464 lock (_locker)
466 _autoCompleteIsDone = true;
470 private void ClearCommands()
472 lock (_locker)
474 _commands.Clear();
478 private bool ProcessClient()
480 ConsoleEventType eventType = ConsoleEventType.Noop;
481 string data = "";
482 if (!ReadData(ref eventType, ref data))
483 return false;
485 switch (eventType)
487 case ConsoleEventType.LogMessage:
488 AddLogMessage(MessageType.Message, data);
489 return SendData(ConsoleEventType.Noop);
490 case ConsoleEventType.LogWarning:
491 AddLogMessage(MessageType.Warning, data);
492 return SendData(ConsoleEventType.Noop);
493 case ConsoleEventType.LogError:
494 AddLogMessage(MessageType.Error, data);
495 return SendData(ConsoleEventType.Noop);
496 case ConsoleEventType.AutoCompleteList:
497 AddAutoCompleteItem(data);
498 return SendData(ConsoleEventType.Noop);
499 case ConsoleEventType.AutoCompleteListDone:
500 AutoCompleteDone();
501 return SendData(ConsoleEventType.Noop);
502 case ConsoleEventType.Req:
503 CommandEvent command = null;
504 if (GetCommand(ref command))
505 return SendData(command.Type, command.Command);
506 else
507 return SendData(ConsoleEventType.Noop);
508 default:
509 return SendData(ConsoleEventType.Noop);
513 public void Dispose()
515 Dispose(true);
516 GC.SuppressFinalize(this);
519 public void Dispose(bool disposing)
521 if(disposing)
523 _clientSocket.Dispose();
524 _clientSocket = null;