You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
469 lines
14 KiB
C#
469 lines
14 KiB
C#
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
// Description : Socket connection with game. See CryEngine's RemoteConsole.cpp for information on the Server
|
|
|
|
|
|
using System.Collections.Generic;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
|
|
namespace RemoteConsole
|
|
{
|
|
static class Constants
|
|
{
|
|
public const int DEFAULT_PORT = 4600;
|
|
public const int DEFAULT_BUFFER = 4096;
|
|
public const int RECONNECTION_TIME = 3000;
|
|
}
|
|
|
|
public enum EMessageType
|
|
{
|
|
eMT_Message = 0,
|
|
eMT_Warning,
|
|
eMT_Error,
|
|
}
|
|
|
|
public class SLogMessage
|
|
{
|
|
public SLogMessage(EMessageType type, string message)
|
|
{
|
|
Type = type;
|
|
Message = message;
|
|
}
|
|
public EMessageType Type;
|
|
public string Message;
|
|
}
|
|
|
|
interface IRemoteConsoleClientListener
|
|
{
|
|
void OnLogMessage(SLogMessage message);
|
|
void OnAutoCompleteDone(List<string> autoCompleteList);
|
|
void OnConnected();
|
|
void OnDisconnected();
|
|
}
|
|
|
|
interface IRemoteConsoleClient
|
|
{
|
|
void Start();
|
|
void Stop();
|
|
void SetServer(string ip);
|
|
void ExecuteConsoleCommand(string command);
|
|
void ExecuteGameplayCommand(string command);
|
|
void SetListener(IRemoteConsoleClientListener listener);
|
|
void PumpEvents();
|
|
}
|
|
|
|
class SRemoteConsoleClientListenerState
|
|
{
|
|
public IRemoteConsoleClientListener listener = null;
|
|
public bool connected = false;
|
|
public bool autoCompleteSent = false;
|
|
|
|
public void SetListener(IRemoteConsoleClientListener listener)
|
|
{
|
|
this.listener = listener;
|
|
Reset();
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
autoCompleteSent = false;
|
|
}
|
|
}
|
|
|
|
|
|
class RemoteConsole : IRemoteConsoleClient
|
|
{
|
|
private enum EConsoleEventType
|
|
{
|
|
eCET_Noop = 0,
|
|
eCET_Req,
|
|
eCET_LogMessage,
|
|
eCET_LogWarning,
|
|
eCET_LogError,
|
|
eCET_ConsoleCommand,
|
|
eCET_AutoCompleteList,
|
|
eCET_AutoCompleteListDone,
|
|
|
|
eCET_Strobo_GetThreads,
|
|
eCET_Strobo_ThreadAdd,
|
|
eCET_Strobo_ThreadDone,
|
|
|
|
eCET_Strobo_GetResult,
|
|
eCET_Strobo_ResultStart,
|
|
eCET_Strobo_ResultDone,
|
|
|
|
eCET_Strobo_StatStart,
|
|
eCET_Strobo_StatAdd,
|
|
eCET_Strobo_IPStart,
|
|
eCET_Strobo_IPAdd,
|
|
eCET_Strobo_SymStart,
|
|
eCET_Strobo_SymAdd,
|
|
eCET_Strobo_CallstackStart,
|
|
eCET_Strobo_CallstackAdd,
|
|
|
|
eCET_GameplayEvent,
|
|
}
|
|
|
|
private class SCommandEvent
|
|
{
|
|
public SCommandEvent(EConsoleEventType type, string command)
|
|
{
|
|
Type = type;
|
|
Command = command;
|
|
}
|
|
public EConsoleEventType Type;
|
|
public string Command;
|
|
}
|
|
|
|
public int Port { get; private set; }
|
|
private Thread thrd = null;
|
|
private System.Net.Sockets.TcpClient clientSocket = null;
|
|
byte[] inStream = new byte[Constants.DEFAULT_BUFFER];
|
|
|
|
private string server = "";
|
|
private List<SCommandEvent> commands = new List<SCommandEvent>();
|
|
private List<string> autoComplete = new List<string>();
|
|
private List<SLogMessage> messages = new List<SLogMessage>();
|
|
|
|
private SRemoteConsoleClientListenerState listener = new SRemoteConsoleClientListenerState();
|
|
|
|
private object locker = new object();
|
|
private object clientLocker = new object();
|
|
|
|
private volatile bool running = false;
|
|
private volatile bool stopping = false;
|
|
private volatile bool isConnected = false;
|
|
private volatile bool autoCompleteIsDone = false;
|
|
private volatile bool resetConnection = false;
|
|
private int ticks = 0;
|
|
|
|
public RemoteConsole()
|
|
{
|
|
clientSocket = null;
|
|
Port = Constants.DEFAULT_PORT;
|
|
}
|
|
|
|
public bool SetPort(string port_)
|
|
{
|
|
int port = -1;
|
|
try
|
|
{
|
|
port = System.Convert.ToInt32(port_);
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
}
|
|
|
|
if (port != -1 && (!isConnected || resetConnection))
|
|
{
|
|
Port = port;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
if (!running)
|
|
{
|
|
running = true;
|
|
thrd = new Thread(threadFct);
|
|
thrd.Start();
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (running)
|
|
{
|
|
running = false;
|
|
stopping = true;
|
|
lock (clientLocker)
|
|
{
|
|
if (clientSocket != null)
|
|
{
|
|
clientSocket.Close();
|
|
clientSocket = new System.Net.Sockets.TcpClient();
|
|
}
|
|
}
|
|
while (stopping)
|
|
System.Threading.Thread.Sleep(10);
|
|
thrd = null;
|
|
}
|
|
}
|
|
|
|
public void SetServer(string ip)
|
|
{
|
|
if (listener.listener != null)
|
|
{
|
|
listener.listener.OnDisconnected();
|
|
listener.connected = false;
|
|
}
|
|
lock (locker)
|
|
{
|
|
server = ip;
|
|
ticks = 0;
|
|
autoCompleteIsDone = false;
|
|
listener.Reset();
|
|
resetConnection = true;
|
|
}
|
|
}
|
|
|
|
public void ExecuteConsoleCommand(string command)
|
|
{
|
|
lock (locker)
|
|
{
|
|
commands.Add(new SCommandEvent(EConsoleEventType.eCET_ConsoleCommand, command));
|
|
}
|
|
}
|
|
|
|
public void ExecuteGameplayCommand(string command)
|
|
{
|
|
lock (locker)
|
|
{
|
|
commands.Add(new SCommandEvent(EConsoleEventType.eCET_GameplayEvent, command));
|
|
}
|
|
}
|
|
|
|
public void SetListener(IRemoteConsoleClientListener listener)
|
|
{
|
|
lock (locker)
|
|
{
|
|
this.listener.SetListener(listener);
|
|
}
|
|
}
|
|
|
|
public void PumpEvents()
|
|
{
|
|
List<SLogMessage> msgs = null;
|
|
List<string> autoCmplt = null;
|
|
bool sendConn = false;
|
|
bool isConn = false;
|
|
IRemoteConsoleClientListener l = null;
|
|
lock (locker)
|
|
{
|
|
l = listener.listener;
|
|
msgs = new List<SLogMessage>(messages);
|
|
messages.Clear();
|
|
if (autoCompleteIsDone && !listener.autoCompleteSent)
|
|
{
|
|
autoCmplt = new List<string>(autoComplete);
|
|
autoComplete.Clear();
|
|
listener.autoCompleteSent = true;
|
|
}
|
|
if (isConnected != listener.connected && !resetConnection)
|
|
{
|
|
listener.connected = isConnected;
|
|
sendConn = true;
|
|
isConn = isConnected;
|
|
}
|
|
}
|
|
if (l != null)
|
|
{
|
|
if (sendConn)
|
|
{
|
|
if (isConn)
|
|
l.OnConnected();
|
|
else
|
|
l.OnDisconnected();
|
|
}
|
|
if (msgs != null)
|
|
{
|
|
for (int i = 0; i < msgs.Count; ++i)
|
|
l.OnLogMessage(msgs[i]);
|
|
}
|
|
if (autoCmplt != null)
|
|
{
|
|
l.OnAutoCompleteDone(autoCmplt);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void threadFct()
|
|
{
|
|
while (running)
|
|
{
|
|
if (!isConnected || resetConnection)
|
|
{
|
|
clearCommands();
|
|
if (ticks++ % Constants.RECONNECTION_TIME == 0)
|
|
{
|
|
lock (clientLocker)
|
|
{
|
|
if (clientSocket != null && clientSocket.Connected)
|
|
clientSocket.Close();
|
|
clientSocket = new System.Net.Sockets.TcpClient();
|
|
}
|
|
try
|
|
{
|
|
clientSocket.Connect(server, Port);
|
|
NetworkStream stream = clientSocket.GetStream();
|
|
stream.ReadTimeout = 3000;
|
|
stream.WriteTimeout = 3000;
|
|
ticks = 0;
|
|
}
|
|
#if DEBUG
|
|
catch (System.Exception ex)
|
|
{
|
|
addLogMessage(EMessageType.eMT_Message, ex.Message);
|
|
}
|
|
#else
|
|
catch (System.Exception) { }
|
|
#endif
|
|
}
|
|
|
|
isConnected = clientSocket != null ? clientSocket.Connected : false;
|
|
if (!isConnected)
|
|
autoCompleteIsDone = false;
|
|
if (resetConnection)
|
|
resetConnection = false;
|
|
}
|
|
else
|
|
{
|
|
isConnected = processClient();
|
|
}
|
|
}
|
|
stopping = false;
|
|
}
|
|
|
|
private bool readData(ref EConsoleEventType id, ref string data)
|
|
{
|
|
try
|
|
{
|
|
NetworkStream stream = clientSocket.GetStream();
|
|
int ret = 0;
|
|
while (true)
|
|
{
|
|
ret += stream.Read(inStream, ret, Constants.DEFAULT_BUFFER - ret);
|
|
if (inStream[ret - 1] == '\0')
|
|
break;
|
|
}
|
|
string returndata = System.Text.Encoding.ASCII.GetString(inStream);
|
|
id = (EConsoleEventType)(returndata[0] - '0');
|
|
int index = returndata.IndexOf('\0');
|
|
data = returndata.Substring(1, index - 1);
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool sendData(EConsoleEventType id, string data = "")
|
|
{
|
|
char cid = (char)((char)id + '0');
|
|
string msg = "";
|
|
msg += cid;
|
|
msg += data;
|
|
msg += "\0";
|
|
try
|
|
{
|
|
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(msg);
|
|
NetworkStream stream = clientSocket.GetStream();
|
|
stream.Write(outStream, 0, outStream.Length);
|
|
stream.Flush();
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void addLogMessage(EMessageType type, string message)
|
|
{
|
|
lock (locker)
|
|
{
|
|
messages.Add(new SLogMessage(type, message));
|
|
}
|
|
}
|
|
|
|
private void addAutoCompleteItem(string item)
|
|
{
|
|
lock (locker)
|
|
{
|
|
autoComplete.Add(item);
|
|
}
|
|
}
|
|
|
|
private bool getCommand(ref SCommandEvent command)
|
|
{
|
|
bool res = false;
|
|
lock (locker)
|
|
{
|
|
if (commands.Count > 0)
|
|
{
|
|
command = commands[0];
|
|
commands.RemoveAt(0);
|
|
res = true;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
private void autoCompleteDone()
|
|
{
|
|
lock (locker)
|
|
{
|
|
autoCompleteIsDone = true;
|
|
}
|
|
}
|
|
|
|
private void clearCommands()
|
|
{
|
|
lock (locker)
|
|
{
|
|
commands.Clear();
|
|
}
|
|
}
|
|
|
|
private bool processClient()
|
|
{
|
|
EConsoleEventType eventType = EConsoleEventType.eCET_Noop;
|
|
string data = "";
|
|
if (!readData(ref eventType, ref data))
|
|
return false;
|
|
|
|
switch (eventType)
|
|
{
|
|
case EConsoleEventType.eCET_LogMessage:
|
|
addLogMessage(EMessageType.eMT_Message, data);
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
case EConsoleEventType.eCET_LogWarning:
|
|
addLogMessage(EMessageType.eMT_Warning, data);
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
case EConsoleEventType.eCET_LogError:
|
|
addLogMessage(EMessageType.eMT_Error, data);
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
case EConsoleEventType.eCET_AutoCompleteList:
|
|
addAutoCompleteItem(data);
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
case EConsoleEventType.eCET_AutoCompleteListDone:
|
|
autoCompleteDone();
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
case EConsoleEventType.eCET_Req:
|
|
SCommandEvent command = null;
|
|
if (getCommand(ref command))
|
|
return sendData(command.Type, command.Command);
|
|
else
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
default:
|
|
return sendData(EConsoleEventType.eCET_Noop);
|
|
}
|
|
}
|
|
}
|
|
}
|