HTTP
This commit is contained in:
parent
24e5bafeb9
commit
d6ba65e939
@ -1,17 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using AutoRetainerAPI;
|
||||
using Dalamud;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Memory;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using ECommons;
|
||||
@ -26,6 +22,7 @@ namespace AutoShutdown;
|
||||
|
||||
public sealed class Plogon : IDalamudPlugin
|
||||
{
|
||||
private readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(1) };
|
||||
private readonly nint _skipMovieAddress;
|
||||
private readonly byte[] _skipMovieOriginalBytes;
|
||||
private readonly AutoRetainerApi _autoRetainerApi;
|
||||
@ -69,7 +66,7 @@ public sealed class Plogon : IDalamudPlugin
|
||||
|
||||
private unsafe void FrameworkUpdate(IFramework _)
|
||||
{
|
||||
if (DateTime.Now > ShutdownAt && _gameGui.TryGetAddonByName<AtkUnitBase>("_TitleMenu", out var addon))
|
||||
if (DateTime.Now > ShutdownAt && _gameGui.TryGetAddonByName<AtkUnitBase>("_TitleMenu", out var _))
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
@ -126,22 +123,21 @@ public sealed class Plogon : IDalamudPlugin
|
||||
{
|
||||
TimeSpan next = nextVesselTimes.Min();
|
||||
_pluginLog.Information($"Next vessel time: {next}");
|
||||
Task.Factory.StartNew(async () =>
|
||||
if (next != TimeSpan.Zero)
|
||||
{
|
||||
try
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
await using NamedPipeClientStream pipe =
|
||||
new NamedPipeClientStream(".", "ffxiv_TheWatcher", PipeDirection.Out);
|
||||
await pipe.ConnectAsync(1000);
|
||||
|
||||
byte[] content = Encoding.UTF8.GetBytes($"{next.TotalSeconds}");
|
||||
await pipe.WriteAsync(content, 0, content.Length);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_pluginLog.Warning(e, "Unable to send IPC");
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
try
|
||||
{
|
||||
await _httpClient.PostAsync(new Uri($"http://localhost:12994/{next.TotalSeconds:F0}"),
|
||||
null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_pluginLog.Warning(e, "Unable to send IPC");
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
73
TheWatcher/Http.cs
Normal file
73
TheWatcher/Http.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace TheWatcher;
|
||||
|
||||
internal sealed class Http
|
||||
{
|
||||
private readonly HttpListener _listener;
|
||||
private readonly byte[] _data = "OK"u8.ToArray();
|
||||
|
||||
public Http()
|
||||
{
|
||||
_listener = new HttpListener();
|
||||
|
||||
// netsh http add urlacl url=http://+:12994/ user=username
|
||||
_listener.Prefixes.Add("http://+:12994/");
|
||||
|
||||
_listener.Start();
|
||||
}
|
||||
|
||||
public async Task RunAsync(CancellationToken token)
|
||||
{
|
||||
token.Register(Close);
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
// wait for the next request
|
||||
HttpListenerContext ctx = await _listener.GetContextAsync();
|
||||
|
||||
HttpListenerRequest request = ctx.Request;
|
||||
HttpListenerResponse response = ctx.Response;
|
||||
|
||||
if (request is { HttpMethod: "POST", Url.Segments.Length: >= 2 } &&
|
||||
double.TryParse(request.Url.Segments[1].TrimEnd('/'), out double duration))
|
||||
{
|
||||
TimeSpan timeSpan = TimeSpan.FromSeconds(duration);
|
||||
if (!SleepCtrl.Enabled)
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Ignoring sleep, not enabled: {timeSpan}");
|
||||
}
|
||||
else if (timeSpan < TimeSpan.FromMinutes(10))
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Not enough sleep duration: {timeSpan}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Task.Factory.StartNew(async () =>
|
||||
{
|
||||
DateTime until = DateTime.Now.Add(timeSpan).Add(TimeSpan.FromMinutes(-5));
|
||||
Console.WriteLine($"[{DateTime.Now}] Waiting a few seconds before sleeping for {timeSpan} until {until}");
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(35), token);
|
||||
if (SleepCtrl.Enabled)
|
||||
SleepCtrl.SleepUntil(until);
|
||||
else
|
||||
Console.WriteLine($"[{DateTime.Now}] Ignoring sleep (II), not enabled: {timeSpan}");
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
|
||||
response.ContentType = "text/plain";
|
||||
response.ContentEncoding = Encoding.UTF8;
|
||||
response.ContentLength64 = _data.LongLength;
|
||||
await response.OutputStream.WriteAsync(_data, token);
|
||||
response.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
_listener.Close();
|
||||
}
|
||||
}
|
@ -1,28 +1,25 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace TheWatcher;
|
||||
|
||||
internal sealed class Program
|
||||
{
|
||||
private static CancellationToken CancellationToken { get; set; }
|
||||
private static bool EnableSleep { get; set; }
|
||||
private static readonly Http Http = new();
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
public static async Task Main()
|
||||
{
|
||||
Console.WriteLine("o.O");
|
||||
|
||||
using CancellationTokenSource cts = new CancellationTokenSource();
|
||||
CancellationToken = cts.Token;
|
||||
|
||||
_ = Task.Factory.StartNew(async () => { await RunServer(); }, TaskCreationOptions.LongRunning);
|
||||
_ = Task.Factory.StartNew(async () => { await Http.RunAsync(CancellationToken); }, TaskCreationOptions.LongRunning);
|
||||
|
||||
Console.CancelKeyPress += (_, e) =>
|
||||
{
|
||||
EnableSleep = !EnableSleep;
|
||||
Console.WriteLine($"[{DateTime.Now}] Sleep => {EnableSleep}");
|
||||
SleepCtrl.Enabled = !SleepCtrl.Enabled;
|
||||
Console.WriteLine($"[{DateTime.Now}] Sleep => {SleepCtrl.Enabled}");
|
||||
e.Cancel = true;
|
||||
};
|
||||
|
||||
@ -102,102 +99,4 @@ internal sealed class Program
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SleepUntil(DateTime dt)
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Sleeping until {dt}...");
|
||||
|
||||
nint wakeTimer = SetWakeAt(dt);
|
||||
if (wakeTimer != nint.Zero)
|
||||
{
|
||||
bool suspended = NativeMethods.SetSuspendState(false, false, false);
|
||||
if (suspended)
|
||||
{
|
||||
if (DateTime.Now < dt)
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Resumed from suspend, but expected to sleep until {dt} => deactivating sleep");
|
||||
EnableSleep = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Resumed from suspend");
|
||||
}
|
||||
}
|
||||
else
|
||||
Console.WriteLine(
|
||||
$"[{DateTime.Now}] Not Suspended / {Marshal.GetLastPInvokeError()} - {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
NativeMethods.CancelWaitableTimer(wakeTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{DateTime.Now}] Not able to set wait timer / {Marshal.GetLastPInvokeError()} - {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr SetWakeAt(DateTime dt)
|
||||
{
|
||||
NativeMethods.TimerCompleteDelegate? timerComplete = null;
|
||||
|
||||
// read the manual for SetWaitableTimer to understand how this number is interpreted.
|
||||
long interval = dt.ToFileTimeUtc();
|
||||
IntPtr handle = NativeMethods.CreateWaitableTimer(IntPtr.Zero, true, "WaitableTimer");
|
||||
NativeMethods.SetWaitableTimer(handle, ref interval, 0, timerComplete, IntPtr.Zero, true);
|
||||
return handle;
|
||||
}
|
||||
|
||||
private static async Task RunServer()
|
||||
{
|
||||
while (!CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await using NamedPipeServerStream pipeServer =
|
||||
new NamedPipeServerStream("ffxiv_TheWatcher", PipeDirection.In, 1, PipeTransmissionMode.Message);
|
||||
Console.WriteLine($"[{DateTime.Now}] Waiting for client...");
|
||||
|
||||
await pipeServer.WaitForConnectionAsync();
|
||||
Console.WriteLine($"[{DateTime.Now}] Client connected...");
|
||||
try
|
||||
{
|
||||
string message = await ReadMessage(pipeServer);
|
||||
Console.WriteLine($"[{DateTime.Now}] Client message: {message}");
|
||||
|
||||
if (!EnableSleep)
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Ignoring sleep");
|
||||
}
|
||||
else if (double.TryParse(message, out double duration))
|
||||
{
|
||||
TimeSpan timeSpan = TimeSpan.FromSeconds(duration);
|
||||
if (timeSpan >= TimeSpan.FromMinutes(10))
|
||||
{
|
||||
DateTime sleep = DateTime.Now.Add(timeSpan).Add(TimeSpan.FromMinutes(-5));
|
||||
await Task.Delay(TimeSpan.FromSeconds(35));
|
||||
SleepUntil(sleep);
|
||||
}
|
||||
else
|
||||
Console.WriteLine($"[{DateTime.Now}] Not enough sleep duration");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Console.WriteLine("ERROR: {0}", e.Message);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[{DateTime.Now}] Finished client loop...");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static async Task<string> ReadMessage(PipeStream pipe)
|
||||
{
|
||||
byte[] buffer = new byte[1024];
|
||||
using var ms = new MemoryStream();
|
||||
do
|
||||
{
|
||||
var readBytes = await pipe.ReadAsync(buffer, 0, buffer.Length);
|
||||
await ms.WriteAsync(buffer, 0, readBytes);
|
||||
} while (!pipe.IsMessageComplete);
|
||||
|
||||
return Encoding.UTF8.GetString(ms.ToArray());
|
||||
}
|
||||
}
|
||||
|
51
TheWatcher/SleepCtrl.cs
Normal file
51
TheWatcher/SleepCtrl.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TheWatcher;
|
||||
|
||||
public class SleepCtrl
|
||||
{
|
||||
public static bool Enabled { get; set; }
|
||||
|
||||
public static void SleepUntil(DateTime dt)
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Sleeping until {dt}...");
|
||||
|
||||
nint wakeTimer = SetWakeAt(dt);
|
||||
if (wakeTimer != nint.Zero)
|
||||
{
|
||||
bool suspended = NativeMethods.SetSuspendState(false, false, false);
|
||||
if (suspended)
|
||||
{
|
||||
if (DateTime.Now < dt)
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Resumed from suspend, but expected to sleep until {dt} => deactivating sleep");
|
||||
Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[{DateTime.Now}] Resumed from suspend");
|
||||
}
|
||||
}
|
||||
else
|
||||
Console.WriteLine(
|
||||
$"[{DateTime.Now}] Not Suspended / {Marshal.GetLastPInvokeError()} - {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
NativeMethods.CancelWaitableTimer(wakeTimer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[{DateTime.Now}] Not able to set wait timer / {Marshal.GetLastPInvokeError()} - {Marshal.GetLastPInvokeErrorMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr SetWakeAt(DateTime dt)
|
||||
{
|
||||
NativeMethods.TimerCompleteDelegate? timerComplete = null;
|
||||
|
||||
// read the manual for SetWaitableTimer to understand how this number is interpreted.
|
||||
long interval = dt.ToFileTimeUtc();
|
||||
IntPtr handle = NativeMethods.CreateWaitableTimer(IntPtr.Zero, true, "WaitableTimer");
|
||||
NativeMethods.SetWaitableTimer(handle, ref interval, 0, timerComplete, IntPtr.Zero, true);
|
||||
return handle;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user