Skip to content

Commit

Permalink
Merge pull request #102 from dennis/webwidgets
Browse files Browse the repository at this point in the history
Webwidgets
  • Loading branch information
dennis committed Aug 4, 2021
2 parents 2bc2876 + c38e74a commit 9f3b088
Show file tree
Hide file tree
Showing 47 changed files with 1,137 additions and 52 deletions.
10 changes: 10 additions & 0 deletions Backend/WebWidget/InstanceIndex.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<html>
<head>
<title>Slipstream Actice WebWidget Instances</title>
</head>
<body>
<h1>Slipstream Actice WebWidget Instances</h1>

{{CONTENT}}
</body>
</html>
52 changes: 52 additions & 0 deletions Backend/WebWidget/ss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
var INSTANCE_ID
var WEB_WIDGET_TYPE
var ASSETS

function connect() {
let socket = a = new WebSocket("ws://" + document.location.host + "/events/" + INSTANCE_ID)

socket.onopen = function (e) {
console.log("[slipstream ws] [open] Connection established")

if (typeof(onConnect) === "function") {
onConnect()
}
}

socket.onmessage = function (event) {
if (typeof (onData) !== "function") {
console.log("[slipstream ws] [onmessage] got data, but no onData() function defined. Ignored", event.data)
}
else {
console.log("[slipstream ws] [onmessage] got data", event.data)
onData(JSON.parse(event.data))
}
}

socket.onclose = function (event) {
if (event.wasClean) {
console.log("[slipstream ws] [close] Connection closed cleanly, code=" + event.code + " reason=" + event.reason)
} else {
console.log('[slipstream ws] [close] Connection died')
}

if (typeof (onDisconnect) === "function") {
onDisconnect()
}

setTimeout(connect, 1000);
}

socket.onerror = function (error) {
console.log("[slipstream ws] [error] " + error.message)
socket.close()
}
}

window.addEventListener('load', function () {
INSTANCE_ID = document.body.getAttribute("data-instance-id")
WEB_WIDGET_TYPE = document.body.getAttribute("data-web-widget-type")
ASSETS = document.body.getAttribute("data-assets")

connect()
})
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
And only scripts using that instance will be notified of its events. Meaning,
you will not get IRacing events, if you don't have require("api/iracing"):instance(..)
- You need a new init.lua, so delete your existing init.lua to get a new one.
- IRacing: Adds pit-stop lua functions: pit_clear_all(), pit_clear_tyres(),
pit_fast_repair(), pit_add_fuel(), pit_change_(left|right)_(front|rear)_tyre()
and pit_clean_windshield()
- New Component: WebWidgets
- Merged UI components into WinFormUI. You need to change `require("api/ui")` to
`require("api/winformui")`

