HTTP
This commit is contained in:
parent
24e5bafeb9
commit
d6ba65e939
@ -1,17 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO.Pipes;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using AutoRetainerAPI;
|
using AutoRetainerAPI;
|
||||||
using Dalamud;
|
using Dalamud;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
|
||||||
using Dalamud.Memory;
|
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using ECommons;
|
using ECommons;
|
||||||
@ -26,6 +22,7 @@ namespace AutoShutdown;
|
|||||||
|
|
||||||
public sealed class Plogon : IDalamudPlugin
|
public sealed class Plogon : IDalamudPlugin
|
||||||
{
|
{
|
||||||
|
private readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(1) };
|
||||||
private readonly nint _skipMovieAddress;
|
private readonly nint _skipMovieAddress;
|
||||||
private readonly byte[] _skipMovieOriginalBytes;
|
private readonly byte[] _skipMovieOriginalBytes;
|
||||||
private readonly AutoRetainerApi _autoRetainerApi;
|
private readonly AutoRetainerApi _autoRetainerApi;
|
||||||
@ -69,7 +66,7 @@ public sealed class Plogon : IDalamudPlugin
|
|||||||
|
|
||||||
private unsafe void FrameworkUpdate(IFramework _)
|
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);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
@ -126,22 +123,21 @@ public sealed class Plogon : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
TimeSpan next = nextVesselTimes.Min();
|
TimeSpan next = nextVesselTimes.Min();
|
||||||
_pluginLog.Information($"Next vessel time: {next}");
|
_pluginLog.Information($"Next vessel time: {next}");
|
||||||
Task.Factory.StartNew(async () =>
|
if (next != TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
try
|
Task.Factory.StartNew(async () =>
|
||||||
{
|
{
|
||||||
await using NamedPipeClientStream pipe =
|
try
|
||||||
new NamedPipeClientStream(".", "ffxiv_TheWatcher", PipeDirection.Out);
|
{
|
||||||
await pipe.ConnectAsync(1000);
|
await _httpClient.PostAsync(new Uri($"http://localhost:12994/{next.TotalSeconds:F0}"),
|
||||||
|
null);
|
||||||
byte[] content = Encoding.UTF8.GetBytes($"{next.TotalSeconds}");
|
}
|
||||||
await pipe.WriteAsync(content, 0, content.Length);
|
catch (Exception e)
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
_pluginLog.Warning(e, "Unable to send IPC");
|
||||||
{
|
}
|
||||||
_pluginLog.Warning(e, "Unable to send IPC");
|
}, TaskCreationOptions.LongRunning);
|
||||||
}
|
}
|
||||||
}, 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.Diagnostics;
|
||||||
using System.IO.Pipes;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace TheWatcher;
|
namespace TheWatcher;
|
||||||
|
|
||||||
internal sealed class Program
|
internal sealed class Program
|
||||||
{
|
{
|
||||||
private static CancellationToken CancellationToken { get; set; }
|
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");
|
Console.WriteLine("o.O");
|
||||||
|
|
||||||
using CancellationTokenSource cts = new CancellationTokenSource();
|
using CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
CancellationToken = cts.Token;
|
CancellationToken = cts.Token;
|
||||||
|
|
||||||
_ = Task.Factory.StartNew(async () => { await RunServer(); }, TaskCreationOptions.LongRunning);
|
_ = Task.Factory.StartNew(async () => { await Http.RunAsync(CancellationToken); }, TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
Console.CancelKeyPress += (_, e) =>
|
Console.CancelKeyPress += (_, e) =>
|
||||||
{
|
{
|
||||||
EnableSleep = !EnableSleep;
|
SleepCtrl.Enabled = !SleepCtrl.Enabled;
|
||||||
Console.WriteLine($"[{DateTime.Now}] Sleep => {EnableSleep}");
|
Console.WriteLine($"[{DateTime.Now}] Sleep => {SleepCtrl.Enabled}");
|
||||||
e.Cancel = true;
|
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