diff --git a/.gitignore b/.gitignore
index 6c58fd9..9c029ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ bin/
packages/
Paissa/Secrets.cs
logs/
+packages.lock.json
diff --git a/AutoSweep.csproj b/AutoSweep.csproj
index f9c9182..91ce71f 100644
--- a/AutoSweep.csproj
+++ b/AutoSweep.csproj
@@ -3,7 +3,7 @@
autoSweep
zhudotexe
- net6.0-windows
+ net7.0-windows
9
true
true
@@ -11,10 +11,14 @@
false
false
bin\$(Configuration)\
- $(appdata)\XIVLauncher\addon\Hooks\dev\
true
+ true
autoSweep
- 1.4.1.0
+ 1.4.2.0
+
+
+
+ $(appdata)\XIVLauncher\addon\Hooks\dev\
@@ -38,71 +42,41 @@
- $(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll
+ $(DalamudLibPath)Dalamud.dll
False
- $(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll
+ $(DalamudLibPath)ImGui.NET.dll
False
- $(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll
+ $(DalamudLibPath)ImGuiScene.dll
False
- $(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll
+ $(DalamudLibPath)Lumina.dll
False
- $(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll
+ $(DalamudLibPath)Lumina.Excel.dll
False
- $(AppData)\XIVLauncher\addon\Hooks\dev\Newtonsoft.Json.dll
+ $(DalamudLibPath)Newtonsoft.Json.dll
False
-
+
-
-
-
+
PreserveNewest
+ false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Paissa/Client.cs b/Paissa/Client.cs
index c63dcf0..a436ebf 100644
--- a/Paissa/Client.cs
+++ b/Paissa/Client.cs
@@ -11,9 +11,6 @@
using Dalamud.Game.Gui;
using Dalamud.Logging;
using DebounceThrottle;
-using JWT;
-using JWT.Algorithms;
-using JWT.Serializers;
using Newtonsoft.Json;
using WebSocketSharp;
@@ -21,8 +18,8 @@ namespace AutoSweep.Paissa {
public class PaissaClient : IDisposable {
private readonly HttpClient http;
private WebSocket ws;
- private readonly JwtEncoder encoder = new(new HMACSHA256Algorithm(), new JsonNetSerializer(), new JwtBase64UrlEncoder());
private bool disposed = false;
+ private string sessionToken;
// dalamud
private readonly ClientState clientState;
@@ -40,7 +37,6 @@ public class PaissaClient : IDisposable {
private const string wsRoute = "wss://paissadb.zhu.codes/ws";
#endif
- private readonly byte[] secret = Encoding.UTF8.GetBytes(Secrets.JwtSecret);
public event EventHandler OnPlotOpened;
public event EventHandler OnPlotUpdate;
@@ -61,9 +57,9 @@ public void Dispose() {
// ==== Interface ====
///
- /// Fire and forget a POST request to register the current character's content ID.
+ /// Make a POST request to register the current character's content ID.
///
- public void Hello() {
+ public async Task Hello() {
PlayerCharacter player = clientState.LocalPlayer;
if (player == null)
return;
@@ -75,7 +71,12 @@ public void Hello() {
};
string content = JsonConvert.SerializeObject(charInfo);
PluginLog.Debug(content);
- PostFireAndForget("/hello", content);
+ var response = await Post("/hello", content, false);
+ if (response.IsSuccessStatusCode) {
+ string respText = await response.Content.ReadAsStringAsync();
+ sessionToken = JsonConvert.DeserializeObject(respText).session_token;
+ PluginLog.Log("Completed PaissaDB HELLO");
+ }
}
///
@@ -139,20 +140,33 @@ private void queueIngest(object data) {
});
}
- private async void PostFireAndForget(string route, string content) {
- await PostFireAndForget(route, content, 5);
+ private async void PostFireAndForget(string route, string content, bool auth = true, ushort retries = 5) {
+ await Post(route, content, auth, retries);
}
- private async Task PostFireAndForget(string route, string content, ushort retries) {
+ private async Task Post(string route, string content, bool auth = true, ushort retries = 5) {
HttpResponseMessage response = null;
+ PluginLog.Verbose(content);
for (var i = 0; i < retries; i++) {
- var request = new HttpRequestMessage(HttpMethod.Post, $"{apiBase}{route}") {
- Content = new StringContent(content, Encoding.UTF8, "application/json"),
- Headers = {
- Authorization = new AuthenticationHeaderValue("Bearer", GenerateJwt())
+ HttpRequestMessage request;
+ if (auth) {
+ if (sessionToken == null) {
+ PluginLog.LogWarning("Trying to send authed request but no session token!");
+ await Hello();
+ continue;
}
- };
+ request = new HttpRequestMessage(HttpMethod.Post, $"{apiBase}{route}") {
+ Content = new StringContent(content, Encoding.UTF8, "application/json"),
+ Headers = {
+ Authorization = new AuthenticationHeaderValue("Bearer", sessionToken)
+ }
+ };
+ } else {
+ request = new HttpRequestMessage(HttpMethod.Post, $"{apiBase}{route}") {
+ Content = new StringContent(content, Encoding.UTF8, "application/json"),
+ };
+ }
try {
response = await http.SendAsync(request);
PluginLog.Debug($"{request.Method} {request.RequestUri} returned {response.StatusCode} ({response.ReasonPhrase})");
@@ -174,10 +188,12 @@ private async Task PostFireAndForget(string route, string content, ushort retrie
}
// todo better error handling
- if (response == null)
+ if (response == null) {
chat.PrintError("There was an error connecting to PaissaDB.");
- else if (!response.IsSuccessStatusCode)
+ } else if (!response.IsSuccessStatusCode) {
chat.PrintError($"There was an error connecting to PaissaDB: {response.ReasonPhrase}");
+ }
+ return response;
}
@@ -185,7 +201,7 @@ private async Task PostFireAndForget(string route, string content, ushort retrie
private void ReconnectWS() {
Task.Run(() => {
ws?.Close(1000);
- ws = new WebSocket(GetWSRouteWithAuth());
+ ws = new WebSocket(wsRoute);
ws.OnOpen += OnWSOpen;
ws.OnMessage += OnWSMessage;
ws.OnClose += OnWSClose;
@@ -249,21 +265,5 @@ private void WSReconnectSoon() {
if (!disposed) ReconnectWS();
});
}
-
-
- // ==== helpers ====
- private string GenerateJwt() {
- var payload = new Dictionary {
- { "cid", clientState.LocalContentId },
- { "aud", "PaissaHouse" },
- { "iss", "PaissaDB" },
- { "iat", DateTimeOffset.Now.ToUnixTimeSeconds() }
- };
- return encoder.Encode(payload, secret);
- }
-
- private string GetWSRouteWithAuth() {
- return $"{wsRoute}?jwt={GenerateJwt()}";
- }
}
}
diff --git a/Paissa/LotteryObserver.cs b/Paissa/LotteryObserver.cs
index 0627b33..60b7f82 100644
--- a/Paissa/LotteryObserver.cs
+++ b/Paissa/LotteryObserver.cs
@@ -54,7 +54,7 @@ long a8
PluginLog.LogDebug(
$"Got PlacardSaleInfo: PurchaseType={saleInfo.PurchaseType}, TenantType={saleInfo.TenantType}, available={saleInfo.AvailabilityType}, until={saleInfo.PhaseEndsAt}, numEntries={saleInfo.EntryCount}");
- PluginLog.LogDebug($"unknown1={saleInfo.Unknown1}, unknown2={saleInfo.Unknown2}, unknown3={BitConverter.ToString(saleInfo.Unknown3)}");
+ PluginLog.LogDebug($"unknown1={saleInfo.Unknown1}, unknown2={saleInfo.Unknown2}, unknown3={saleInfo.Unknown3}, unknown4={BitConverter.ToString(saleInfo.Unknown4)}");
PluginLog.LogDebug(
$"housingType={housingType}, territoryTypeId={territoryTypeId}, wardId={wardId}, plotId={plotId}, apartmentNumber={apartmentNumber}, placardSaleInfoPtr={placardSaleInfoPtr}, a8={a8}");
diff --git a/Paissa/Schema.cs b/Paissa/Schema.cs
index fda8679..7bb06c9 100644
--- a/Paissa/Schema.cs
+++ b/Paissa/Schema.cs
@@ -10,6 +10,12 @@ public class WSMessage {
public JObject Data { get; set; }
}
+ public class HelloResponse {
+ public string message { get; set; }
+ public double server_time { get; set; }
+ public string session_token { get; set; }
+ }
+
public class DistrictDetail {
public ushort district_id { get; set; }
public string name { get; set; }
diff --git a/Paissa/Utils.cs b/Paissa/Utils.cs
index 036b3f8..bcf9081 100644
--- a/Paissa/Utils.cs
+++ b/Paissa/Utils.cs
@@ -7,7 +7,7 @@ public class Utils {
// configuration constants
public const string CommandName = "/psweep";
public const string HouseCommandName = "/phouse";
- public const int NumWardsPerDistrict = 24;
+ public const int NumWardsPerDistrict = 30;
public static uint TerritoryTypeIdToLandSetId(uint territoryTypeId) {
return territoryTypeId switch {
diff --git a/Plugin.cs b/Plugin.cs
index 6665190..e4f773c 100644
--- a/Plugin.cs
+++ b/Plugin.cs
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
+using System.Threading.Tasks;
using AutoSweep.Paissa;
using AutoSweep.Structures;
using Dalamud.Data;
@@ -144,7 +145,7 @@ private void OnLogin(object _, EventArgs __) {
private void OnUpdateEvent(Framework f) {
if (clientNeedsHello && ClientState?.LocalPlayer != null && PaissaClient != null) {
clientNeedsHello = false;
- PaissaClient.Hello();
+ Task.Run(async () => await PaissaClient.Hello());
}
}
diff --git a/Structures/Lottery.cs b/Structures/Lottery.cs
index 76c3820..162d2a1 100644
--- a/Structures/Lottery.cs
+++ b/Structures/Lottery.cs
@@ -7,10 +7,11 @@ public class PlacardSaleInfo {
public TenantType TenantType; // 0x21
public AvailabilityType AvailabilityType; // 0x22
public byte Unknown1; // 0x23
- public uint PhaseEndsAt; // 0x24 - 0x27
- public uint Unknown2; // 0x28 - 0x2B
- public uint EntryCount; // 0x2C - 0x2F
- public byte[] Unknown3; // 0x30 - 0x3F
+ public uint Unknown2; // 0x24 - 0x27
+ public uint PhaseEndsAt; // 0x28 - 0x2B
+ public uint Unknown3; // 0x2C - 0x2F
+ public uint EntryCount; // 0x30 - 0x33
+ public byte[] Unknown4; // 0x34 - 0x4B
public static unsafe PlacardSaleInfo Read(IntPtr dataPtr) {
var saleInfo = new PlacardSaleInfo();
@@ -20,10 +21,11 @@ public static unsafe PlacardSaleInfo Read(IntPtr dataPtr) {
saleInfo.TenantType = (TenantType)binaryReader.ReadByte();
saleInfo.AvailabilityType = (AvailabilityType)binaryReader.ReadByte();
saleInfo.Unknown1 = binaryReader.ReadByte();
- saleInfo.PhaseEndsAt = binaryReader.ReadUInt32();
saleInfo.Unknown2 = binaryReader.ReadUInt32();
+ saleInfo.PhaseEndsAt = binaryReader.ReadUInt32();
+ saleInfo.Unknown3 = binaryReader.ReadUInt32();
saleInfo.EntryCount = binaryReader.ReadUInt32();
- saleInfo.Unknown3 = binaryReader.ReadBytes(16);
+ saleInfo.Unknown4 = binaryReader.ReadBytes(16);
return saleInfo;
}
}
diff --git a/autoSweep.json b/autoSweep.json
index efdba3c..01cf356 100644
--- a/autoSweep.json
+++ b/autoSweep.json
@@ -9,6 +9,5 @@
"housing"
],
"IconUrl": "https://raw.githubusercontent.com/zhudotexe/FFXIV_PaissaHouse/main/images/icon.png",
- "Punchline": "Crowdsourced housing alerts and lottery tracking for all.",
- "DalamudApiLevel": 7
+ "Punchline": "Crowdsourced housing alerts and lottery tracking for all."
}