Expand Down
2 changes: 0 additions & 2 deletions Components/Twitch/Events/TwitchReceivedMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ namespace Slipstream.Components.Twitch.Events
public class TwitchReceivedMessage : IEvent
{
public string EventType => nameof(TwitchReceivedMessage);

public IEventEnvelope Envelope { get; set; } = new EventEnvelope();


[Description("User that sent the message")]
public string From { get; set; } = string.Empty;
Expand Down
16 changes: 16 additions & 0 deletions Components/WebWidget/EventFactory/WebWidgetEventFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Slipstream.Components.WebWidget;
using Slipstream.Components.WebWidget.Events;
using Slipstream.Shared;

#nullable enable

namespace Slipstream.Components.Internal.EventFactory
{
public class WebWidgetEventFactory : IWebWidgetEventFactory
{
public WebWidgetCommandEvent CreateWebWidgetCommandEvent(IEventEnvelope envelope, string data)
{
return new WebWidgetCommandEvent { Envelope = envelope, Data = data };
}
}
}
32 changes: 32 additions & 0 deletions Components/WebWidget/EventHandler/WebWidgetEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#nullable enable

using Slipstream.Components.WebWidget.Events;
using Slipstream.Shared;
using System;

namespace Slipstream.Components.WebWidget.EventHandler
{
internal class WebWidgetEventHandler : IEventHandler
{
public event EventHandler<WebWidgetCommandEvent>? OnWebWidgetCommandEvent;

public IEventHandler.HandledStatus HandleEvent(IEvent @event)
{
return @event switch
{
WebWidgetCommandEvent tev => OnEvent(OnWebWidgetCommandEvent, tev),
_ => IEventHandler.HandledStatus.NotMine,
};
}

private IEventHandler.HandledStatus OnEvent<TEvent>(EventHandler<TEvent>? onEvent, TEvent args)
{
if (onEvent != null)
{
onEvent.Invoke(this, args);
return IEventHandler.HandledStatus.Handled;
}
return IEventHandler.HandledStatus.UseDefault;
}
}
}
13 changes: 13 additions & 0 deletions Components/WebWidget/Events/WebWidgetCommandEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#nullable enable

using Slipstream.Shared;

namespace Slipstream.Components.WebWidget.Events
{
public class WebWidgetCommandEvent : IEvent
{
public string EventType => typeof(WebWidgetCommandEvent).Name;
public IEventEnvelope Envelope { get; set; } = new EventEnvelope();
public string Data { get; set; } = string.Empty;
}
}
133 changes: 133 additions & 0 deletions Components/WebWidget/HttpServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#nullable enable

using System;
using System.Threading;
using System.Threading.Tasks;
using EmbedIO;
using Serilog;
using Slipstream.Components.WebWidget.EventHandler;
using Slipstream.Shared;

namespace Slipstream.Components.WebWidget
{
public class HttpServer : IHttpServer, IHttpServerApi
{
private const string WEB_WIDGET_ROOT_DIRECTORY = "WebWidgets/";

private readonly Object Lock = new object();
private Thread? ServiceThread;
private readonly ILogger Logger;
private readonly IEventBusSubscription Subscription;
private readonly WebSocketsEventsServer EventsServerModule;
private readonly IEventHandlerController EventHandlerController;
private readonly IWebWidgetInstances Instances = new WebWidgetInstances();
private const string Url = "http://127.0.0.1:1919"; // Must NOT end with slash

public HttpServer(ILogger logger, IEventBusSubscription subscription, IEventHandlerController eventHandlerController)
{
Logger = logger;
Subscription = subscription;
EventHandlerController = eventHandlerController;

EventsServerModule = new WebSocketsEventsServer(Logger, Instances);

System.IO.Directory.CreateDirectory(WEB_WIDGET_ROOT_DIRECTORY);
}

public void AddInstance(string instanceId, string webWidgetType, string? data)
{
lock (Lock)
{
if (ServiceThread == null)
{
ServiceThread = new Thread(new ThreadStart(ThreadMain))
{
Name = GetType().Name,
};
ServiceThread.Start();
}
}

// Crude sanity check
var indexFile = WEB_WIDGET_ROOT_DIRECTORY + webWidgetType + "/index.html";
if (System.IO.File.Exists(indexFile))
{
Instances.Add(instanceId, webWidgetType, data);
Subscription.AddImpersonate(instanceId);

Logger.Information($"HttpServer: {Url}/instances/{instanceId} added");
}
else
{
Logger.Error($"HttpServer: {Url}/instances/{instanceId} not added, as {indexFile} does not exist");
}
}

private void ThreadMain()
{
Logger.Information("HttpServer started");

using (var server = CreateWebServer(Url))
{
Task serverTask = server.RunAsync();
bool stopping = false;

var internalHandler = EventHandlerController.Get<Internal.EventHandler.Internal>();
var webWidgetHandler = EventHandlerController.Get<WebWidgetEventHandler>();

internalHandler.OnInternalCommandShutdown += (_, e) => stopping = true;
webWidgetHandler.OnWebWidgetCommandEvent += (_, e) =>
{
if (e.Envelope.Recipients == null)
return;
foreach (var recipient in e.Envelope.Recipients)
{
EventsServerModule.Broadcast(recipient, e.Data);
}
};

while (!stopping)
{
IEvent? @event = Subscription.NextEvent(100);
EventHandlerController.HandleEvent(@event);

stopping = stopping || serverTask.IsCompleted;
}
}

Logger.Information("HttpServer stopped");
}

public void RemoveInstance(string instanceId)
{
Instances.Remove(instanceId);
Subscription.DeleteImpersonation(instanceId);
Logger.Information($"HttpServer: {Url}/instances/{instanceId} removed");
}

private WebServer CreateWebServer(string url)
{
// endpoints:
// /
// /ss.js
// /instances/<instanceid>/
// /webwidgets/<webwidgettype>/
// /events/<instanceid>/

var server = new WebServer(o => o
.WithUrlPrefix(url))
.WithStaticFolder("/webwidgets/", WEB_WIDGET_ROOT_DIRECTORY, false)
.WithModule(EventsServerModule)
.WithModule(new JavascriptWebModule())
.WithModule(new InstanceWebModule(Instances, WEB_WIDGET_ROOT_DIRECTORY, Logger))
.WithModule(new InstanceIndexWebModule(Instances, Logger));

return server;
}

public void Dispose()
{
}
}
}
10 changes: 10 additions & 0 deletions Components/WebWidget/IHttpServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#nullable enable

using System;

namespace Slipstream.Components.WebWidget
{
public interface IHttpServer : IDisposable
{
}
}
11 changes: 11 additions & 0 deletions Components/WebWidget/IHttpServerApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#nullable enable

namespace Slipstream.Components.WebWidget
{
public interface IHttpServerApi
{
// These needs to be threadsafe!
void AddInstance(string instanceId, string instanceType, string? data);
void RemoveInstance(string instanceId);
}
}
12 changes: 12 additions & 0 deletions Components/WebWidget/IWebWidgetEventFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Slipstream.Components.WebWidget.Events;
using Slipstream.Shared;

#nullable enable

namespace Slipstream.Components.WebWidget
{
public interface IWebWidgetEventFactory
{
WebWidgetCommandEvent CreateWebWidgetCommandEvent(IEventEnvelope envelope, string data);
}
}
16 changes: 16 additions & 0 deletions Components/WebWidget/IWebWidgetInstances.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#nullable enable


using System.Collections.Generic;

namespace Slipstream.Components.WebWidget
{
public interface IWebWidgetInstances
{
void Add(string id, string type, string? data);
void Remove(string id);
string this[string id] {get;}
ICollection<string> GetIds();
string? InitData(string id);
}
}
Loading

0 comments on commit 9f3b088

Please sign in to comment.