Skip to content

Commit

Permalink
WebSocket Server (LiveSplit#2513)
Browse files Browse the repository at this point in the history
* Add WebSocketSharp depend.

* Add WebSocket server button.

* Make the servers mutually exclusive.

* Switch to IConnection interface.

* Create WsConnection.cs

* Add WsServer to CommandServer.

* Hook up context menu to WsServer.

* Update README.md

* Update README.md
  • Loading branch information
TheTedder authored Jul 17, 2024
1 parent 124624b commit e215638
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 8 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,17 @@ The documentation for how to develop, test, and submit an Auto Splitter can be f

## The LiveSplit Server

The internal LiveSplit Server allows for other programs and other computers to control LiveSplit. The server can accept connections over either a named pipe located at `\\<hostname>\pipe\LiveSplit` (`.` is the hostname if the client and server are on the same computer) or over TCP/IP.
The internal LiveSplit Server allows for other programs and other computers to control LiveSplit. The server can accept connections over either a named pipe located at `\\<hostname>\pipe\LiveSplit` (`.` is the hostname if the client and server are on the same computer), raw TCP/IP, or a WebSocket (WS) server, located at `ws://<hostname>:port/livesplit`.

### Control

The named pipe is always open while LiveSplit is running but the TCP server **MUST** be started before programs can talk to it (Right click on LiveSplit -> Control -> Start TCP Server). You **MUST** manually start it each time you launch LiveSplit.
The named pipe is always open while LiveSplit is running but the TCP and WS servers **MUST** be started before programs can talk to them (Right click on LiveSplit -> Control -> Start TCP/WS Server). You **MUST** manually start the one you wish to use each time you launch LiveSplit. The TCP and WS servers cannot both run at the same time because the WS server runs on top of TCP/IP.

### Settings

#### Server Port

**Server Port** is the door (one of thousands) on your computer that this program sends data through. Default is 16834. This should be fine for most people, but depending on network configurations, some ports may be blocked. See also https://en.wikipedia.org/wiki/Port_%28computer_networking%29
**Server Port** is the door (one of thousands) on your computer that this program sends data through. Default is 16834. This should be fine for most people, but depending on network configurations, some ports may be blocked. See also https://en.wikipedia.org/wiki/Port_%28computer_networking%29.

### Known Uses

Expand All @@ -92,7 +92,7 @@ Made something cool? Consider getting it added to this list.

Commands are case sensitive and end with a new line. You can provide parameters by using a space after the command and sending the parameters afterwards (`<command><space><parameters><newline>`).

Some commands will respond with data and some will not. Every response ends with a newline character.
Some commands will respond with data and some will not. Every response ends with a newline character. Note that since the WS server has a concept of messages, commands and reponses sent over it do not end in newline characters.

All times and deltas returned by the server are formatted according to [C#'s Constant Format Specifier](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings#the-constant-c-format-specifier). The server will accept times in the following format: `[-][[[d.]hh:]mm:]ss[.fffffff]`. The hours field can be greater than 23, even if days are present. Individual fields do not need to be padded with zeroes. Any command that returns a time or a string can return a single hyphen `-` to indicate a "null" or invalid value. Commands that take a COMPARISON or a NAME take plain strings that may include spaces. Because it is used as a delimiter to mark the end of a command, newline characters may not appear anywhere within a command.

Expand Down
1 change: 1 addition & 0 deletions src/LiveSplit.Core/LiveSplit.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<PackageReference Include="SharpDX.DirectInput" Version="2.5.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Resources.Extensions" Version="6.0.0" />
<PackageReference Include="WebSocketSharp" Version="1.0.3-rc11" />
</ItemGroup>

<ItemGroup>
Expand Down
18 changes: 17 additions & 1 deletion src/LiveSplit.Core/Server/CommandServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
using System.Net.Sockets;
using System.Windows.Forms;
using System.Xml;
using WebSocketSharp.Server;

namespace LiveSplit.Server
{
public class CommandServer
{
public TcpListener Server { get; set; }
public WebSocketServer WsServer { get; set; }
public List<Connection> PipeConnections { get; set; }
public List<TcpConnection> TcpConnections { get; set; }

Expand Down Expand Up @@ -50,6 +52,14 @@ public void StartTcp()
Server.BeginAcceptTcpClient(AcceptTcpClient, null);
}

public void StartWs()
{
StopWs();
WsServer = new WebSocketServer(State.Settings.ServerPort);
WsServer.AddWebSocketService("/livesplit", () => new WsConnection(connection_MessageReceived));
WsServer.Start();
}

public void StartNamedPipe()
{
StopPipe();
Expand All @@ -61,6 +71,7 @@ public void StopAll()
{
StopTcp();
StopPipe();
StopWs();
}

public void StopTcp()
Expand All @@ -74,6 +85,11 @@ public void StopTcp()
Server?.Stop();
}

public void StopWs()
{
WsServer?.Stop();
}

public void StopPipe()
{
WaitingServerPipe?.Dispose();
Expand Down Expand Up @@ -145,7 +161,7 @@ void connection_MessageReceived(object sender, MessageEventArgs e)
ProcessMessage(e.Message, e.Connection);
}

private void ProcessMessage(string message, Connection clientConnection)
private void ProcessMessage(string message, IConnection clientConnection)
{
string response = null;
var args = message.Split(new[] { ' ' }, 2);
Expand Down
6 changes: 3 additions & 3 deletions src/LiveSplit.Core/Server/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ namespace LiveSplit.Server
{
public class MessageEventArgs : EventArgs
{
public Connection Connection { get; }
public IConnection Connection { get; }
public string Message { get; }

public MessageEventArgs(Connection connection, string message)
public MessageEventArgs(IConnection connection, string message)
{
Connection = connection;
Message = message;
Expand All @@ -21,7 +21,7 @@ public MessageEventArgs(Connection connection, string message)

public delegate void MessageEventHandler(object sender, MessageEventArgs e);

public class Connection : IDisposable
public class Connection : IConnection, IDisposable
{
protected Stream Stream { get; private set; }
protected StreamReader Reader { get; private set; }
Expand Down
13 changes: 13 additions & 0 deletions src/LiveSplit.Core/Server/IConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LiveSplit.Server
{
public interface IConnection
{
void SendMessage(string message);
}
}
26 changes: 26 additions & 0 deletions src/LiveSplit.Core/Server/WsConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebSocketSharp.Server;

namespace LiveSplit.Server
{
internal class WsConnection : WebSocketBehavior, IConnection
{
private readonly MessageEventHandler _eventHandler;

internal WsConnection(MessageEventHandler eventHandler) : base()
{
_eventHandler = eventHandler;
}

protected override void OnMessage(WebSocketSharp.MessageEventArgs e)
{
_eventHandler.Invoke(this, new MessageEventArgs(this, e.Data));
}

public void SendMessage(string message) => Send(message);
}
}
9 changes: 9 additions & 0 deletions src/LiveSplit.View/View/TimerForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions src/LiveSplit.View/View/TimerForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public partial class TimerForm : Form

public CommandServer Server { get; set; }
public bool ServerStarted { get; protected set; } = false;
public bool WebSocketStarted { get; protected set; } = false;

protected GraphicsCache GlobalCache { get; set; }

Expand Down Expand Up @@ -654,6 +655,8 @@ void ServerMenuItem_Click(object sender, EventArgs e)
if (ServerStarted)
{
Server.StopTcp();
webSocketMenuItem.Enabled = true;

this.InvokeIfRequired(() =>
{
serverMenuItem.Text = "Start TCP Server";
Expand All @@ -662,6 +665,8 @@ void ServerMenuItem_Click(object sender, EventArgs e)
else
{
Server.StartTcp();
webSocketMenuItem.Enabled = false;

this.InvokeIfRequired(() =>
{
serverMenuItem.Text = "Stop TCP Server";
Expand All @@ -671,6 +676,32 @@ void ServerMenuItem_Click(object sender, EventArgs e)
ServerStarted = !ServerStarted;
}

void WebSocketMenuItem_Click(object sender, EventArgs e)
{
if (WebSocketStarted)
{
Server.StopWs();
serverMenuItem.Enabled = true;

this.InvokeIfRequired(() =>
{
webSocketMenuItem.Text = "Start WebSocket Server";
});
}
else
{
Server.StartWs();
serverMenuItem.Enabled = false;

this.InvokeIfRequired(() =>
{
webSocketMenuItem.Text = "Stop WebSocket Server";
});
}

WebSocketStarted = !WebSocketStarted;
}

void CurrentState_OnSkipSplit(object sender, EventArgs e)
{
this.InvokeIfRequired(() =>
Expand Down Expand Up @@ -2867,6 +2898,7 @@ private void RebuildControlMenu()

controlMenuItem.DropDownItems.Add(new ToolStripSeparator());
controlMenuItem.DropDownItems.Add(serverMenuItem);
controlMenuItem.DropDownItems.Add(webSocketMenuItem);

var components = Layout.Components;
if (CurrentState.Run.IsAutoSplitterActive())
Expand Down

0 comments on commit e215638

Please sign in to comment.